How to combine the video mixer and the automatic fader |
|
Inside the previous tutorial How to use the video mixer we have seen how two video players can mix the output of their respective video streams in one single window and inside the tutorial How to use the Automatic Fader we have seen how two players can fade two audio streams on the same speakers of a specific sound card: now we will see how these two features could be combined together in order to create a video mixer that will support the fading of both audio and video streams at the same time.
The easiest approach for implementing this feature is probably linking the volume level of the player fading-out, on the top of the Z-order (meaning that it will by default cover the video stream of the player fading-in), with the alpha channel transparency of its video stream: while the volume on this player is fading down, we raise the transparency level of the related video stream so the video stream of the player fading-in, which is at the bottom of the Z-Order, appears through until the video stream of the player fading-out totally disappears when its volume level reaches level 0.
Let's see in detail how we could proceed; first of all we need to initialize the control by instancing two players and initialize and instruct the automatic fader in order to manage one single playlist whose items will be played by the two players: for video clips, both players are set to work with the AUDIO_RENDERER_MODE_CUSTOM_2 audio rendering mode, allowing a better synchronization between audio and video stream.
Visual Basic 6 |
' init the control Amp3dj1.InitDJSystem 2, Me.hWnd, 0, 0, 0, 0
' initialize the fader which will use this single playlist split for player 1 and player 2 Amp3dj1.Fader.PlayListUseSingle Player_1 Amp3dj1.Fader.Init FADE_SINGLE_PLAYLIST, Player_1, Player_2
' set the audio rendering mode Amp3dj1.VideoPlayer.AudioRendererModeSet Player_1, AUDIO_RENDERER_MODE_CUSTOM_2 Amp3dj1.VideoPlayer.AudioRendererModeSet Player_2, AUDIO_RENDERER_MODE_CUSTOM_2
|
Visual C++ |
// init the control Amp3dj1.InitDJSystem(2,(long)this->m_hWnd, 0, 0, 0, 0);
// initialize the fader which will use this single playlist split for player 1 and player 2 Amp3dj1.GetFader().PlayListUseSingle (Player_1); Amp3dj1.GetFader().Init (FADE_SINGLE_PLAYLIST, Player_1, Player_2);
// set the audio rendering mode Amp3dj1.GetVideoPlayer().AudioRendererModeSet (Player_1, AUDIO_RENDERER_MODE_CUSTOM_2); Amp3dj1.GetVideoPlayer().AudioRendererModeSet (Player_2, AUDIO_RENDERER_MODE_CUSTOM_2);
|
For Visual Basic 6 it's recommended adding the code above into the Form_Load handler of the container form while for Visual C++ it's recommended adding the code below into the OnInitDialog (WM_INITDIALOG Windows message) handler of the container dialog box.
Now we can continue by creating the video mixer into a secondary form, whose client area will be totally covered by the video mixer surface, and attach the two players:
Visual Basic 6 |
' set scale mode to pixels Me.ScaleMode = 3 FormMixer.ScaleMode = 3
' create the video mixer object into a secondary form (FormMixer) and get back its unique identifier m_nMixerUniqueID = Amp3dj1.VideoMixer.Create(FormMixer.hWnd, 0, 0, FormMixer.ScaleWidth, FormMixer.ScaleHeight, RGB(0, 0, 0))
' attach the 2 instanced players to the VideoMixer object Amp3dj1.VideoPlayer.AttachToVideoMixer Player_1, m_nMixerUniqueID Amp3dj1.VideoPlayer.AttachToVideoMixer Player_2, m_nMixerUniqueID
' initialize a variable that will store the index of the player currently fading-out m_nPlayerFadingOut = -1
|
Visual C++ |
// create the video mixer object and get back its unique identifier CRect rect; pMixerDlg->GetClientRect(rect); m_nMixerUniqueID = Amp3dj1.GetVideoMixer().Create ((long)pMixerDlg->m_hWnd, 0, rect.Width(), rect.Height(), RGB(0, 0, 0));
// attach the 2 instanced players to the VideoMixer object Amp3dj1.GetVideoPlayer().AttachToVideoMixer(Player_1, m_nMixerUniqueID); Amp3dj1.GetVideoPlayer().AttachToVideoMixer(Player_2, m_nMixerUniqueID);
// initialize a variable that will store the index of the player currently fading-out m_nPlayerFadingOut = -1;
|
we have also initialized a variable, named m_nPlayerFadingOut, that will store the index of the player currently fading-out; this will be useful later to determine the player that will need its alpha channel transparency to be modified. You may notice the absence of the video preview window for the involved video players: the presence of the preview windows requires an amount of CPU which is probably not justified for this specific situation where all should be automated so omitting its creation may be a good idea.
Now we could proceed with loading a playlist but we still need to decide if we want to work with standard playlist formats, like M3U, PLS and WPL, which don't contain any automation information (so we will totally rely upon settings of the automatic fader) or if we prefer working with the PDJ playlist format which allows creating very customized automations. The difference here is quite important because, differently from audio clips whose duration can be determined on the fly, with video clips we cannot automatically obtain the duration because building filters graph through DirectShow is a lengthy operation that would require a huge amount of time when dealing with long playlists. As a general rule, if the playlist is in PDJ format and contains fading automation, we can load the playlist using the automation mode while in all other cases we should load it in speed or full mode
Visual Basic 6 |
' check the format to load If bFormatIsPDJ = True Then Amp3dj1.PlayListLoad(Player_1, strPathname, PLAYLIST_AUTOMATION_MODE) else ' dealing with video clips whose duration is unknown until effective loading, it's better to ' disable the automatic check that verifies if loaded files are long enough to fit the fader settings ' this is only a sample application without strong error checking so use this setting carefully :-) Amp3dj1.Fader.CheckItemsDurationOnStart = False
Amp3dj1.PlayListLoad(Player_1, strPathname, PLAYLIST_SPEED_MODE) end if
' start fading Amp3dj1.Fader.StartManualFading
|
Visual C++ |
// check the format to load if (bFormatsIsPDJ) Amp3dj1.PlayListLoad(Player_1, strPathname, PLAYLIST_AUTOMATION_MODE); else { // dealing with video clips whose duration is unknown until effective loading, it's better to // disable the automatic check that verifies if loaded files are long enough to fit the fader settings // this is only a sample application without strong error checking so use this setting carefully :-) Amp3dj1.GetFader().SetCheckItemsDurationOnStart (FALSE);
Amp3dj1.PlayListLoad(Player_1, strPathname, PLAYLIST_SPEED_MODE); }
// start fading Amp3dj1.GetFader().StartManualFading();
|
As seen above, once loaded the automatic fader can be started through the Fader.StartManualFading method; at this point the multimedia engine automatically checks if the loaded playlist contains at least two songs whose duration is long enough to apply the fader's timings and settings: if this check should fail, the call to the Fader.StartManualFading method would return an error code.
When dealing with playlists in PDJ format, containing volume fading automation, this wouldn't be an issue because the playlist is tailored to allow a total automation but when dealing with other playlist formats, which don't contain any kind of information about embedded video clips, the first fading would obviously fail: for this reason if we should be sure that all video clips are compatible with fader's settings, it could be useful disabling this automatic check by setting the Fader.CheckItemsDurationOnStart property to "False".
The automatic fader has now been started so it immediately begins loading into the involved players the sequence of playlist's video clips: it must be remarked that, when dealing with video clips, due to the filters graph building by DirectShow, the loading could be a bit more "heavy" respect to audio clips so you will have to keep count of some small delay during the loading.
The automatic fader already knows how to load and start video clips and also knows how to fade the respective audio stream but it still doesn't know how to manage the fading of the video streams into the stream mixer's window so, for achieving this purpose, we still need to add some coding.
As mentioned before, a possible solution would be linking the volume level of the player fading-out, on the top of the Z-order (meaning that it will by default cover the video stream of the player fading-in), to the alpha channel transparency of its video stream: while the volume on this player is fading down, we raise the transparency level of the related video stream so the video stream of the player fading-in, which is at the bottom of the Z-Order, appears through until the video stream of the player fading-out totally disappears when its volume level reaches level 0. The best approach is obviously synchronizing with events generated by the automatic fader as seen below:
Visual Basic 6 |
Private Sub Amp3dj1_SoundLoaded(ByVal nPlayer As Integer, ByVal nItemIndex As Integer) ' this is the next player to fade-in so put it at the bottom of the Z-Order Amp3dj1.VideoMixer.PlayerZOrderSet m_nMixerUniqueID, nPlayer, 100
... do other stuffs
End Sub
Private Sub Amp3dj1_FadeOutStarted(ByVal nPlayer As Integer) ' keep count of the player fading out because we will apply transparency on its video stream m_nPlayerFadingOut = nPlayer End Sub
Private Sub Amp3dj1_FadeOutCompleted(ByVal nPlayer As Integer) ' the player is no more fading m_nPlayerFadingOut = -1 End Sub
Private Sub Amp3dj1_FadeInCompleted(ByVal nPlayer As Integer) ' fade in has been completed so put it at the top of the Z-Order Amp3dj1.VideoMixer.PlayerZOrderSet m_nMixerUniqueID, nPlayer, 0 End Sub
Private Sub Amp3dj1_FadingVolumeChanged(ByVal nPlayer As Integer, ByVal fVolume As Single) ' check if the player is fading-out If Amp3dj1.GetPlayerStatus(nPlayer) = SOUND_PLAYING And m_nPlayerFadingOut = nPlayer Then ' raise the transparent factor on the player fading out Amp3dj1.VideoMixer.PlayerAlphaSet m_nMixerUniqueID, nPlayer, 100 - fVolume End If End Sub
|
Visual C++ |
// handler of the SoundLoaded event void CVideoMixerWithFaderPrjDlg::OnSoundLoaded(short nPlayer, short nItemIndex) { // this is the next player to fade-in so put it at the bottom of the Z-Order Amp3dj1.GetVideoMixer().PlayerZOrderSet (m_nMixerUniqueID, nPlayer, 100);
... do other stuffs
}
// handler of the FadeOutStarted event void CVideoMixerWithFaderPrjDlg::OnFadeOutStarted(short nPlayer) { // keep count of the player fading out because we will apply transparency on its video stream m_nPlayerFadingOut = nPlayer; }
// handler of the FadeOutCompleted event void CVideoMixerWithFaderPrjDlg::OnFadeOutCompleted(short nPlayer) { // the player is no more fading m_nPlayerFadingOut = -1; }
// handler of the FadeInCompleted event void CVideoMixerWithFaderPrjDlg::OnFadeInCompleted(short nPlayer) { // fade in has been completed so put the player at the top of the Z-Order Amp3dj1.GetVideoMixer().PlayerZOrderSet (m_nMixerUniqueID, nPlayer, 0); }
// handler of the FadingVolumeChanged event void CVideoMixerWithFaderPrjDlg::OnFadingVolumeChanged(short nPlayer, float fVolume) { // check if the player is fading-out if (Amp3dj1.GetPlayerStatus(nPlayer) == SOUND_PLAYING && m_nPlayerFadingOut == nPlayer) // raise the transparent factor on the player fading out Amp3dj1.GetVideoMixer().PlayerAlphaSet (m_nMixerUniqueID, nPlayer, int (100 - fVolume)); }
|
This is one of the possible approaches but you could obviously implement a totally different way to manage the Z-Order and the alpha channel transparency. As a final note, please, keep count that code snippets above don't contain strong error checking: in a production environment it's recommended to add some check on values returned by various methods calls.
A sample of use of the VideoMixer object in conjunction with the Fader object in Visual Basic 6 and Visual C++ 6 can be found inside the following sample installed with the product's setup package:
- VideoMixerWithFader