## Play a Movie from C++

This article walks through a simple C++ program designed to demonstrate one way to play movies. It is based on the PlayFile function code, taken from the Playfile.cpp file, which you can find in the Playfile sample in the Samples\ds\player directory.

The PlayFile function has no control over the filters selected by the filter graph manager to play the input media file or over the playback window created. This article contains the following sections.

- Playing a Media File — the basic code to play back a media file.
- Adding Media Seeking — shows the code needed to seek to a particular location in the media file.

See these related sections to add a particular feature to your playback code:

- Control the Video Playback Window from C++ — demonstrates how to set the playback window's style and position.
- Display a Filter's Property Page from C++ — demonstrates how to display a filter's property page, so that the user can change how media files are played back.

Playing a Media File

This section explains the code needed to play a media file from within C/C++. The Playfile sample contains Playfile.cpp and demonstrates how to create an application window, display a menu to open a media file, and call the PlayFile function to play the media file. You can examine the Playfile application in the Samples\ds\player\Playfile directory to see how to use the PlayFile function. To learn how to build the Playfile sample from Visual C++ 5.x, see Setting the Visual Studio Include and Lib Directories.

The PlayFile function plays a specified file in a playback window. This function uses the filter graph manager to automatically render the media clip. The filter graph manager selects the appropriate filters and constructs the filter graph.

PlayFile function code demonstrates:

- Basic interfaces needed to play and control a media file.
- Instantiating the filter graph manager.
- Calling the filter graph manager to build the filter graph that renders the media file.
- Playing the media file.
- Accessing events to tell when the playback is finished (media file ended).

After any function call that retrieves an interface pointer (CoCreateInstance, RenderFile, and QueryInterface), you should insert error-checking code to make sure the interface pointer was successfully obtained; if it wasn't, release any interfaces pointers already obtained. An example of error-checking code is:

            if (FAILED(hr)) {
                goto ObjectRelease; // go to the clean-up section
            }

You can call the PlayFile function from an application with code such as the following:

      TCHAR *szFilename = "c:\\dxmedia\\movie\\movie.avi";
      PlayFile(szFilename);

Perform the following steps to play a media file from within C/C++. You don't necessarily have to perform the steps in the order presented.

1. Include the necessary headers.

         #include <windows.h>
         #include <mmsystem.h>
         #include <streams.h>
         #include "playfile.h"

2. Define a windows message constant and the HELPER_RELEASE macro, which will be used to release the interfaces from the WndMainProc callback (see the Playfile code for generic window code).

        #define WM_GRAPHNOTIFY  WM_USER+13
        #define HELPER_RELEASE(x) { if (x) x->Release(); x = NULL; }

3. Declare variables.

        HWND      ghApp;
        HINSTANCE ghInst;
        HRESULT   hr;
        LONG      evCode;
        LONG      evParam1;
        LONG      evParam2;

   The ghApp variable is the handle of window to notify when the graph signals an event. The ghInst variable is the HINSTANCE of the window. The evCode variable will hold the event code, and the evParam1 and evParam2 variables will hold the event parameters.

4. Declare and initialize the necessary interfaces. The reference count of the interfaces is automatically incremented on initialization, so you don't need to call the IUnknown::AddRef method on them. For this example, you need only the four interfaces shown in the following code. For more information, see the documentation for the IMediaEventEx, IGraphBuilder, IMediaControl, and IVideoWindow interfaces.

         IGraphBuilder *pigb  = NULL;
         IMediaControl *pimc  = NULL;
         IMediaEventEx *pimex = NULL;
         IVideoWindow  *pivw  = NULL;

5. Define the function. The szFile parameter is the name of the media file that will be played.

           void PlayFile (LPSTR szFile)
           {
               HRESULT hr;

6. Create a Unicode (wide character) string from the input file name.

               WCHAR wFile[MAX_PATH];
               MultiByteToWideChar( CP_ACP, 0, szFile, -1, wFile, MAX_PATH );

7. Instantiate the filter graph manager, asking for the IGraphBuilder interface.

               hr = CoCreateInstance(CLSID_FilterGraph,
               NULL,
               CLSCTX_INPROC_SERVER,
               IID_IGraphBuilder,
               (void **)&pigb);

8. Query for the IMediaControl interface (provides the methods to run, pause, and stop the playback), the IMediaEventEx interface (so you can receive event notifications), and the IVideoWindow interface to hide the window when the movie is finished playing.

          pigb->QueryInterface(IID_IMediaControl, (void **)&pimc);
          pigb->QueryInterface(IID_IMediaEventEx, (void **)&pimex);
          pigb->QueryInterface(IID_IVideoWindow, (void **)&pivw);

9. Ask the filter graph manager to build the filter graph that renders the input file. This does not play the media file. (When you play the file with Run, the filter graph will automatically render the input file's media type. You do not have to specify a renderer filter.)

          hr = pigb->RenderFile(wFile, NULL);

10. Use a window to capture graph signal events. This not only improves performance, but allows your application to run in any threading model.

           pimex->SetNotifyWindow((OAHWND)ghApp, WM_GRAPHNOTIFY, 0);

    The window specified by ghApp will handle messages in response to all events from the graph. If an event occurs, DirectShow posts a WM_GRAPHNOTIFY message to the window.

11. Start playing the media file.

           hr = pimc->Run();

    Alternatively, if your playback had a pause or stop button (see, for example, the CPlay sample or Controlling Filter Graphs Using C), you can pause or stop the playback on the button event with the IMediaControl::Pause or IMediaControl::Stop method, as shown in the following code.

                hr = pimc->Pause();
                hr = pimc->Stop();

The WndMainProc callback function in Playfile handles the filter graph messages and releases the interfaces when necessary, using the HELPER_RELEASE macro. The GetClipFileName function gets the movie to be played, while the WinMain function creates the window. These are all generic windows functions.

This section showed how to play a media file from the beginning. The next section shows how to control where within a media file to start and stop playing.

Adding Media Seeking

You can use the IMediaPosition or IMediaSeeking interface to seek to a particular place in your media file. The IMediaPosition::put_CurrentPosition method enables you to specify a start time within the media file. For example, you can use the following code to rewind to the media file's beginning.

      IMediaPosition *pimp;
      hr = pigb->QueryInterface(&IID_IMediaPosition, (void **)&pimp);
      hr = pimp->put_CurrentPosition(0);

Time is specified in 100-nanosecond units. The following code seeks into the media file 1 second:

        hr = pimp->put_CurrentPosition(10000000);

You can use the IMediaPosition::put_StopTime method to set the time within the media file to stop playback.

However, with IMediaPosition you can seek only to times within a media file. With the IMediaSeeking interface, you can set your seeking time format to 100-nanosecond time units, frames, bytes of data, media samples, or interlaced video fields. You set the format you want to use with the IMediaSeeking::SetTimeFormat method. Make sure your media file is not playing when you the set the format.

The term media time refers to positions within a seekable medium. Media time can be expressed in a variety of units, and indicates a position within the data in the file. The following table shows the possible media time formats.

Value | Description
TIME_FORMAT_MEDIA_TIME | Seeks to the specified time in the media file, in 100-nanosecond units. This is the default.
TIME_FORMAT_BYTE | Seeks to the specified byte in the stream.
TIME_FORMAT_FIELD | Seeks to the specified interlaced video field.
TIME_FORMAT_FRAME | Seeks to the specified video frame.
TIME_FORMAT_SAMPLE | Seeks to the specified sample in the stream.

For example, the following code sets the format so that the application seeks for sample numbers.

       IMediaSeeking *pims;
       hr = pigb->QueryInterface(IID_IMediaSeeking, (void **)&pims);
       hr = pims->SetTimeFormat(&TIME_FORMAT_SAMPLE);

An application can use the various seeking modes to seek in a stream to a particular video frame or audio sample without doing time/rate conversions itself. This is useful for editing, which requires sample-accurate playback. The frame or sample number that the application specifies is passed through to the AVI or MPEG parser without the risk of rounding errors.

The following steps show how to set which frame in a media file to start playing at and which frame to stop playing at; for example, to start playing a movie at the fifth frame after its beginning. You can insert this code into the PlayFile function anywhere after the RenderFile function has built the filter graph.

1. Access the IMediaSeeking interface.

          IMediaSeeking *pims;
          hr = pigb->QueryInterface(IID_IMediaSeeking, (void **)&pims);

2. Set the time format. In the following example, the time format is set to seek to frames.

          hr = pims->SetTimeFormat(&TIME_FORMAT_FRAME);

3. Declare and initialize the media-seeking variables. In this case, they are frames within the media file to start and stop playback. The following values set the start frame to 5 and the stop frame to 15.

          LONGLONG start = 5L;
          LONGLONG stop = 15L;

4. Set the start and stop media time with the IMediaSeeking::SetPositions method. The AM_SEEKING_AbsolutePositioning flag means that the start and stop frames are absolute positions within the media file (not relative to the present position in the media file). In this example, the media file will start playing at frame 5 into the file and stop at frame 15, for a duration of 10 frames. The length of playing time depends on the video's frame rate.

          pims->SetPositions(&start, AM_SEEKING_AbsolutePositioning, &stop,
                      AM_SEEKING_AbsolutePositioning);

5. Release the IMediaSeeking interface.

          pims->Release();

By removing the SetTimeFormat call and setting the values of start and stop as follows, you can set the media file to start playing 5 seconds into the file and stop 7 seconds into the file, for a duration of 2 seconds.

       LONGLONG start = 50000000L;
       LONGLONG stop = 70000000L;

By setting other formats in the SetTimeFormat call, you can seek to frames, sample numbers, byte, and so on.

© 1997 Microsoft Corporation. All rights reserved. Terms of Use.