User Tools

Site Tools


playing_20a_20media_20file_20using_20direct_20show

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:

        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

To ensure the necessary 'clean up' operations are carried out on exit the program should include ON ERROR and ON CLOSE statements:

        ON ERROR PROCcleanup : SYS "MessageBox", @hwnd%, REPORT$, 0, 48 : QUIT
        ON CLOSE PROCcleanup : QUIT

The next step is to instantiate the filter graph manager:

        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"

Once the filter graph manager COM object is instantiated we can use it to create two other DirectShow objects, firstly a media control object:

        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"

and secondly a media event object:

        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"

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:

        SYS !(!m_mediaControl%+44), m_mediaControl%, FNwide(mediafile$), 0 TO hr% : REM Render()
        IF hr% THEN ERROR 100, "Could not render file "+mediafile$

Here mediafile$ should be a fully-qualified path and filename of the file to be played.

Now everything is ready to play the file:

        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%

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:

        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

Should you wish to pause or stop playback prematurely you can do so using the following code segments:

        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()

Finally here are the functions FNwide and FNguid used by the above code:

        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%

Fullscreen output

To display a video file fullscreen, add the following code immediately before the call to Run():

        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"

To close the fullscreen window, use the following code:

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

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():

        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"

To close the video window, use the following code:

        WM_CLOSE = &10
        SYS "FindWindow", 0, "ActiveMovie Window" TO hamw%
        IF hamw% SYS "SendMessage", hamw%, WM_CLOSE, 0, 0
This website uses cookies for visitor traffic analysis. By using the website, you agree with storing the cookies on your computer.More information
playing_20a_20media_20file_20using_20direct_20show.txt · Last modified: 2018/04/15 13:03 by richardrussell