DirectShow - Analyze audio stream from AVI - as fast as possible
-
I need to create audio peaks for the audio stream from an AVI. For this I intend to use DirectShow by using SampleGrabber. I adapted some of the samples from the SDK. There is only one problem here: I receive the audio samples in real-time (i.e. the same as I would be playing the file), but I need this process to be much faster. I was advised to set the graph SyncSource to NULL and by this the samples will be processed as quickly as possible. Unfortunatelly I get no effect by doing this. I'm attaching here the code I'm using, perhaps I'm doing something wrong and somebody can help me out Thanks alot !
#include
#include
#include
#include
#includeint GrabBitmaps(TCHAR * szFile);
HRESULT GetPin(IBaseFilter * pFilter, PIN_DIRECTION dirrequired, int iNum, IPin **ppPin);
IPin * GetInPin ( IBaseFilter *pFilter, int Num );
IPin * GetOutPin( IBaseFilter *pFilter, int Num );class CSampleGrabberCB : public ISampleGrabberCB {
public:
STDMETHODIMP\_(ULONG) AddRef() { return 2; } STDMETHODIMP\_(ULONG) Release() { return 1; } STDMETHODIMP QueryInterface(REFIID riid, void \*\* ppv) { CheckPointer(ppv,E\_POINTER); if( riid == IID\_ISampleGrabberCB || riid == IID\_IUnknown ) { \*ppv = (void \*) static\_cast ( this ); return NOERROR; } return E\_NOINTERFACE; } STDMETHODIMP SampleCB( double SampleTime, IMediaSample \* pSample ) {return 0;} STDMETHODIMP BufferCB( double SampleTime, BYTE \* pBuffer, long BufferSize ) { \_tprintf(TEXT("Found a sample at %f s\\t\[%ld\]\\r\\n"), SampleTime , BufferSize ); return 0; }
}
int _tmain(int argc, TCHAR* argv[]) {
if( argc != 2 || !argv || !argv[1] ) {
_tprintf( TEXT("GrabBitmaps: You must specify a media filename!\r\n") );
_tprintf( TEXT("Usage: GrabBitmaps Filename\r\n"));
return 0;
}CoInitializeEx(NULL, COINIT\_APARTMENTTHREADED); // Read the filename from the command line TCHAR szFile\[MAX\_PATH\]; \_tcsncpy(szFile, argv\[1\], MAX\_PATH-1); szFile\[MAX\_PATH-1\] = 0; // Null-terminate int nSuccess = GrabBitmaps(szFile); CoUninitialize(); return nSuccess;
}
int GrabBitmaps(TCHAR * szFile ) {
USES_CONVERSION;
CComPtr< ISampleGrabber > pGrabber;
CComPtr< IBaseFilter > pSource;
CComPtr< IGraphBuilder > pGraph;
CComPtr< IVideoWindow > pVideoWindow;
HRESULT hr;if (!szFile) return -1; \_tprintf(TEXT("Grabbing bitmaps from %s.\\r\\n"), szFile); // Create the sample grabber pGrabber.CoCreateI
-
I need to create audio peaks for the audio stream from an AVI. For this I intend to use DirectShow by using SampleGrabber. I adapted some of the samples from the SDK. There is only one problem here: I receive the audio samples in real-time (i.e. the same as I would be playing the file), but I need this process to be much faster. I was advised to set the graph SyncSource to NULL and by this the samples will be processed as quickly as possible. Unfortunatelly I get no effect by doing this. I'm attaching here the code I'm using, perhaps I'm doing something wrong and somebody can help me out Thanks alot !
#include
#include
#include
#include
#includeint GrabBitmaps(TCHAR * szFile);
HRESULT GetPin(IBaseFilter * pFilter, PIN_DIRECTION dirrequired, int iNum, IPin **ppPin);
IPin * GetInPin ( IBaseFilter *pFilter, int Num );
IPin * GetOutPin( IBaseFilter *pFilter, int Num );class CSampleGrabberCB : public ISampleGrabberCB {
public:
STDMETHODIMP\_(ULONG) AddRef() { return 2; } STDMETHODIMP\_(ULONG) Release() { return 1; } STDMETHODIMP QueryInterface(REFIID riid, void \*\* ppv) { CheckPointer(ppv,E\_POINTER); if( riid == IID\_ISampleGrabberCB || riid == IID\_IUnknown ) { \*ppv = (void \*) static\_cast ( this ); return NOERROR; } return E\_NOINTERFACE; } STDMETHODIMP SampleCB( double SampleTime, IMediaSample \* pSample ) {return 0;} STDMETHODIMP BufferCB( double SampleTime, BYTE \* pBuffer, long BufferSize ) { \_tprintf(TEXT("Found a sample at %f s\\t\[%ld\]\\r\\n"), SampleTime , BufferSize ); return 0; }
}
int _tmain(int argc, TCHAR* argv[]) {
if( argc != 2 || !argv || !argv[1] ) {
_tprintf( TEXT("GrabBitmaps: You must specify a media filename!\r\n") );
_tprintf( TEXT("Usage: GrabBitmaps Filename\r\n"));
return 0;
}CoInitializeEx(NULL, COINIT\_APARTMENTTHREADED); // Read the filename from the command line TCHAR szFile\[MAX\_PATH\]; \_tcsncpy(szFile, argv\[1\], MAX\_PATH-1); szFile\[MAX\_PATH-1\] = 0; // Null-terminate int nSuccess = GrabBitmaps(szFile); CoUninitialize(); return nSuccess;
}
int GrabBitmaps(TCHAR * szFile ) {
USES_CONVERSION;
CComPtr< ISampleGrabber > pGrabber;
CComPtr< IBaseFilter > pSource;
CComPtr< IGraphBuilder > pGraph;
CComPtr< IVideoWindow > pVideoWindow;
HRESULT hr;if (!szFile) return -1; \_tprintf(TEXT("Grabbing bitmaps from %s.\\r\\n"), szFile); // Create the sample grabber pGrabber.CoCreateI
What do you need to do with the output - does it need to be rendered? Is there a video stream as well as the audio stream? Mark
"Posting a VB.NET question in the C++ forum will end in tears." Chris Maunder
-
What do you need to do with the output - does it need to be rendered? Is there a video stream as well as the audio stream? Mark
"Posting a VB.NET question in the C++ forum will end in tears." Chris Maunder
I don't need to render the output, I just need to analyze it and write the results to another file. Yes, the input media file contains video as well as audio. Claudiu
-
I don't need to render the output, I just need to analyze it and write the results to another file. Yes, the input media file contains video as well as audio. Claudiu
By using pGraph->Render(), you're getting the default renderers, right? In your current code, does the audio play when you run the graph? I'm thinking if you use the NULL Renderer filter on the outputs of the grabber and the AVI Splitter (instead of the default renderers) then maybe the graph will run as fast as possible with the sync source set to NULL. I thought the audio renderer was determining the flow rate of samples through the graph, which is why setting the sync source to NULL has no effect. If the AVI splitter is pushing samples at the real-time rate, then you'd probably need a custom grabber filter. Mark
"Posting a VB.NET question in the C++ forum will end in tears." Chris Maunder
-
By using pGraph->Render(), you're getting the default renderers, right? In your current code, does the audio play when you run the graph? I'm thinking if you use the NULL Renderer filter on the outputs of the grabber and the AVI Splitter (instead of the default renderers) then maybe the graph will run as fast as possible with the sync source set to NULL. I thought the audio renderer was determining the flow rate of samples through the graph, which is why setting the sync source to NULL has no effect. If the AVI splitter is pushing samples at the real-time rate, then you'd probably need a custom grabber filter. Mark
"Posting a VB.NET question in the C++ forum will end in tears." Chris Maunder
Exactly, the audio gets played when I run the graph. Can you please show me where to insert those filters in the graph? To make a test and see if it's working ? Claudiu
-
Exactly, the audio gets played when I run the graph. Can you please show me where to insert those filters in the graph? To make a test and see if it's working ? Claudiu
After you connect the source filter to the grabber filter, there will be some splitter filter(s) inserted in between by the graph builder. To get an idea of exactly which filter(s) are inserted use GRAPHEDIT.EXE - Add a file source (async) filter, set it's input filename in the dialog, add a grabber filter, and connect the output of the source to the input of the grabber. You'll then see the inserted filter(s). So after you connect the source filter to the grabber filter, You can traverse the partial graph (as described here[^]) starting from the grabber's input pin. Unless an intermediate decoder filter was needed for the audio stream, the next filter upstream should be a splitter filter (it may be AVI, MPEG-1, or other depending on the file format). It should be the first filter you find that has unconnected output pins (see Find an Unconnected Pin on a Filter[^]). For each of the unconnected output pins, create a NULL renderer filter and connect its input pin to the unconnected output pin. Create another NULL Renderer filter and connect it to the grabber filter's output pin. The graph should be complete at this point. You don't need to call pGraph->Render(). You'll still want to call SetSyncSource on the graph builder's IMediaFilter interface to set the sync source to NULL. Mark
"Posting a VB.NET question in the C++ forum will end in tears." Chris Maunder
-
By using pGraph->Render(), you're getting the default renderers, right? In your current code, does the audio play when you run the graph? I'm thinking if you use the NULL Renderer filter on the outputs of the grabber and the AVI Splitter (instead of the default renderers) then maybe the graph will run as fast as possible with the sync source set to NULL. I thought the audio renderer was determining the flow rate of samples through the graph, which is why setting the sync source to NULL has no effect. If the AVI splitter is pushing samples at the real-time rate, then you'd probably need a custom grabber filter. Mark
"Posting a VB.NET question in the C++ forum will end in tears." Chris Maunder
I managed to create the NULL Renderer and insert it into the graph and it works ! The samples are received as I would expected. Here's the modifications I made:
CComPtr nullRenderer;
hr = nullRenderer.CoCreateInstance(CLSID_NullRenderer);
hr = pGraph->AddFilter(nullRenderer, L"NULL renderer");CComPtr nullGrabInPin = GetInPin(nullRenderer, 0);
CComPtr pGrabOutPin = GetOutPin(pGrabberBase, 0);
pGraph->Connect(pGrabOutPin, nullGrabInPin);Thanks so much for your help ! Now I have to find somewhere the format of the data received on BufferCB for audio PCM and I'm all done. Claudiu
-
I managed to create the NULL Renderer and insert it into the graph and it works ! The samples are received as I would expected. Here's the modifications I made:
CComPtr nullRenderer;
hr = nullRenderer.CoCreateInstance(CLSID_NullRenderer);
hr = pGraph->AddFilter(nullRenderer, L"NULL renderer");CComPtr nullGrabInPin = GetInPin(nullRenderer, 0);
CComPtr pGrabOutPin = GetOutPin(pGrabberBase, 0);
pGraph->Connect(pGrabOutPin, nullGrabInPin);Thanks so much for your help ! Now I have to find somewhere the format of the data received on BufferCB for audio PCM and I'm all done. Claudiu
Claudiu Bucur wrote:
Now I have to find somewhere the format of the data received on BufferCB for audio PCM
Maybe pGrabPin->ConnectionMediaType()
"Posting a VB.NET question in the C++ forum will end in tears." Chris Maunder