=====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 [[http://www.bbcbasic.co.uk/bbcwin/examples/player.html|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