What is the right way to add buttons to the Media Foundation Interfaces video screen - c++

I'm currently trying to build a player literally from scratch using Media Foundation Interfaces.
I use the example taken from here:
https://msdn.microsoft.com/en-us/library/windows/desktop/dd979592(v=vs.85).aspx
As the basis for the player.
I want to add buttons (back/forward button , progress bar etc.) to the video screen window, but it isn't so clear to me how.
I can not simply add a button as a child of the main window because it is hidden by the player, I tried to add the buttons as children of Video window but it did not work ...
What is the right way to add buttons to a video window?
This is part of the code where you register to the player window (to the full code please enter the link I have attached).
// create the instance of the player hwnd = handle of main window
HRESULT hr = CPlayer::CreateInstance(hwnd, hwnd, &g_pPlayer);
// inner call to cplayer
CPlayer *pPlayer = new (std::nothrow) CPlayer(hVideo, hEvent);
// Create a partial topology. (m_hwndVideo == hVideo == hwnd)
hr = CreatePlaybackTopology(m_pSource, pSourcePD, m_hwndVideo, &pTopology);
i try to use this function in order to change video paint so i will have some
place left at the bottom to put there my buttons, here is my function:
VOID update_lower_window(HWND hwnd)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc;
GetClientRect(hwnd, &rc);
//rc = father size
RECT repaint;
memcpy(&repaint, &rc, sizeof(RECT));
repaint.top = repaint.bottom - DOWN_SPACE; // DOWN_SPACE == 50
// paint back bottom to white
FillRect(hdc, &repaint, (HBRUSH)COLOR_WINDOW);
EndPaint(hwnd, &ps);
}
i call this function when:
WM_PAINT happend
WM_SIZE happend
WM_CREATE happend
With this said I still could not achieve my goals:
When the movie starts playing it starts full screen and does not leave the space I wanted down (I can not seem to find the message sent when the movie is played or in other words when the movie paint on the screen it's not sent one of the messages on which I inserted my function)
When I reduce the screen, the remnants of the previous screen still remain down and I can't see the background.
When using the Minimize button it get the same phenomenon as in 2.
Another interesting detail The phenomenon I mentioned in 2 occurs only when I change window size by drag from the bottom up, if I change window size by drag from one corner (enlarges or decreases height and width simultaneously) then the window corrects itself and I see in the bottom white part (the background) all the time.

You can reposition video area so your win32 GUI (buttons and progress) is below. See WM_SIZE handler in that sample for how to do it, they calculate rectangle to occupy the window, you can adjust to accommodate your controls.
If you want translucent overlaid controls you can write a EVR Presenter.
https://msdn.microsoft.com/en-us/library/windows/desktop/bb530107(v=vs.85).aspx
https://github.com/Microsoft/Windows-classic-samples/tree/master/Samples/Win7Samples/multimedia/mediafoundation/evrpresenter
But that’s relatively hard.
P.S. If you just want a simple solution, use something higher-level. Like Windows Media Player control note they have a C++ example, or .NET for GUI and MediaElement in it.
Update: I think the right place to set initial video position in that sample is likely CPlayer::OnTopologyStatus, after MFGetService(MR_VIDEO_RENDER_SERVICE) line.
Anyway, another approach that might be simpler for your task, create a child window dedicated to the video. Position it so it occupies the majority of your app’s main window, and only leaves bottom 50px unoccupied. Don’t forget to handle at least WM_SIZE to reposition and ideally also WM_DISPLAYCHANGED, WM_ENDSESSION, WM_GETMINMAXINFO. Setup the MF playback so that video occupies the complete child video window.
This will ensure the video won’t interfere with Win32 stuff you’re painting on your bottom 50px panel.
BTW that’s what MPC-HC is doing, see the pic.
As you see on Spy++ screenshot, they have dedicated video window, and also other child windows for Win32 controls of the player.

Related

C++ redraw with DrawText

Currently i'm drawing the text from a textbox in my window. I successfully get the text i need to draw and it draws the text. It's ok.
Here's the problem: when i write something else in my input box and draw the text again (via button push), the new text is drawn right on top of the previous text as to be expected.
I'm new to all of this and i can't find a way to clear the previous text before drawing the new text.
Here's my code:
void DrawMyText(HWND hwnd) {
int iTextLength = GetWindowTextLength(hDrawInput) + 1;
char cDrawText[1000] = "";
HDC wdc = GetWindowDC(hwnd);
RECT canvas;
canvas.left = 168;
canvas.top = 108;
canvas.right = 500;
canvas.bottom = 500;
GetWindowText(hDrawInput, cDrawText, iTextLength);
SetTextColor(wdc, 0x00FF0066);
SetBkMode(wdc,TRANSPARENT);
DrawText(wdc, cDrawText, -1, &canvas, DT_LEFT);
DeleteDC(wdc);
}
Any tips on how to do this? I will gladly provide any additional information if needed. Thanks in advance!
DrawText is more like a spray can - it paints over top whatever is already there. I'd recommend switching to SetWindowText. The difference is DrawText is more like a canvas rendering call, and doesn't consider much about the type of control it's drawing to. SetWindowText is an explicit "set the text of this window to this specific value", and is specific to text-based controls. More to the point, it will replace the current text with the new text value.
If you absolutely must do it with DrawText (i.e. you prefer a canvas approach as above), then you'll have to manually clear the text area yourself with something like InvalidateRect() (using the RECT of the text area). Or, by drawing a rectangle equal in size to the text area and with the same color as the background. Let me know if that's not enough detail.
If some other window covers and then uncovers the place where you drew the text it will be gone! The illusion that windows can sit on top of each other is destroyed! Windows provides a scheme to overcome this problem but you are not cooperating with the scheme.
Paint your text only in response to the WM_PAINT message, using the BeginPaint and EndPaint API calls. On the button click just call InvalidateRect, which asks Windows to send you a WM_PAINT. BeginPaint erases your window just before you paint. So by putting your painting code in the right place - the WM_PAINT handler - you solve two problems: It erases any old text, and it repaints whenever your window is uncovered.

How to set a bitmap as the background of a CHILD window (WIN32)

First of all this is not MFC.
Here is a cropped version of the GUI I have been working on:
As you can see there is a bitmap image above the tab control (which uses the default window's handle), I will refer to it as "Blue Bitmap" I have no problems with it, it is functioning perfectly.
The problem I am having is setting another bitmap (from a bitmap file on disk) to the background of the child window (more specifically the tab control child window), replacing the grey colour. Here are some things I did to try and set the bitmap as the background for the child window (the tab).
1) I used the same method as was used to assign the blue bitmap to the window by first importing the bitmap using LoadImage like so:
index->hbmBitmapBanner = (HBITMAP)LoadImage(index->hInstance,L"Images\\horizontal.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
Then in the WM_PAINT message:
case WM_PAINT:
{
PAINTSTRUCT ps1;
if (BeginPaint(WINDOWHANDLE,&ps1))
{
HDC hdcBanner = CreateCompatibleDC(ps1.hdc);
HBITMAP hbmOldHorizontal = (HBITMAP)SelectObject(hdcBanner,index->hbmBitmapBanner); // banner
BitBlt(ps1.hdc,0,0,516,101,hdcBanner,5,0,SRCCOPY);
SelectObject(hdcBanner,hbmOldHorizontal);
DeleteDC(hdcBanner);
EndPaint(WINDOWHANDLE,&ps1);
}
break;
}
That code was successful in setting blue bitmap to the window, but not in setting the other bitmap into the window, (with the blue bitmap's variables substituted into, size and orientation changed as well of course). It simply did not display the bitmap on the screen, and the window remained unchanged, yet no functions failed. ALSO, when I swapped the file locations around, it loaded the bitmap into where the blue bitmap was, so there is definitely no failure in the loading function.
2) Using the above code, I tried changing the first argument of BeginPaint to the window handle of the tab control box, this proved yet again of no use, and the window remained unchanged. I also tried changing the first argument of BeginPaint to a child window which is displayed on the information tab, this caused the child window (which is a groupbox) to disappear, yet still no bitmap appeared on the window.
3) As a last resort I stupidly attempted to use this function on the child window
SetClassLongPtr(tab->hTabIndex[0],GCLP_HBRBACKGROUND,(DWORD)GetStockObject(BLACK_BRUSH));
And of course, it didn't work
If it is of any use, here is how I created the tabbing in my window:
TCITEM tie = {0};
tab->hTab = CreateWindowEx(0,WC_TABCONTROL,L"",WS_CHILD | WS_VISIBLE,0,101,600,400,
WINDOWHANDLE,NULL,(HINSTANCE)GetWindowLong(WINDOWHANDLE,GWLP_HINSTANCE),NULL
);
Is the main tab control, followed by inserting individual the tabs (i.e the information tab)
TCHAR pszTab1 [] = L"Information"; // tab1's text
tie.pszText = pszTab1; // the tab's text/caption
TabCtrl_InsertItem(tab->hTab, 0, &tie); // insert the tab
Additional info: The tab control, and the windows are working/interacting perfectly apart from this. I was testing/attempting this for only one tab, because I knew if it worked on one tab, then it would work on all the tabs, hence saving time.
I have Programming For Windows Fifth Edition, by Charles Petzold beside me as reference, and there is a rather large section on bitmaps, but he doesn't go into talking about loading a bitmap to a child window, the closest he gets is using a bitmap in a menu, which is completely different than a window.
I think that the issue here is that Windows Common Controls do not interact with your application via your main thread's window procedure but via their own (system-defined) window procedures.
So, for example, when your tab window redraws itself, it does not do so in response to a WM_PAINT message arriving at the window procedure defined in your code. You should be able to verify for yourself that this is so by examining the window handles of messages passed to your window procedure, eg WM_PAINT.
If you wish to 'tap into' the default behaviour of a common control you have to 'subclass' it (see here) but in my experience attempting to change the re-draw behaviour is generally problematic.
In the case of a tab control the best thing to do is create an array of child windows sized to the client area of the tab control and arrange that the one displayed at any one time corresponds to the tab that is selected.
If you're using C++ you might consider creating a base class to wrap these windows in which the background (bitmap) drawing is handled. You can then derive a series of wrappers to handle each individual tab. I've used exactly this approach in the past and it works well.
It may be helpful to bear in mind that the tabs of the tab control do not extend over the whole of the control's client area but are just literally the tabs themselves. I don't remember the details but I think they are handled internally by the control and that manipulating them in any way is rather trick, even when the control is subclassed - may be wrong about that.
Hope that helps.
Cheers, Ian.

change background colour in parent window

I'm very new to win32api programming. I have 3 questions.
how to change the background colour in the parent window. i did it as folows but it dont work
wClass.hbrBackground=(HBRUSH)(RGB(255,255,255));
second question is, I use to add text in the window as follows. the text is in bold font and with a background colour. I want the text to be in normal and without background colour.
PAINTSTRUCT ps;
HDC hDC;
char szBuffer[]="Hello, World!";
hDC=BeginPaint(hWnd,&ps);
TextOut(hDC,10,10,szBuffer,strlen(szBuffer));
third question is how to add group boxes in the parent window. i searched it in the internet but this was discribed how to add group boxes on dialog boxes using resources.
pls some one help me with these isue...
> wClass.hbrBackground=(HBRUSH)(RGB(255,255,255));
I suppose the class structure requiers the handle of the brush, not the color value itself (but I'm not sure). Something like this:
wClass.hbrBackground=(HBRUSH)(CreateSolidBrush(RGB(255,255,255)));
To make the text background transparent use special WinAPI function, SetBkMode(TRANSPARENT); (Oof, spend some time to remember it's name:) ).
In most tasks it will be much better to make a dialog resource and use it like an ordinary window (drawing smth in it, putting simple windows in which you draw, etc) than take an ordinary window and try to add dialog controls in it. It became a common practice since WinForms and then WPF - every window in them is a "form" in which you can add controls, draw in it and so on.
I'm trying to remember if Microsoft put in code to clear the client area. I know at the minimum, you can get the client rect and then use that to base a drawRect() command to the whole client area. You also may have to trap the command to erase the background

Use DrawText to update label

I am currently writing a program in c++ (no MFC) and want to update a label (win32 static control) using the win32 DrawText function. However when i call the function nothing is written to the label. I use the following code:
HDC devCon = ::GetDC(GetDlgItem(IDC_TITLE).m_hWnd);
RECT rect = {10, 10, 100, 15};
::DrawText(devCon, _T("TEST DC TEXT!!!"), -1, &rect, DT_NOCLIP);
::ReleaseDC(GetDlgItem(IDC_TITLE).m_hWnd, devCon);
As you see with the GetDlgItem(...) I am using ATL but that should not be a problem in my opinion. When I specify NULL in the GetDC method the text is drawn in the upper left corner of the screen as it is supossed to be since the method return the DC to the entire screen.
Why doesn't this work with the DC of the label?
Hope you folks can help me.
If you want to draw the text manually because setting the control text doesn't do what you want, then you need to tell Windows that you're doing that. Otherwise the control will draw itself over whatever you do whenever it needs to be redrawn.
To draw it yourself, mark your control as owner draw by setting the SS_OWNERDRAW style, and then handle the WM_DRAWITEM message to draw it in the window procedure of the parent window, or subclass the window and handle the WM_PAINT message in your new window procedure.
I guess that the text is drawn but at the next window message is set to the default text.
Try to set the text with SendMessage(..,WM_SETTEXT,...);
Use SetDlgItemText() to set the text for the control.
You are trying to paint directly onto the static control's device context.
This is not going to be so simple, because:
the control will repaint itself whenever it's update region is invalidated
usually the controls share the device context with the parent window, so what you are getting in GetDC(...) is actually your dialog's device context.
So, use SetDlgItemText, or SetWindowText to set the text of the window.
To use a custom font (or set the text/background color), handle the WM_CTLCOLORSTATIC message in your WindowProc.

Transparent child control

I'm writing a control that may have some of its parts transparent or semitransparent. Basically this control shows a png image (with alpha channel). The control's parent window has some graphics on it. Therefore, to make the png control render correctly it needs to get image of what the parent window would draw beneath it. Parent dialog might have WS_CLIPCHILDREN flag set, that means that the the parent window won't draw anything under the the png control and in this case png control won't work properly.
This control has to work on Windows Mobile also, so it can't have WS_EX_TRANSPARENT
Funny you should ask. I just wrote code to do this yesterday. It's in the code base for Project Resistance. Look at how ResistorView (which has a PNG with transparency) interacts with MainForm (which has the background image).
I'm not doing c#, so I don't think that could be helpful. I tried to look into your code and I don't see anything that can solve my problem even though you accomplish the same task as me.
To give more details, my control also supports gif animation and I also used the same IImage load from stream as you do in your project. From my experience, that IImage load from stream is unbelievable junk code, it's extremely slow. I have no idea how that possibly could be so slow. Loading 32x32 gif with 31 frames takes like 1.5secods using that junk IImage stuff. I wrote my own loader (using some opensource gif library) and without any optimization entire decoding/loading of the gif frames takes less than 100ms. I'm using TouchPro2... I can't imagine how badly this code would perform on a low end device.
As a quick alternative here's a possible solution to my question:
in the WM_PAINT handler of the child control that draws images (gif or png) I do the following:
first, I call SetRedraw(false) on self and on parent window.
then I hide the child control, and send WM_PAINT to parent window with optional HDC (as wParam). The parent window renders everything to the offscreen bitmap (passed via hdc to WM_PAINT) and after WM_PAINT successfully returns I take relevant part of the offscreen bitmap.
Then I enable show the child window and call SetRedraw(true) on both child and the parent window. That trick works but has some disadvantages obviously (I need to createa huge offscreen bitmap to capture entire screen area even though I need 32x32 pixel from the middle of parent window).
the code is below:
bool pic_control::get_parent_bg(MyBitmap & bg)
{
CWindow parent = GetParent();
CClientDC dc(parent);
bool is_visible = IsWindowVisible() && parent.IsWindowVisible();
if(!is_visible){
return false;
}
parent.SetRedraw(false);
SetRedraw(false);
CRect rect;
parent.GetClientRect(rect);
MyBitmap bmp;
bmp.create(rect.Width(), rect.Height());
ShowWindow(SW_HIDE);
parent.SendMessage(WM_PAINT, (WPARAM)(HDC)bmp.dc());
ShowWindow(SW_SHOW);
GetWindowRect(rect);
parent.ScreenToClient(rect);
bg.create(rect.Width(), rect.Height());
bg.dc().BitBlt(0, 0, rect.Width(), rect.Height(), bmp.dc(), rect.left, rect.top, SRCCOPY);
IF_DEBUG SAL::saveHBITMAPToJpeg(bg.GetBitmap(), "frames/BG.jpg", 100);
SetRedraw(true);
parent.SetRedraw(true);
return true;
}
WS_CLIPCHILDREN is forced on in WinCe, you cannot toggle it. I dont know why, maybe it is done for performance reasons.
From my experience what i did in this situation.
1) If the parent window bacgkround is dynamic (for instance window that contains map, which can be moved), then it is painted to memory canvas first, then to the screen, and memory canvas is saved and used for painting transparent childs. Memory canvas would not contain holes in place of child windows, so it can be used for futher pixel merge. The disadvantage here is memory consumption to hold canvas in memory.
2) If parent window background is static (dialog box, menu, etc) then you can make non-window childs.
class CImageButton
{
public:
bool IsPointInside(POINT pt);
void OnPaint(HDC canvas);
void OnClick();
void SetRect(RECT& rc);
private:
RECT m_rc;
};
Your parent window will contain an array of such objects and redirect WM_PAINT and
mouse clicks to them.
The disavantage is additional code needed to be added to parent window, but you can make a base class for all your parent windows, which would handle issues with non-windowed controls.