How to render and save captured Video/Audio into a

2019-09-15 01:14发布

问题:

Basiclly, I want to capture audio/video. Run it through a mp4 muxer and save it to a file on disk. Before I used ICaptureGraphBuilder2, but it seems unusuable when saving to custom formats.

My code so far,

I enumerate video/audio devices. In this sample I only try to capture audio. I get the correct device and use GetPin to enumerate the filters pins to get its output pin.

hr = pMoniker2->BindToObject(0, 0, IID_IBaseFilter, (void**)&pSrc2);
hr = pGraph->AddFilter(pSrc2, L"AudioCap");

hr = GetPin(pSrc2, PINDIR_OUTPUT, &outPin);

This is the custom filter, a MP4 muxer. It properly loads and I can get the input pin and connect it with my output pin. So far so good.

HRESULT hr = CreateObjectFromPath(TEXT("c:\\filters\\mp4mux.dll"), clsid, &pUnk);
if (SUCCEEDED(hr))
{
    IBaseFilterPtr pFilter = pUnk;
    HRESULT hr = pGraph->AddFilter(pFilter, L"Private Filter");
    hr = GetPin(pFilter, PINDIR_INPUT, &inPin);
}

hr = pGraph->Connect(outPin, inPin);

This is where I get lost, I can't find out how to take the next steps to Render and save the output to a file on disk. So any help with the next steps would be greatfully appreciated, thanks in advance!

EDIT: Filesink code

AM_MEDIA_TYPE mType;

mType.majortype = MEDIATYPE_Video;
mType.subtype = MEDIASUBTYPE_H264;
mType.bFixedSizeSamples = FALSE;
mType.bTemporalCompression = TRUE;
mType.lSampleSize = 0;
mType.formattype = FORMAT_None;
mType.pUnk = NULL;
mType.cbFormat = 0;
mType.pbFormat = NULL;
//Not 100% sure about the setup of the media format.

IBaseFilter * iFiltera = NULL; 
IFileSinkFilter* iFilter = NULL; 
IGraphBuilder *pGraph;

hr = pMoniker2->BindToObject(0, 0, IID_IBaseFilter, (void**)&pSrc2); //audio capture
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,  IID_IGraphBuilder, (void**)&pGraph);
hr = CoCreateInstance(CLSID_FileWriter, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&iFiltera);

hr = pBuild->SetFiltergraph(pGraph);

hr = pGraph->AddFilter(pSrc2, L"AudioCap");
hr = GetPin(pSrc2, PINDIR_OUTPUT, &outPin); //ADDED

hr = pGraph->AddFilter(iFiltera, L"FileWriter");
hr = iFiltera->QueryInterface(IID_IFileSinkFilter, (void**)&iFilter);

iFilter->SetFileName((LPCOLESTR)"c:\\wav\\tester.mp4", NULL); //UPDATED mType set to NULL

HRESULT hr = CreateObjectFromPath(TEXT("c:\\filters\\mp4mux.dll"), clsid, &pUnk);

IBaseFilterPtr pFilter = pUnk;
if (SUCCEEDED(hr))
{

    HRESULT hr = pGraph->AddFilter(pFilter, L"Private Filter");
    hr = GetPin(pFilter, PINDIR_INPUT, &inPin); //mux in

    hr = GetPin(pFilter, PINDIR_OUTPUT, &mOutPin); //mux out
    hr = GetPin(iFiltera, PINDIR_INPUT, &filePin); // filewriter in
}

hr = pGraph->Connect(outPin, inPin); //connect audio out and mux in
hr = pGraph->Connect(mOutPin, filePin); //connect mux out and file in; Error 0x80040217(VFW_E_CANNOT_CONNECT?) //works now

//ADDED code
IMediaControl *pMC = NULL;
hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pMC);

hr = pMC->Run();
Sleep(4000);
hr = pMC->Stop();  

回答1:

You need to have an idea what filter graph topology you need for specific task. You are doing capture, here - fine. So you have audio capture filter you provided code snippet for. Then you either compress audio (where your preferred choice should be AAC AKA MPEG-4 Part 3, provided that you are to produce MP4 file), or keep audio uncompressed PCM. Then you connect MPEG-4 multiplexer as you do. The multiplexer produces output stream and you are expected to complete the pipeline with File Writer Filter.

You can manually build the chain in GraphEdit SDK application (or there are alternate richer tools). Your filter graph looks like this:

Note that you can expose filter graph in your application, and connect to it remotely and inspect the topology. This makes debugging much easier. Starting/stopping the filter graph (IMediaControl::Run, ::Stop from code) creates you the file.

My understanding is that you get lost immediately after adding multiplexer. Now you need to find its output pin, add File Writer, query its IFileSinkFilter, set the destination file name using it, find its input pin, connect the two unconnected pins (mux output, writer input). Your pipeline is ready to run.