Below is a snippet that I took from the MSDN example, which basically interrupts the video stream into small thumbnails at selected intervals:
//------------------------------------------------------------------- // CreateBitmaps // // Creates an array of thumbnails from the video file. // // pRT: Direct2D render target. Used to create the bitmaps. // count: Number of thumbnails to create. // pSprites: An array of Sprite objects to hold the bitmaps. // // Note: The caller allocates the sprite objects. //------------------------------------------------------------------- HRESULT ThumbnailGenerator::CreateBitmaps( ID2D1RenderTarget *pRT, DWORD count, Sprite pSprites[] ) { HRESULT hr = S_OK; BOOL bCanSeek = 0; LONGLONG hnsDuration = 0; LONGLONG hnsRangeStart = 0; LONGLONG hnsRangeEnd = 0; LONGLONG hnsIncrement = 0; hr = CanSeek(&bCanSeek); if (FAILED(hr)) { return hr; } if (bCanSeek) { hr = GetDuration(&hnsDuration); if (FAILED(hr)) { return hr; } hnsRangeStart = 0; hnsRangeEnd = hnsDuration; // We have the file duration , so we'll take bitmaps from // several positions in the file. Occasionally, the first frame // in a video is black, so we don't start at time 0. hnsIncrement = (hnsRangeEnd - hnsRangeStart) / (count + 1); } // Generate the bitmaps and invalidate the button controls so // they will be redrawn. for (DWORD i = 0; i < count; i++) { LONGLONG hPos = hnsIncrement * (i + 1); hr = CreateBitmap( pRT, hPos, &pSprites[i] ); } return hr; } // /// Private methods // //------------------------------------------------------------------- // CreateBitmap // // Creates one video thumbnail. // // pRT: Direct2D render target. Used to create the bitmap. // hnsPos: The seek position. // pSprite: A Sprite object to hold the bitmap. //------------------------------------------------------------------- HRESULT ThumbnailGenerator::CreateBitmap( ID2D1RenderTarget *pRT, LONGLONG& hnsPos, Sprite *pSprite ) { HRESULT hr = S_OK; DWORD dwFlags = 0; BYTE *pBitmapData = NULL; // Bitmap data DWORD cbBitmapData = 0; // Size of data, in bytes LONGLONG hnsTimeStamp = 0; BOOL bCanSeek = FALSE; // Can the source seek? DWORD cSkipped = 0; // Number of skipped frames IMFMediaBuffer *pBuffer = 0; IMFSample *pSample = NULL; ID2D1Bitmap *pBitmap = NULL; hr = CanSeek(&bCanSeek); if (FAILED(hr)) { return hr; } if (bCanSeek && (hnsPos > 0)) { PROPVARIANT var; PropVariantInit(&var); var.vt = VT_I8; var.hVal.QuadPart = hnsPos; hr = m_pReader->SetCurrentPosition(GUID_NULL, var); if (FAILED(hr)) { goto done; } } // Pulls video frames from the source reader. // NOTE: Seeking might be inaccurate, depending on the container // format and how the file was indexed. Therefore, the first // frame that we get might be earlier than the desired time. // If so, we skip up to MAX_FRAMES_TO_SKIP frames. while (1) { IMFSample *pSampleTmp = NULL; hr = m_pReader->ReadSample( (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, NULL, &dwFlags, NULL, &pSampleTmp ); if (FAILED(hr)) { goto done; } if (dwFlags & MF_SOURCE_READERF_ENDOFSTREAM) { break; } if (dwFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED) { // Type change. Get the new format. hr = GetVideoFormat(&m_format); if (FAILED(hr)) { goto done; } } if (pSampleTmp == NULL) { continue; } // We got a sample. Hold onto it. SafeRelease(&pSample); pSample = pSampleTmp; pSample->AddRef(); if (SUCCEEDED( pSample->GetSampleTime(&hnsTimeStamp) )) { // Keep going until we get a frame that is within tolerance of the // desired seek position, or until we skip MAX_FRAMES_TO_SKIP frames. // During this process, we might reach the end of the file, so we // always cache the last sample that we got (pSample). if ( (cSkipped < MAX_FRAMES_TO_SKIP) && (hnsTimeStamp + SEEK_TOLERANCE < hnsPos) ) { SafeRelease(&pSampleTmp); ++cSkipped; continue; } } SafeRelease(&pSampleTmp); hnsPos = hnsTimeStamp; break; } if (pSample) { UINT32 pitch = 4 * m_format.imageWidthPels; // Get the bitmap data from the sample, and use it to create a // Direct2D bitmap object. Then use the Direct2D bitmap to // initialize the sprite. hr = pSample->ConvertToContiguousBuffer(&pBuffer); if (FAILED(hr)) { goto done; } hr = pBuffer->Lock(&pBitmapData, NULL, &cbBitmapData); if (FAILED(hr)) { goto done; } assert(cbBitmapData == (pitch * m_format.imageHeightPels)); hr = pRT->CreateBitmap( D2D1::SizeU(m_format.imageWidthPels, m_format.imageHeightPels), pBitmapData, pitch, D2D1::BitmapProperties( // Format = RGB32 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE) ), &pBitmap ); if (FAILED(hr)) { goto done; } pSprite->SetBitmap(pBitmap, m_format); } else { hr = MF_E_END_OF_STREAM; } done: if (pBitmapData) { pBuffer->Unlock(); } SafeRelease(&pBuffer); SafeRelease(&pSample); SafeRelease(&pBitmap); return hr; }
Instead of bitmaps placed inside Sprite objects (passed as an array), I want the method to return an array of bitmaps. MSDN assumes that the Bitmap class exists for C ++, although I cannot include a reference to it for this class. What I want to do is compile this class as a DLL eventually and use it in my C # project, where I can pass the uri to the movie and split it and return it as raster frames from these methods. I will take care of the logic myself, I just need to know how I could process this code and return Bitmap[] from frames from the CreateBitmaps() method.