Playing+a+media+file+using+Direct+Show

//by Richard Russell, December 2007//

Windows provides several methods for playing audio and video files. One of the simplest is the Media Control Interface (MCI) which is used by //BBC BASIC for Windows// to play MIDI files and is also the basis of the **PLAYER.BBC** media player application which can be found [|here]. However, whilst MCI is easy to use it does not support some file formats such as **animated GIFs**.

An alternative is to use **Direct Show** (a subset of the DirectX system). This is not as straightforward as **MCI** in terms of code, but it may support a wider range of file formats. This article describes the simplest use of Direct Show to play an audio or video file.

Firstly we need to perform some initialisation:

code format="bb4w" SYS "LoadLibrary", "OLE32.DLL" TO ole32% SYS "GetProcAddress", ole32%, "CoInitialize"    TO `CoInitialize` SYS "GetProcAddress", ole32%, "CoUninitialize"  TO `CoUninitialize` SYS "GetProcAddress", ole32%, "CoCreateInstance" TO `CoCreateInstance` SYS "GetProcAddress", ole32%, "CLSIDFromString" TO `CLSIDFromString` SYS `CoInitialize`, 0 code To ensure the necessary 'clean up' operations are carried out on exit the program should include **ON ERROR** and **ON CLOSE** statements:

code format="bb4w" ON ERROR PROCcleanup : SYS "MessageBox", @hwnd%, REPORT$, 0, 48 : QUIT ON CLOSE PROCcleanup : QUIT code The next step is to //instantiate// the **filter graph manager**:

code format="bb4w" CLSID_FilterGraph% = FNguid("{e436ebb3-524f-11ce-9f53-0020af0ba770}") IID_IGraphBuilder% = FNguid("{56a868a9-0ad4-11ce-b03a-0020af0ba770}") CLSCTX_INPROC_SERVER = 1 SYS `CoCreateInstance`, CLSID_FilterGraph%, 0, CLSCTX_INPROC_SERVER, \ \                      IID_IGraphBuilder%, ^m_graphBuilder% TO hr% IF hr% THEN ERROR 100, "Could not create filter graph manager" code Once the filter graph manager COM object is instantiated we can use it to create two other DirectShow objects, firstly a **media control object**:

code format="bb4w" IID_IMediaControl% = FNguid("{56a868b1-0ad4-11ce-b03a-0020af0ba770}")

SYS !(!m_graphBuilder%), m_graphBuilder%, IID_IMediaControl%, ^m_mediaControl% TO hr% IF hr% THEN ERROR 100, "Could not instantiate media control object" code and secondly a **media event object**:

code format="bb4w" IID_IMediaEvent% = FNguid("{56a868b6-0ad4-11ce-b03a-0020af0ba770}") SYS !(!m_graphBuilder%), m_graphBuilder%, IID_IMediaEvent%, ^m_mediaEvent% TO hr% IF hr% THEN ERROR 100, "Could not instantiate media event object" code If the above code succeeded we now have all three Direct Show objects required to handle streamed audio or video playback. To play a file we need to create a **filter graph**:

code format="bb4w" SYS !(!m_mediaControl%+44), m_mediaControl%, FNwide(mediafile$), 0 TO hr% : REM Render IF hr% THEN ERROR 100, "Could not render file "+mediafile$ code Here **mediafile$** should be a fully-qualified path and filename of the file to be played.

Now everything is ready to play the file:

code format="bb4w" SYS !(!m_mediaControl%+28), m_mediaControl% TO hr% : REM Run REM Wait for the playback to complete: timeout% = 100 REPEAT SYS !(!m_mediaEvent%+36), m_mediaEvent%, timeout%, ^pEvCode% : REM Wait UNTIL pEvCode% code You can carry out other operations while the file is playing by including them in the above waiting loop.

To play another file, you should release the three objects as in **PROCcleanup** below (but //not// call **CoUninitialize**) and then instantiate the three objects again starting with the filter graph manager. It is preferable to call **CoInitialize** just once at the very start of the program, and **CoUninitialize** just once on exit.

Before exiting the program you should call the 'clean up' routine:

code format="bb4w" PROCcleanup QUIT

DEF PROCcleanup m_mediaEvent% += 0  : IF m_mediaEvent%   SYS !(!m_mediaEvent%+8), m_mediaEvent% m_mediaControl% += 0 : IF m_mediaControl% SYS !(!m_mediaControl%+8), m_mediaControl% m_graphBuilder% += 0 : IF m_graphBuilder% SYS !(!m_graphBuilder%+8), m_graphBuilder% SYS `CoUninitialize` ENDPROC code Should you wish to pause or stop playback prematurely you can do so using the following code segments:

code format="bb4w" REM Pause playback: SYS !(!m_mediaControl%+32), m_mediaControl% TO hr% : REM Pause REM Stop playback: SYS !(!m_mediaControl%+36), m_mediaControl% TO hr% : REM Stop code Finally here are the functions **FNwide** and **FNguid** used by the above code:

code format="bb4w" DEF FNwide(A$) LOCAL M$     M$ = STRING$(2*LENA$+2," ") SYS "MultiByteToWideChar", 0, 0, A$, -1, !^M$, LENA$+1 = M$     DEF FNguid(A$) LOCAL C%, M%     DIM C% 15, M% LOCAL 2*LENA$+1 SYS "MultiByteToWideChar", 0, 0, A$, -1, M%, LENA$+1 SYS `CLSIDFromString`, M%, C%     = C% code

Fullscreen output
To display a video file fullscreen, add the following code immediately before the call to **Run**:

code format="bb4w" REM Instantiate a video window object: IID_IVideoWindow% = FNguid("{56a868b4-0ad4-11ce-b03a-0020af0ba770}") SYS !(!m_graphBuilder%), m_graphBuilder%, IID_IVideoWindow%, ^pVW% TO hr% IF hr% THEN ERROR 100, "Could not instantiate video window object" REM Define the IVideoWindow interface: DIM IVideoWindow{QueryInterface%, AddRef%, Release%, GetTypeInfoCount%, \ \  GetTypeInfo%, GetIDsOfNames%, Invoke%, put_Caption%, get_Caption%, \ \  put_WindowStyle%, get_WindowStyle%, put_WindowStyleEx%, get_WindowStyleEx%, \ \  put_AutoShow%, get_AutoShow%, put_WindowState%, get_WindowState%, \ \  put_BackgroundPalette%, get_BackgroundPalette%, put_Visible%, get_Visible%, \ \  put_Left%, get_Left%, put_Width%, get_Width%, put_Top%, get_Top%, \ \  put_Height%, get_Height%, put_Owner%, get_Owner%, \ \  put_MessageDrain%, get_MessageDrain%, get_BorderColor%, put_BorderColor%, \ \  get_FullScreenMode%, put_FullScreenMode%, SetWindowForeground%, \ \  NotifyOwnerMessage%, SetWindowPosition%, GetWindowPosition%, \ \  GetMinIdealImageSize%, GetMaxIdealImageSize%, GetRestorePosition%, \ \  HideCursor%, IsCursorHidden%} !(^IVideoWindow{}+4) = !pVW% REM Set fullscreen mode: SYS IVideoWindow.put_FullScreenMode%, pVW%, TRUE TO hr% IF hr% THEN ERROR 100, "Could not set fullscreen mode" code To close the fullscreen window, use the following code:

code format="bb4w" WM_CLOSE = &10 SYS "FindWindow", 0, "ActiveMovie Window" TO hamw% IF hamw% SYS "SendMessage", hamw%, WM_CLOSE, 0, 0 code

Windowed output
To display a video file in a window (whose size and position you can specify), add the following code immediately before the call to **Run**:

code format="bb4w" REM Instantiate a video window object: IID_IVideoWindow% = FNguid("{56a868b4-0ad4-11ce-b03a-0020af0ba770}") SYS !(!m_graphBuilder%), m_graphBuilder%, IID_IVideoWindow%, ^pVW% TO hr% IF hr% THEN ERROR 100, "Could not instantiate video window object" REM Define the IVideoWindow interface: DIM IVideoWindow{QueryInterface%, AddRef%, Release%, GetTypeInfoCount%, \ \  GetTypeInfo%, GetIDsOfNames%, Invoke%, put_Caption%, get_Caption%, \ \  put_WindowStyle%, get_WindowStyle%, put_WindowStyleEx%, get_WindowStyleEx%, \ \  put_AutoShow%, get_AutoShow%, put_WindowState%, get_WindowState%, \ \  put_BackgroundPalette%, get_BackgroundPalette%, put_Visible%, get_Visible%, \ \  put_Left%, get_Left%, put_Width%, get_Width%, put_Top%, get_Top%, \ \  put_Height%, get_Height%, put_Owner%, get_Owner%, \ \  put_MessageDrain%, get_MessageDrain%, get_BorderColor%, put_BorderColor%, \ \  get_FullScreenMode%, put_FullScreenMode%, SetWindowForeground%, \ \  NotifyOwnerMessage%, SetWindowPosition%, GetWindowPosition%, \ \  GetMinIdealImageSize%, GetMaxIdealImageSize%, GetRestorePosition%, \ \  HideCursor%, IsCursorHidden%} !(^IVideoWindow{}+4) = !pVW% REM Set the video window's owner to BB4W's window: SYS IVideoWindow.put_Owner%, pVW%, @hwnd% TO hr% IF hr% THEN ERROR 100, "Could not set owner window" REM Set the style of the video window: WS_CHILD = &40000000 WS_CLIPSIBLINGS = &4000000 SYS IVideoWindow.put_WindowStyle%, pVW%, WS_CHILD OR WS_CLIPSIBLINGS TO hr% IF hr% THEN ERROR 100, "Could not set window style" REM Set the video window size and position: SYS IVideoWindow.SetWindowPosition%, pVW%, left%, top%, right%, bottom% TO hr% IF hr% THEN ERROR 100, "Could not set window rect" code To close the video window, use the following code:

code format="bb4w" WM_CLOSE = &10 SYS "FindWindow", 0, "ActiveMovie Window" TO hamw% IF hamw% SYS "SendMessage", hamw%, WM_CLOSE, 0, 0 code