A Jukebox sample in C# PDF Print E-mail

In this tutorial, we'll implement a version of the JukeBox sample from DirectShow. I have simulated the sample GUI (to some extent) and I looked at the code only when I needed to know how to accomplish a certain task.   

The sample will use a form, a groupbox with buttons and a trackbar on the top. Then a listbox on the left, with a panel to hold the video window to its right. At the bottom of the form, we find a button to set the directory and another button to launch the GraphEdit application.

For this version, we haven't provided any pin info GUIs component and it doesn't have a mute button. We have used the quartz.dll as wrapped by VS and we have used the implementation of the Genghis group for the FolderNameDialog. We have provided a zip file which contains the source code for this sample plus a VS project file and a command script to compile the code (if you don't have VS). We have included a dll for the Genghis code since their license doesn't allow us to distribute their work in source form.

With these preliminaries out of the way, let's look at some code:

  1. FilgraphManagerClass graphBuilder = null;
  2. IMediaControl mediaCtrl = null;
  3. IMediaEventEx mediaEvt = null;
  4. IVideoWindow videoWin = null;
 

We have declared the graphBuilder object and three interfaces that we'll need. For every files that we'll play, we'll have to call:

  1. void InitInterfaces()
  2. {
  3. try
  4. {
  5. graphBuilder = new FilgraphManagerClass();
  6. mediaCtrl = (IMediaControl) graphBuilder;
  7. mediaEvt = (IMediaEventEx) graphBuilder;
  8. }
  9. catch(Exception)
  10. {
  11. MessageBox.Show( "Couldn't start" );
  12. }
  13. }
  14. void CloseInterfaces()
  15. {
  16. if( mediaCtrl != null )
  17. {
  18. mediaCtrl.StopWhenReady();
  19. mediaEvt.SetNotifyWindow( 0, WM_GRAPHNOTIFY, 0 );
  20. }
  21. mediaCtrl = null;
  22. mediaEvt = null;
  23. videoWin = null;
  24. graphBuilder = null;
  25. }
 

The InitInterfaces calls create the graphBuilder object and the needed interfaces. While the CloseInterfaces calls stop the graph and we cancel event notification as required by the IMediaEventEx interface (the doc says "Before you release the IMediaEventEx pointer, cancel event notification by calling SetNotifyWindow with a NULL window handle").

When we want to play a file we call:(where fName is the file name)

  1.  
  2. graphBuilder.RenderFile( fName );
 

Followed by:

  1. isSeeking = (graphBuilder.CanSeekForward() == -1) &&
  2. (graphBuilder.CanSeekBackward() == -1 );
  3. if( isSeeking )
  4. trackBar1.Enabled = true;
  5. else
  6. trackBar1.Enabled = false;
  7. ...
  8. videoWin = graphBuilder as IVideoWindow;
  9. videoWin.Owner = (int)panel1.Handle;
  10. videoWin.WindowStyle = WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
  11. Rectangle rc = panel1.ClientRectangle;
  12. videoWin.SetWindowPosition( 0, 0, rc.Right, rc.Bottom );
  13. mediaEvt.SetNotifyWindow( (int)this.Handle, WM_GRAPHNOTIFY, 0 );
 

graphBuilder.CanSeekForward returns OATRUE to indicate that the graph can seek forward. OATRUE is defined as -1 in dshow.h. We use the value of CanSeekForward and CanSeekBackward to decide to enable or not enable the trackbar.

Then we set the video window owner to the panel handle with the proper window style and the the window position to occupy the whole panel. We, then, tell the filter graph to sent event notifications to the form window as WM_GRAPHNOTIFY messages. When we do this, the DirectShow runtime will stuff WM_GRAPHNOTIFY messages in our main window message queue. To handle these messages we have to override the method Control.WndProc as follow:

  1. protected override void WndProc( ref Message m )
  2. {
  3. if( m.Msg == WM_GRAPHNOTIFY )
  4. {
  5. if( mediaEvt != null )
  6. OnGraphNotify();
  7. return;
  8. }
  9. base.WndProc( ref m );
  10. }
 

The interesting part is the call to OnGraphNotify. This method is implemented as:

  1. void OnGraphNotify()
  2. {
  3. int p1, p2;
  4. int code;
  5. try
  6. {
  7. do
  8. {
  9. if( mediaEvt == null )
  10. return;
  11. mediaEvt.GetEvent( out code, out p1, out p2, 0 );
  12. mediaEvt.FreeEventParams( code, p1, p2 );
  13. if( code == EC_COMPLETE )
  14. OnClipCompleted();
  15. }
  16. while( true );
  17. }
  18. catch( Exception ){}
  19. }
 

This is the C# translation of the C++ code given in the DirectShow documentation (see the sections entitled "Responding to events" and "Learning when an event occurs"). The main difference between this code and the one in the SDK is that we wait for GetEvent to throw an exception to get out of the loop. The "native" GetEvent returns an HRESULT value (i.e. E_ABORT) which get translated to an exception by the interop layer. OnClipCompleted just updates some properties maintained by the GUI controls in the application.

The rest of the code is just a collection of event handlers for the controls of this application. We'll point out the timer that we added to display the current position beside the trackbar. The timer object generates Tick events that we use to update the position label. But we also use it to set a flag isTimer so we don't have to handle the TrackBar.ValueChanged event too often (since this handler seeks the filter graph).




Bookmark it...
Digg!Reddit!Del.icio.us!Google!Facebook!Slashdot!Technorati!StumbleUpon!Newsvine!Furl!Yahoo!Ma.gnolia!
 
< Prev   Next >
Joomla Template by Joomlashack
Joomla Templates by JoomlaShack Joomla Templates by Compass Design