Windows >
Software Development for Windows Client Forums
>
DirectShow Development
>
Multiple Audio Track Playback Using WMV Files With Multiple Audio Streams in DES
Multiple Audio Track Playback Using WMV Files With Multiple Audio Streams in DES
- I have a video editing application that works fine using DES and WMV files containing two stereo audio streams. However, I'm having trouble growing the model to include WMV files with 3 or more audio streams.
I know there isn't a problem with the WMV Files as I can see and hear all the audio tracks when I drop the file into GraphEdit. I can also extract the WMA audio using the WMSDK stream tools.
In an attempt to solve the problem I've distilled the code down to what you see below (A win32 Console App). A DES Graph is created using one WMV file as the source of all video and audio. There is Video Group containing one Video Compostion and one Video Track/Source. One audio Group/Composition is created that has a track for each stream that is going to be played back.
I find the results I get when trying to access the third and fourth audio streams somewhat odd:
1: A video track on it's own plays back fine.
2: Video and Audio Stream 0 plays back fine.
3: Video and Audio Stream 1 plays back fine.
4: Video and Audio Stream 0 and 1 plays back fine
5: Video and Audio Stream 2 plays back fine.
6: Video and Audio Stream 0 and 2 plays back fine.
7: Video and Audio Stream 1 and 2 DOESN'T PLAY.
8: Video and Audio Stream 0, 1 and 2 DOESN'T PLAY.
9: Video and Audio Stream 3 plays back fine.
10: Video and Audio Stream 0 and 3 plays back fine.
11: Video and Audio Stream 1 and 3 DOESN'T PLAY.
12: Video and Audio Stream 0, 1 and 3 DOESN'T PLAY.
13: Video and Audio Stream 2 and 3 DOESN'T PLAY.
14: Video and Audio Stream 0, 2 and 3 DOESN'T PLAY.
15: Video and Audio Stream 1, 2 and 3 DOESN'T PLAY.
16: Video and Audio Stream 0, 1, 2 and 3 DOESN'T PLAY.
So it doesn't seem to be DES limiting playback to two audio groups, or only getting hold of the 3rd and 4th audio streams in the WMV file... it's quite odd!
I'm using Visual Studio 2008 on XP/Vista.
Microsoft Platform SDK for Windows Server 2003 R2
The WMV files were created using WMFSDK 9.5
The audio streams can be turned on and off by commenting the code blocks out in the method addAudio. The code as it stands work, and plays back the first two audio streams in the WMV file.
Any help would be greatfully appreciated.
Thanks
Matt
// DESTest.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "DESTest.h" #ifdef _DEBUG #define new DEBUG_NEW #endif #include <Streams.h> #include <ATLBase.h> #include <QEdit.h> #include <DShowASF.h> #include <DShow.h> // The one and only application object CWinApp theApp; using namespace std; HRESULT appMain(); #ifndef ON_FAIL #define ON_FAIL(error,hr) \ if( FAILED(hr) ) \ { \ char errStr[0xff]; \ sprintf(errStr,"%s 0x%8.8X\n",error,hr); \ printf(errStr); \ USES_CONVERSION; \ OutputDebugString(A2W(errStr)); \ return hr; \ } #endif #ifndef SAFE_RELEASE(x) #define SAFE_RELEASE(x) \ if( x != NULL ) \ { \ x->Release(); \ x = NULL; \ } #endif //////////////////////////////////////////////////////////////////////////////// // In/Out Points #define MEDIA_START 100000000 #define MEDIA_STOP 500000000 #define TIMELINE_START 0 #define TIMELINE_STOP ( MEDIA_STOP - MEDIA_START ) // // _tmain // // Application entry point. // int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { int nRetCode = 0; // initialize MFC and print and error on failure if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { // TODO: change error code to suit your needs _tprintf(_T("Fatal Error: MFC initialization failed\n")); nRetCode = 1; } else { CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); //Initializes COM appMain(); CoUninitialize(); // Releases COM } return nRetCode; } // _tmain // // SaveGraphFile // // Saves the graph to disk, so it can be inspectec in graph edit later. // HRESULT SaveGraphFile(IGraphBuilder *pGraph, WCHAR *wszPath) { HRESULT hr = S_OK; #ifdef _DEBUG const WCHAR wszStreamName[] = L"ActiveMovieGraph"; IStorage *pStorage = NULL; // First, create a document file that will hold the GRF file hr = StgCreateDocfile( wszPath, STGM_CREATE | STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pStorage); if(FAILED(hr)) { return hr; } // Next, create a stream to store. IStream *pStream; hr = pStorage->CreateStream(wszStreamName, STGM_WRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream); if (FAILED(hr)) { SAFE_RELEASE( pStorage ); return hr; } // The IpersistStream::Save method converts a stream // into a persistent object. IPersistStream *pPersist = NULL; pGraph->QueryInterface(IID_IPersistStream, reinterpret_cast<void**>(&pPersist)); hr = pPersist->Save(pStream, TRUE); SAFE_RELEASE( pStream ); SAFE_RELEASE( pPersist ); if (SUCCEEDED(hr)) { hr = pStorage->Commit(STGC_DEFAULT); } SAFE_RELEASE( pStorage ); #endif // _DEBUG return hr; } // SaveGraphFile // // addVideo // // Adds a video group and track to the timeline. Then places a video source on the track. // HRESULT addVideo(IAMTimeline *pTL,BSTR bstrFile) { HRESULT hr = S_OK; //////////////////////////////////////////////////////////////////////////// // GROUP: Add a video group to the timeline. IAMTimelineGroup *pGroup = NULL; IAMTimelineObj *pGroupObj = NULL; hr = pTL->CreateEmptyNode(&pGroupObj, TIMELINE_MAJOR_TYPE_GROUP); ON_FAIL("addVideo : ERROR : Failed to create the Video Group Object.",hr); hr = pGroupObj->QueryInterface(IID_IAMTimelineGroup, (void **)&pGroup); ON_FAIL("addVideo : ERROR : Failed to get a handle to the timeline group from the video group object.",hr); // Set the group media type. This example sets the type to "video" and // lets DES pick the default settings. For a more detailed example, // see "Setting the Group Media Type." AM_MEDIA_TYPE mtGroup; ZeroMemory(&mtGroup, sizeof(AM_MEDIA_TYPE)); mtGroup.majortype = MEDIATYPE_Video; hr = pGroup->SetMediaType(&mtGroup); ON_FAIL("addVideo : ERROR : Failed to set the Media Type of the Video Group.",hr); hr = pTL->AddGroup(pGroupObj); ON_FAIL("addVideo : ERROR : Failed to add the Video Group to the timeline.",hr); SAFE_RELEASE( pGroupObj ); // TRACK: Add a track to the VIDEO group. IAMTimelineObj *pTrackObj; IAMTimelineTrack *pTrack; IAMTimelineComp *pComp = NULL; hr = pTL->CreateEmptyNode(&pTrackObj, TIMELINE_MAJOR_TYPE_TRACK); ON_FAIL("addVideo : ERROR : Failed to the Video Track.",hr); hr = pGroup->QueryInterface(IID_IAMTimelineComp, (void **)&pComp); ON_FAIL("addVideo : ERROR : Failed to get a handle to the Timeline Composition from the Video Group.",hr); hr = pComp->VTrackInsBefore(pTrackObj, 0); ON_FAIL("addVideo : ERROR : Failed to insert the Video Track into the timeline.",hr); hr = pTrackObj->QueryInterface(IID_IAMTimelineTrack, (void **)&pTrack); ON_FAIL("addVideo : ERROR : Failed to get the TimelineTrack from the video Track.",hr); SAFE_RELEASE( pTrackObj ); // SOURCE: Add a source clip to the track. IAMTimelineSrc *pSource = NULL; IAMTimelineObj *pSourceObj = NULL; hr = pTL->CreateEmptyNode(&pSourceObj, TIMELINE_MAJOR_TYPE_SOURCE); ON_FAIL("addVideo : ERROR : Failed to create a new Video Source.",hr); hr = pSourceObj->QueryInterface(IID_IAMTimelineSrc, (void **)&pSource); ON_FAIL("addVideo : ERROR : Failed to get the Video Timeline Source from the Timeline Source Object.",hr); // Set the times and the file name. hr = pSource->SetMediaName(bstrFile); ON_FAIL("addVideo : ERROR : Failed to set the Video Source File Name.",hr); SysFreeString(bstrFile); hr = pSource->SetMediaTimes(MEDIA_START, MEDIA_STOP); ON_FAIL("addVideo : ERROR : Failed to set the Video Source Media Times.",hr); hr = pSourceObj->SetStartStop(TIMELINE_START, TIMELINE_STOP); ON_FAIL("addVideo : ERROR : Failed to set the Video Source Start/Stop times.",hr); hr = pTrack->SrcAdd(pSourceObj); ON_FAIL("addVideo : ERROR : Failed to add the Video source to the Video Track.",hr); SAFE_RELEASE( pSourceObj ); SAFE_RELEASE( pSource ); return hr; } // addVideo // // addAudioGroup // // Adds an audio group to the timeline. // HRESULT addAudioGroup(IAMTimeline *pTL, IAMTimelineGroup **ppAudioGroup) { HRESULT hr = S_OK; //////////////////////////////////////////////////////////////////////////// // GROUP: Add an audio group to the timeline. IAMTimelineObj *pAudioGroupObj = NULL; hr = pTL->CreateEmptyNode(&pAudioGroupObj, TIMELINE_MAJOR_TYPE_GROUP); ON_FAIL("addAudioGroup : ERROR : Failed to create the Audio Group",hr); hr = pAudioGroupObj->QueryInterface(IID_IAMTimelineGroup, (void **)ppAudioGroup); ON_FAIL("addAudioGroup : ERROR : Failed to get the TimelineGroup interface from the audio group.",hr); AM_MEDIA_TYPE amtGroup; ZeroMemory(&amtGroup, sizeof(AM_MEDIA_TYPE)); amtGroup.majortype = MEDIATYPE_Audio; hr = (*ppAudioGroup)->SetMediaType(&amtGroup); ON_FAIL("addAudioGroup : ERROR : Failed to set the Media Type of the Audio Group.",hr); hr = pTL->AddGroup(pAudioGroupObj); ON_FAIL("addAudioGroup : ERROR : Failed to add the Audio Group to the timline.",hr); SAFE_RELEASE( pAudioGroupObj ); return hr; } // addAudioGroup // // addAudioTrack // // Adds an audio track to the audio group. // HRESULT addAudioTrack(IAMTimeline *pTL,IAMTimelineGroup *pAudioGroup,IAMTimelineObj **ppAudioTrackObj) { HRESULT hr = S_OK; // TRACK: Add an audio track to the group. IAMTimelineComp *pAudioComp = NULL; hr = pTL->CreateEmptyNode(ppAudioTrackObj, TIMELINE_MAJOR_TYPE_TRACK); ON_FAIL("addAudioTrack : ERROR : Failed to create a new Audio Track.",hr); hr = pAudioGroup->QueryInterface(IID_IAMTimelineComp, (void **)&pAudioComp); ON_FAIL("addAudioTrack : ERROR : Failed to get the Audio Composition from the Audio Group.",hr); hr = pAudioComp->VTrackInsBefore(*ppAudioTrackObj, 0); ON_FAIL("addAudioTrack : ERROR : Failed to insert the Audio Track into the Audio Composition.",hr); SAFE_RELEASE( pAudioComp ); return hr; } // addAudioTrack // // addAudioSource // // Adds an audio source to the passed track. // // *pTL : Pointer to the timeline. // *pAudioTrackObj : Pointer to the Audio Track. // bstrFile : Name of the media file to take the audio stream from. // iStreamNumber : Which audio stream in the source media file to use. // HRESULT addAudioSource(IAMTimeline *pTL, IAMTimelineObj *pAudioTrackObj, BSTR bstrFile,INT iStreamNumber) { HRESULT hr = S_OK; // SOURCE: Add audio source to the track. IAMTimelineSrc *pSource = NULL; IAMTimelineObj *pSourceObj = NULL; IAMTimelineTrack *pAudioTrack = NULL; hr = pTL->CreateEmptyNode(&pSourceObj, TIMELINE_MAJOR_TYPE_SOURCE); ON_FAIL("addAudioSource : ERROR : Failed to create a new timeline source.",hr); hr = pSourceObj->QueryInterface(IID_IAMTimelineSrc, (void **)&pSource); ON_FAIL("addAudioSource : ERROR : Failed to get the Source from the Source Object.",hr); // Set the times and the file name. hr = pSourceObj->SetStartStop(TIMELINE_START,TIMELINE_STOP); ON_FAIL("addAudioSource : ERROR : Failed to set the Audio Source Start/Stop times.",hr); hr = pSource->SetMediaName(bstrFile); ON_FAIL("addAudioSource : ERROR : Failed to set the Audio Source File Name.",hr); SysFreeString(bstrFile); hr = pSource->SetMediaTimes(MEDIA_START,MEDIA_STOP); ON_FAIL("addAudioSource : ERROR : Failed to set the Audio Source Media Times.",hr); hr = pSource->SetStreamNumber(iStreamNumber); ON_FAIL("addAudioSource : ERROR : Failed to set the Audio Source Stream Number.",hr); hr = pAudioTrackObj->QueryInterface(IID_IAMTimelineTrack, (void **)&pAudioTrack); ON_FAIL("addAudioSource : ERROR : Failed to get the Timeline Track from the Audio Track Object.",hr); hr = pAudioTrack->SrcAdd(pSourceObj); ON_FAIL("addAudioSource : ERROR : Failed to add the Audio Source to the Audio Track.",hr); SAFE_RELEASE( pAudioTrack ); SAFE_RELEASE( pSourceObj ); SAFE_RELEASE( pSource ); return hr; } // addAudioSource // // addAudioStream // // Configures a whole audio stream/track on the timeline. // HRESULT addAudioStream( IAMTimeline *pTL, BSTR bstrFile, INT iStreamNumber, IAMTimelineGroup *pAudioGroup ) { HRESULT hr = S_OK; printf("addAudioStream : Adding Audio Stream %d.\n",iStreamNumber); IAMTimelineObj *pAudioTrackObj = NULL; hr = addAudioTrack(pTL,pAudioGroup,&pAudioTrackObj); ON_FAIL("addAudioStream : ERROR : Failed to add the Audio Track.",hr); hr = addAudioSource(pTL,pAudioTrackObj,bstrFile,iStreamNumber); ON_FAIL("addAudioStream : ERROR : Failed to add the Audio Source.",hr); SAFE_RELEASE( pAudioTrackObj ); return hr; } // addAudioStream // // addAudio // // Configures the audio portion of the graph. // HRESULT addAudio(IAMTimeline *pTL,BSTR bstrFile) { HRESULT hr = S_OK; IAMTimelineGroup *pAudioGroup = NULL; hr = addAudioGroup(pTL,&pAudioGroup); ON_FAIL("addAudio : ERROR : Failed to add the Audio Group.",hr); //////////////////////////////////////////////////////////////////////////// // printf("addAudio : Adding Audio Stream 0.\n"); hr = addAudioStream(pTL,bstrFile,0,pAudioGroup); ON_FAIL("addAudio : ERROR : Failed to add audio stream 0.",hr); printf("addAudio : Adding Audio Stream 1.\n"); hr = addAudioStream(pTL,bstrFile,1,pAudioGroup); ON_FAIL("addAudio : ERROR : Failed to add audio stream 1.",hr); //printf("addAudio : Adding Audio Stream 2.\n"); //hr = addAudioStream(pTL,bstrFile,2,pAudioGroup); //ON_FAIL("addAudio : ERROR : Failed to add audio stream 2.",hr); //printf("addAudio : Adding Audio Stream 3.\n"); //hr = addAudioStream(pTL,bstrFile,3,pAudioGroup); //ON_FAIL("addAudio : ERROR : Failed to add audio stream 3.",hr); // //////////////////////////////////////////////////////////////////////////// SAFE_RELEASE( pAudioGroup ); return hr; } // addAudio // // waitForState // // Blocks until passed State is reached, or error. // HRESULT waitForState(IMediaControl *pControl, OAFilterState filterState, BOOL fIgnoreFailure ) { HRESULT hr = S_OK; OAFilterState prevFS = -1; OAFilterState FS = -1; do { hr = pControl->GetState(0,&FS); switch( hr ) { case S_OK : { if( prevFS != FS) { switch(FS) { case State_Stopped : printf("waitForState : GetState : OK : Graph is Stopped.\n"); break; case State_Paused : printf("waitForState : GetState : OK : Graph is Paused.\n"); break; case State_Running : printf("waitForState : GetState : OK : Graph is Running.\n"); break; } prevFS = FS; } } break; case VFW_S_STATE_INTERMEDIATE : printf("waitForState : GetState : The filter graph is still in transition to the indicated state.\n"); break; case VFW_S_CANT_CUE : printf("waitForState : GetState : The filter graph is paused, but cannot cue data.\n"); break; case E_FAIL : printf("waitForState : GetState : Failure.\n"); break; default : printf("waitForState : GetState : Unknown Error 0x%8.8X.\n",hr); break; } Sleep(1000); }while( ((fIgnoreFailure==TRUE)?(TRUE):(hr!=E_FAIL)) && FS != filterState ); return hr; } // waitForState // // renderTimeline // // Renders the timeline once it has been built. // HRESULT renderTimeline(IAMTimeline *pTL) { HRESULT hr = S_OK; IGraphBuilder *pGraph = NULL; IMediaControl *pControl = NULL; IMediaEvent *pEvent = NULL; IRenderEngine *pRender = NULL; hr = CoCreateInstance(CLSID_RenderEngine,NULL,CLSCTX_INPROC_SERVER,IID_IRenderEngine,(void**) &pRender ); ON_FAIL("renderTimeline : ERROR : Failed to create an instance of the Render Engine.",hr); // Build the graph. hr = pRender->SetTimelineObject(pTL); ON_FAIL("renderTimeline : ERROR : Failed to set the Renderer's Timeline object.",hr); hr = pRender->ConnectFrontEnd( ); switch(hr) { case S_OK : printf("renderTimeline : ConnectFrontEnd : Success.\n"); break; case S_WARN_OUTPUTRESET : printf("renderTimeline : ConnectFrontEnd : Rendering portion of the graph was deleted.\n"); break; case E_INVALIDARG : printf("renderTimeline : ConnectFrontEnd : No timeline set for this render engine.\n"); break; case E_MUST_INIT_RENDERER : printf("renderTimeline : ConnectFrontEnd : Render engine failed to initialize.\n"); break; case E_RENDER_ENGINE_IS_BROKEN : printf("renderTimeline : ConnectFrontEnd : Operation failed because the project was not rendered successfully.\n"); break; case E_UNEXPECTED : printf("renderTimeline : ConnectFrontEnd : Unexpected error.\n"); break; case VFW_E_INVALIDMEDIATYPE : printf("renderTimeline : ConnectFrontEnd : Invalid media type.\n"); break; default : printf("renderTimeline : ConnectFrontEnd : Unknown Error : 0x%8.8X\n",hr); break; } ON_FAIL("renderTimeline : ERROR : Failed to connect the front end.",hr); hr = pRender->RenderOutputPins( ); ON_FAIL("renderTimeline : ERROR : Failed to Render the output pins.",hr); // Run the graph. hr = pRender->GetFilterGraph(&pGraph); ON_FAIL("renderTimeline : ERROR : Failed to get the Graph from the Renderer.",hr); hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl); ON_FAIL("renderTimeline : ERROR : Failed to get a handle to the Media Control Interface",hr); hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent); ON_FAIL("renderTimeline : ERROR : Failed to get a handle to the Media Event Interface.",hr); SaveGraphFile(pGraph, L"C:\\MyGraph_preview.GRF"); // Save the graph // file to disk hr = pControl->Run(); ON_FAIL("renderTimeline : ERROR : Failed to Run the Graph.",hr); switch(hr) { case S_OK : printf("renderTimeline : The graph is already running.\n"); break; case S_FALSE : { printf("renderTimeline : The graph is preparing to run...\n"); hr = waitForState(pControl, State_Running,FALSE); } break; default : printf("renderTimeline : ERROR : Unknown response from IMediaControl->Run. 0x%8.8X",hr); break; } long evCode; hr = pEvent->WaitForCompletion(INFINITE, &evCode); switch( hr ) { case S_OK : printf("renderTimeline : WaitForCompletion : S_OK - Success\n"); break; case E_ABORT : printf("renderTimeline : WaitForCompletion : E_ABORT - Time-out Expired.\n"); break; case VFW_E_WRONG_STATE : printf("renderTimeline : WaitForCompletion : VFW_E_WRONG_STATE - The fitler graph is not running.\n"); break; default : printf("renderTimeline : WaitForCompletion : Unknown : 0x%8.8X\n",hr); break; } ON_FAIL("renderTimeline : ERROR : Error when Waiting for Completion.",hr); hr = pControl->Stop(); ON_FAIL("renderTimeline : ERROR : Error when stopping the graph.",hr); // Clean up. SAFE_RELEASE( pEvent ); SAFE_RELEASE( pControl ); SAFE_RELEASE( pGraph ); return hr; } // renderTimeline // // appMain // // // HRESULT appMain() { BSTR bstrFile = SysAllocString(OLESTR("E:\\VideoWith4AudioStreams.wmv")); // Start by making an empty timeline. Add a media detector as well. IAMTimeline *pTL = NULL; CoInitialize(NULL); printf("appMain : Initialising the timeline...\n"); HRESULT hr = CoCreateInstance(CLSID_AMTimeline, NULL, CLSCTX_INPROC_SERVER, IID_IAMTimeline, (void**)&pTL); ON_FAIL("appMain : ERROR : Failed to create an instance of a Timeline.",hr); printf("appMain : Adding Video...\n"); hr = addVideo(pTL,bstrFile); ON_FAIL("appMain : ERROR : Failed to add the Video.",hr); if( TRUE ) { printf("appMain : Adding Audio...\n"); hr = addAudio(pTL,bstrFile); ON_FAIL("appMain : ERROR : Failed to add the Audio.",hr); } printf("appMain : Rendering Timeline...\n"); hr = renderTimeline(pTL); ON_FAIL("appMain : ERROR : Failed to render timeline.",hr); printf("appMain : Complete. 0x%.8X\n",hr); return hr; } // appMain
All Replies
- I've modified the code to Log DES errors (a la http://msdn.microsoft.com/en-us/library/dd390656(VS.85).aspx ), but none are reported.
Any help would be greatly appreciated.
Kind Regards
Matt - If I use the first audio stream from four seperate WMV files this also works - but isn't what I want!
This leads me to believe it isn't a problem with the way the graph is constructed but more to do with the way DirectShow/DES is dealing audio streams from the same WMV source. - Adding audio from four seperate WMV files, using the 1st audio stream from the first file, 2nd audio stream from the 2nd file etc. doesn't work either.
// // addAudio // // Configures the audio portion of the graph. // HRESULT addAudio(IAMTimeline *pTL,BSTR bstrFile) { HRESULT hr = S_OK; IAMTimelineGroup *pAudioGroup = NULL; hr = addAudioGroup(pTL,&pAudioGroup); ON_FAIL("addAudio : ERROR : Failed to add the Audio Group.",hr); //////////////////////////////////////////////////////////////////////////// // printf("addAudio : Adding Audio Stream 0.\n"); hr = addAudioStream(pTL,SysAllocString(OLESTR("c:\\File1.wmv")),0,pAudioGroup); ON_FAIL("addAudio : ERROR : Failed to add audio stream 0.",hr); printf("addAudio : Adding Audio Stream 1.\n"); hr = addAudioStream(pTL,SysAllocString(OLESTR("c:\\File2.wmv")),1,pAudioGroup); ON_FAIL("addAudio : ERROR : Failed to add audio stream 1.",hr); printf("addAudio : Adding Audio Stream 2.\n"); hr = addAudioStream(pTL,SysAllocString(OLESTR("c:\\File3.wmv")),2,pAudioGroup); ON_FAIL("addAudio : ERROR : Failed to add audio stream 2.",hr); printf("addAudio : Adding Audio Stream 3.\n"); hr = addAudioStream(pTL,SysAllocString(OLESTR("C:\\File4.wmv")),3,pAudioGroup); ON_FAIL("addAudio : ERROR : Failed to add audio stream 3.",hr); // //////////////////////////////////////////////////////////////////////////// SAFE_RELEASE( pAudioGroup ); return hr; } // addAudio
- Bump
Any ideas, any one?
Thanks
Matt


