c++ builder: convert video to png-snapshots with directshow

1.5k views Asked by At

Thanks to your help I was able to search for the right words to use directshow a bit better. I found a tutorial how to use the SampleGrabber-object here: http://msdn.microsoft.com/en-us/library/windows/desktop/dd407288%28v=vs.85%29.aspx

I could implement it and modified it a bit so it isn't just saving the first frame, but every Frame to a PNG. For that I use corona. However, I just guessed something around and don't quite know which buffers are containing my data and in which form.

So, I have basically 3 questions:
Am I using SavePNG right? the resulting Images are upside-down!
Can I replace the BaseFilter for the video with one that is connected to a camera?
Contains pBuffer my Imagedata so I can get rgb-byte-informations by simply type pBuffer[123]?

I'm using embarcadero's C++-Builder (XE2 16).

Here is the code I found at the website, a bit modified (error-handling removed for better view. after each hr=... there is a Failed-check):

void __fastcall TForm1::btn_kameraClick(TObject *Sender)
{
HRESULT hr = S_OK;
    IGraphBuilder *pGraph = NULL;
    IMediaControl *pControl = NULL;
    IMediaEventEx *pEvent = NULL;
    IBaseFilter *pGrabberF = NULL;
    ISampleGrabber *pGrabber = NULL;
    IBaseFilter *pSourceF = NULL;
    IEnumPins *pEnum = NULL;
    IPin *pPin = NULL;
    IBaseFilter *pNullF = NULL;
    BYTE *pBuffer = NULL;

    hr = CoCreateInstance(CLSID_FilterGraph, NULL,CLSCTX_INPROC_SERVER,IID_PPV_ARGS(&pGraph));
    hr = pGraph->QueryInterface(IID_PPV_ARGS(&pControl));
    hr = pGraph->QueryInterface(IID_PPV_ARGS(&pEvent));
    hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,IID_PPV_ARGS(&pGrabberF));
    hr = pGraph->AddFilter(pGrabberF, L"Sample Grabber");
    hr = pGrabberF->QueryInterface(IID_PPV_ARGS(&pGrabber));

    AM_MEDIA_TYPE mt;
    ZeroMemory(&mt, sizeof(mt));
    mt.majortype = MEDIATYPE_Video;
    mt.subtype = MEDIASUBTYPE_RGB24;

    hr = pGrabber->SetMediaType(&mt);
    hr = pGraph->AddSourceFilter(L"C:/Users/Julian/Desktop/homogenität/1,1x_2,7y.mpg", L"Source", &pSourceF);
    hr = pSourceF->EnumPins(&pEnum);
    while (S_OK == pEnum->Next(1, &pPin, NULL))
    {
        hr = ConnectFilters(pGraph, pPin, pGrabberF);
        SafeRelease(&pPin);
        if (SUCCEEDED(hr))break;
    }
    hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,IID_PPV_ARGS(&pNullF));
    hr = pGraph->AddFilter(pNullF, L"Null Filter");
    hr = ConnectFilters(pGraph, pGrabberF, pNullF);
    hr = pGrabber->SetOneShot(TRUE);
    hr = pGrabber->SetBufferSamples(TRUE);
    long evCode=0;
    long cbBuffer=0;
    hr = pControl->Run();
    hr = pEvent->WaitForCompletion(INFINITE, &evCode);
    hr = pGrabber->GetCurrentBuffer(&cbBuffer, NULL);
    pBuffer = (BYTE*)CoTaskMemAlloc(cbBuffer);
    hr = pGrabber->GetConnectedMediaType(&mt);

    CComQIPtr< IMediaSeeking, &IID_IMediaSeeking > pSeeking( pGraph );


//  for(int i=0;i<10;i++){
    bool hui=true;int i=0;
    while(hui){
    REFERENCE_TIME Start = i * UNITS;
    hr = pSeeking->SetPositions( &Start, AM_SEEKING_AbsolutePositioning,NULL, AM_SEEKING_NoPositioning );
//  Sleep(10);
    hr = pEvent->WaitForCompletion(INFINITE,&evCode);
    if(hr!=0)hui=false;
    hr = pGrabber->GetCurrentBuffer(&cbBuffer, (long*)pBuffer);
    if ((mt.formattype == FORMAT_VideoInfo) &&(mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&(mt.pbFormat != NULL))
    {
        VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)mt.pbFormat;

//      hr = WriteBitmap(("hui"+(String)i+".bmp").c_str(), &pVih->bmiHeader, mt.cbFormat - SIZE_PREHEADER, pBuffer, cbBuffer);
        hr = SavePNG(i,pBuffer, pVih->bmiHeader.biWidth, pVih->bmiHeader.biHeight);
    }
    else hr = VFW_E_INVALIDMEDIATYPE;
    i++;
    }
    FreeMediaType(mt);

done:
    CoTaskMemFree(pBuffer);
    SafeRelease(&pPin);
    SafeRelease(&pEnum);
    SafeRelease(&pNullF);
    SafeRelease(&pSourceF);
    SafeRelease(&pGrabber);
    SafeRelease(&pGrabberF);
    SafeRelease(&pControl);
    SafeRelease(&pEvent);
    SafeRelease(&pGraph);

}
bool SavePNG(int i, Byte* m_pImageData,long m_Width,long m_Height)
{
    // Make sure there is image data

    if (!m_pImageData)
        return false;

    stringstream FilePath;
    FilePath << "hui"<< i<<".png";
    // Create a corona image
    corona::Image* pImage = corona::CreateImage(m_Width, m_Height, corona::PF_R8G8B8, m_pImageData);

    // Make sure the image was created
    if (!pImage)
        return false;

    // Save the image to a PNG file
    corona::SaveImage(FilePath.str().c_str(), corona::FF_PNG, pImage);

    // Delete the corona image
    delete pImage;

    // Nothing went wrong
    return true;
}

I hope I have done nothing horribly wrong... I really tried to research everything^^ Does somebody knows about my 3 questions above? I you found something really wrong here, I would also appreciate for you to tell me, so I can fix and improve.

Regards, Julian

1

There are 1 answers

3
Roman Ryltsov On BEST ANSWER

Now when you have a video frame in 24-bit RGB format, all you need is to compress to PNG. You have choices here:

Possibly, C++ bulder has native classes to cover PNG as well.

P.S. DirectShow API you are using is not DirectX, it is a part of Windows core SDK.