How to use the video mixer
As explained inside the How to play video files through DirectShow tutorial, Audio DJ Studio allows rendering video clips inside output video windows so each player can have one or more output video windows added for this specific purpose; through a specific filter named VMR-9, introduced with version 9 of DirectX, DirectShow gives the possibility to render two or more video clips inside the same output video window at the same time and Audio DJ Studio, through the embedded VideoMixer class and the related DisplayVideoMixer property, leverages the VMR-9 filter in order to provide mixing/blending capabilities to loaded video clips.
A VideoMixer object can be created and sized through the VideoMixer.Create method on a given output window: this method will return a unique identifier that will be used in every other call related to this specific VideoMixer object. Only one instance of the video mixer can be created during the life of the container application.
Once the VideoMixer object has been created, you can attach instanced players to the video mixer through the VideoPlayer.AttachToVideoMixer method so, each time a video clip is loaded inside a player through a video loading method (like VideoPlayer.Load and VideoPlayer.LoadSync methods), the video stream output will be automatically redirected to the VideoMixer output window: it's important to note that, if you should have already added a video output window to a specific player through a previous call to the VideoPlayer.VideoWindowAdd method, you could obtain a preview window of the same video clip, perfectly in sync with the one being rendered on the video mixer output window.
IMPORTANT NOTE ABOUT CODECS AND WINDOWS 7: When dealing with Windows 7, also if you have installed K-Lite and configured ffdshow to manage a specific format (for example the MPEG-2 format or the H.264 format used in some MP4 video clip), DirectShow will continue using the default decoder named "Microsoft DTV-DVD Video Decoder": the quality of this decoder is quite poor and doesn't support the VMR-9 video renderer used for mixing/blending video clips so a call to methods like VideoPlayer.Load could return back an error when the involved player has been added to a video mixer through the VideoPlayer.AttachToVideoMixer method; inside Appendix B you will find a few guidelines that could be of help in order to manage DirectShow configuration through K-Lite provided tools.
As for the VideoPlayer object, the VideoMixer object allows resizing its output window through the VideoMixer.Move method and to hide/show the output window through the VideoMixer.Show method. Contents of the VideoMixer can be discarded through the VideoMixer.Reset method and the VideoMixer object itself can be totally destroyed through the VideoMixer.Destroy method: after this call its unique identifier becomes invalid and no other operations will be available for this specific VideoMixer.
When two or more video clips are being rendered on a video mixer, you can set the Z-Order, determining which player will appear on top of the other, through the VideoMixer.PlayerZOrderSet method: players with a lower Z-Order value will stay on top respect to other players having a higher Z-Order value; for example, a player with Z-Order 0 will stay on top of any other player but, in case two players should be assigned the same Z-Order, the latest one having got the same Z-Order will be placed on top of the previous one. If a player is currently playing a video clip inside the VideoMixer, by loading a new video clip inside another player you would certainly like to avoid covering the playing one: for this reason it's always suggested that you assign a higher Z-Order to the player loading a new video clip and to assign Z-Order 0 only when you really want a specific player to stay on top of others.
Now we know how to determine players Z-Order but we still need to know how to smoothly fade-out a playing video clip and fade-in a new video clip loaded inside a second player: fade-in/fade-out of video streams can be controlled through the VideoMixer.PlayerAlphaSet method, allowing to change the alpha channel transparency of a specific player. In order to better understand how to put things together, imagine the following scenario:
1. The control allocates, through the InitSoundSystem, two players (named PlayerA and PlayerB).
4. PlayerA is placed on top of the Z-Order through a call to the VideoMixer.PlayerZOrderSet method while PlayerB is placed on bottom of the Z-Order by setting any value higher than 0.
6. After a while, PlayerB loads a video clip through the VideoPlayer.Load method and starts playback through the VideoPlayer.Play method: having a higher Z-Order respect to PlayerA, it will not cover the video clip played by PlayerA itself.
7. When we need to fade-out PlayerA, using a slider or an automated procedure controlled by a timer, we smoothly increase its alpha channel transparency through the VideoMixer.PlayerAlphaSet method: during this transparency sliding, we will start seeing in transparency what is played by PlayerB and, when the transparency sliding will reach maximum transparency, we will only see what is played by PlayerB.
8. We can now loop, by inverting PlayerA and PlayerB, to point 4.
This is only an example of possible uses of Z-Order and alpha channel transparency combination but you could adapt this procedure to your specific needs without issues of sort: for example, instead of calling the VideoPlayer.Load method for both players and using timers for sliding the alpha channel transparency, you could use the video mixer in combination with the Automatic Fader and, eventually, with playlists management; at this point you could for example apply the alpha channel transparency in combination with automatic volume modifications notified through the FadingVolumeChanged event. A possible implementation of this approach is briefly explained inside the tutorial How to combine the video mixer and the automatic fader.
Another feature allowed by the VideoMixer object, is the possibility to modify the rectangle enclosing the video stream rendering of a specific player: this rectangle is known as "normalized rectangle" and its coordinates are expressed in percentage respect to actual size of the VideoMixer output window: this allows keeping the same aspect ratio when the VideoMixer output window is resized during playback. The VideoMixer.PlayerNormalizedRectSet method allow modifying the normalized rectangle for a specific player and the VideoMixer.PlayerNormalizedRectSetIdeal allows setting a normalized rectangle that will best fit into the VideoMixer the original dimensions of the loaded video clip so, if for example the video clip is in 16:9, it will maintain its original aspect ratio.
An interesting feature of normalized rectangles is the possibility to create the so called "Picture-In-Picture" effect by using a player to render its video clip on the full VideoMixer output window and another player to render its video clip inside a smaller rectangle inside the same VideoMixer output window.
Another feature of the VideoMixer object is the possibility to display an On-Screen-Display (also known as OSD) which allows rendering a composition of graphical objects (mainly bitmaps and text) over video clips being played.
IMPORTANT NOTE ABOUT OSD BEHAVIOUR: We have already seen the OSD feature available for the VideoPlayer object but with the VideoMixer object the approach is a bit different; the OSD of the VideoPlayer object is implemented through a custom transform filter which is placed "upstream" respect to the video rendering window inside the DirectShow filters graph so graphical items of the OSD are rendered directly inside video frames: as you will understand, in case the output rendering window should be resized, graphical items will be scaled accordingly because they have already been merged into the frame; differently from the described case, the OSD of the VideoMixer object is implemented directly on the output window of the VMR-9 video renderer so, when resizing this output window, the OSD will be automatically scaled to best fit the area of the window but the size of contained graphical items won't be scaled and will keep their original dimensions.
The OSD can be enabled/disabled through the VideoMixer.OSDEnable method: when the OSD is disabled it doesn't appear on the screen. You can know if the OSD is enabled or disabled through the VideoMixer.OSDIsEnabled method. As for video clips, also the rectangle of the OSD can be normalized, in this case through the VideoMixer.OSDNormalizedRectSet method.
In order to avoid covering the video clips being played, through the VideoMixer.OSDKeyColorSet method you can define a key color that will be always rendered as totally transparent on the OSD; for example, you could create a bitmap with a black background like the one below:
By setting the key color to "black", the bitmap would be rendered on the video mixer as seen on the screenshot below:
Graphical objects (also known as "items") that can be added to compose the OSD are:
|•||Picture files: you can add a picture file through the VideoMixer.OSDItemPictureFileAdd method and to change the picture through the VideoMixer.OSDItemPictureFileChange method.|
|•||Picture files stored in memory: you can add a picture file stored inside a memory buffer through the VideoMixer.OSDItemPictureMemoryAdd method and to change the picture through the VideoMixer.OSDItemPictureMemoryChange method.|
|•||Bitmaps: you can add a bitmap stored in memory, accessible through its HBITMAP handle, through the VideoMixer.OSDItemBitmapAdd method and to change the bitmap through the VideoMixer.OSDItemBitmapChange method.|
|•||Strings of text: you can add a string of text through the VideoMixer.OSDItemTextAdd method and to change the string characters through the VideoMixer.OSDItemTextChange method. With mentioned methods you can also define both color and font for each single string of text.|
|•||Outlined strings of text: you can add an outlined string of text through the VideoMixer.OSDItemOutlineTextAdd method and to change the string characters through the VideoMixer.OSDItemOutlineTextChange method. With mentioned methods you can also define both color and font for each single string of text.|
|•||Rectangles: you can add a rectangle through the VideoMixer.OSDItemRectangleAdd method and to change its size and color through the VideoMixer.OSDItemRectangleChange method.|
You have no limit on the number of items you can add to the OSD but, keep in mind, the bigger the number of objects, the higher will be the CPU load if you should animate the OSD contents, for example for scrolling a text item.
After adding a picture item from a file or from a memory buffer or from a HBITMAP handle, there is the possibility to obtain back the handle (HBITMAP) to a memory bitmap, eventually resized to custom dimensions, through the VideoMixer.OSDItemBitmapGet method.
Each item has the following set of properties:
|•||Position: the VideoMixer.OSDItemMove and the VideoMixer.OSDItemScrollByPixels methods can move the position of each item: this allows "animating" the OSD, for example for scrolling the item horizontally or vertically. After every position change, you can know if the item is still visible on the VideoMixer area through the VideoMixer.OSDItemIsOnVisibleArea method. The possibility to move an item automatically can be achieved through the VideoMixer.OSDItemMovementSet method. You can also obtain the normalized rectangle, with coordinates and size expressed in percentage, used by a specific item through the VideoMixer.OSDItemNormalizedRectGet method; this is quite useful for items containing a string of text whose total size, in terms of width and height, depends upon the chosen font size.|
|•||Z-Order: the VideoMixer.OSDItemZOrderSet method determines which item will appear on top of the other.|
|•||Visibility: the VideoMixer.OSDItemShow method shows/hides the item.|
|•||Transparency: the VideoMixer.OSDItemAlphaSet allows changing the alpha channel transparency of the item so it will not totally cover what's behind its position.|
Each item can be removed at any time from the list of items through the VideoMixer.OSDItemRemove method.
If available graphical features of the OSD shouldn't be enough for you, don't forget that you could make a custom graphical rendering inside a memory bitmap through Windows GDI or GDI+ API and then you could display the resulting bitmap, through its HBITMAP handle, in one single shot through the VideoMixer.OSDItemBitmapAdd method and to update it in real time through the VideoMixer.OSDItemBitmapChange method.
A last feature of the OSD is the capability to display a background picture through the VideoMixer.OSDBackgroundPictureFileSet method or a memory bitmap through the VideoMixer.OSDBackgroundBitmapSet method: the picture file or memory bitmap will be always rendered under any other item added to the OSD.
IMPORTANT NOTE ABOUT RUNNING THE WINDOWS TASK MANAGER AND SCREENSAVERS: When the video mixer is running, it would be convenient to avoid the usage of the "Ctrl+Alt+Del" keyboard combination in order to open the Windows task manager because this could cause a temporary disconnection of the video renderer: when you need opening the Windows task manager, while the video mixer is running, you should prefer the usage of the "Ctrl+Shift+Esc" keyboard combination.
For the same reason, while the video mixer is running, it's highly recommended to avoid the usage of a screensaver.
In case you should need to take a screenshot of what it's currently being played on the window of a specific video mixer, you may use the VideoMixer.ScreenshotSaveToFile method.
A sample of use of the VideoMixer class in combination with the VideoPlayer class in Visual C# and Visual Basic.NET can be found inside the following sample installed with the product's setup package: