Samouczek: odczytywanie pliku ASF przy użyciu obiektów WMContainer
W tym samouczku pokazano, jak pobrać pakiety danych z pliku Advanced Systems Format (ASF) przy użyciu dzielnika ASF . W tym samouczku utworzysz prostą aplikację konsolową, która odczytuje plik ASF i generuje skompresowane próbki danych multimedialnych dla pierwszego strumienia wideo w pliku. Aplikacja wyświetla informacje o klatkach kluczowych w strumieniu wideo.
Ten samouczek zawiera następujące kroki:
- wymagania wstępne
- 1. Konfigurowanie projektu
- 2. Otwórz plik ASF
- 3. Przeczytaj obiekt nagłówka ASF
- 4. Utwórz rozdzielacz ASF
- 5. Wybieranie strumienia do analizowania
- 6. Generowanie skompresowanych przykładów multimediów
- 7. Napisz Entry-Point funkcję
- Lista programu
- Tematy pokrewne
W samouczku nie opisano sposobu dekodowania skompresowanych danych, które aplikacja otrzymuje z rozdzielacza ASF.
Warunki wstępne
Ten samouczek zakłada, że:
- Znasz strukturę pliku ASF i składniki udostępniane przez program Media Foundation do pracy z obiektami ASF. Te składniki obejmują obiekt ContentInfo, rozdzielacz, multiplekser i profil. Aby uzyskać więcej informacji, zobacz WMContainer ASF Components.
- Znasz Bufory Multimediów i strumienie bajtów: w szczególności operacje na plikach za pomocą strumienia bajtów, odczytywanie danych ze strumienia bajtów do buforu multimediów i zapisywanie zawartości buforu multimediów do strumienia bajtów.
1. Konfigurowanie projektu
Dołącz następujące nagłówki do pliku źródłowego:
#include <stdio.h> // Standard I/O
#include <windows.h> // Windows headers
#include <mfapi.h> // Media Foundation platform
#include <wmcontainer.h> // ASF interfaces
#include <Mferror.h>
Dołącz do następujących plików biblioteki:
- mfplat.lib
- mf.lib
- mfuuid.lib
Zadeklaruj funkcję SafeRelease:
template <class T> void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
Otwórz plik ASF
Następnie otwórz określony plik, wywołując funkcję MFCreateFile. Metoda zwraca wskaźnik do obiektu strumienia bajtów, który zawiera zawartość pliku. Nazwa pliku jest określana przez użytkownika za pośrednictwem argumentów wiersza polecenia aplikacji.
Poniższy przykładowy kod przyjmuje nazwę pliku i zwraca wskaźnik do obiektu strumienia bajtów, którego można użyć do odczytania pliku.
// Open the file.
hr = MFCreateFile(MF_ACCESSMODE_READ, MF_OPENMODE_FAIL_IF_NOT_EXIST,
MF_FILEFLAGS_NONE, pszFileName, &pStream);
3. Odczyt obiektu nagłówka ASF
Następnie utwórz obiekt ASF ContentInfo i użyj go do analizowania obiektu nagłówka ASF określonego pliku. Obiekt ContentInfo przechowuje informacje z nagłówka ASF, w tym atrybuty plików globalnych i informacje o każdym strumieniu. W dalszej części samouczka użyjesz obiektu ContentInfo, aby zainicjować rozdzielacz ASF i uzyskać numer strumienia wideo.
Aby utworzyć obiekt ASF ContentInfo:
- Wywołaj funkcję MFCreateASFContentInfo, aby utworzyć obiekt ContentInfo. Metoda zwraca wskaźnik do interfejsu IMFASFContentInfo.
- Odczytaj pierwsze 30 bajtów danych z pliku ASF do buforu multimediów.
- Przekaż bufor multimedialny do metody IMFASFContentInfo::GetHeaderSize. Ta metoda zwraca całkowity rozmiar obiektu nagłówka w pliku ASF.
- Przekaż ten sam bufor multimedialny do metody IMFASFContentInfo::ParseHeader.
- Odczytaj pozostałą część obiektu header do nowego buforu multimediów.
- Przekaż drugi bufor do metody ParseHeader. Określ przesunięcie o 30 bajtów w cbOffsetWithinHeader parametru ParseHeader. Metoda ParseHeader inicjuje obiekt ContentInfo, wykorzystując informacje zebrane z różnych obiektów ASF zawartych w Obiekcie nagłówka.
// Read the ASF Header Object from a byte stream and return a pointer to the
// populated ASF ContentInfo object.
//
// The current read position of the byte stream must be at the start of the
// ASF Header Object.
HRESULT CreateContentInfo(IMFByteStream *pStream,
IMFASFContentInfo **ppContentInfo)
{
const DWORD MIN_ASF_HEADER_SIZE = 30;
QWORD cbHeader = 0;
DWORD cbBuffer = 0;
IMFASFContentInfo *pContentInfo = NULL;
IMFMediaBuffer *pBuffer = NULL;
// Create the ASF ContentInfo object.
HRESULT hr = MFCreateASFContentInfo(&pContentInfo);
// Read the first 30 bytes to find the total header size.
if (SUCCEEDED(hr))
{
hr = MFCreateMemoryBuffer(MIN_ASF_HEADER_SIZE, &pBuffer);
}
if (SUCCEEDED(hr))
{
hr = ReadFromByteStream(pStream, pBuffer,MIN_ASF_HEADER_SIZE);
}
if (SUCCEEDED(hr))
{
hr = pContentInfo->GetHeaderSize(pBuffer, &cbHeader);
}
// Pass the first 30 bytes to the ContentInfo object.
if (SUCCEEDED(hr))
{
hr = pContentInfo->ParseHeader(pBuffer, 0);
}
SafeRelease(&pBuffer);
if (SUCCEEDED(hr))
{
cbBuffer = (DWORD)(cbHeader - MIN_ASF_HEADER_SIZE);
hr = MFCreateMemoryBuffer(cbBuffer, &pBuffer);
}
// Read the rest of the header and finish parsing the header.
if (SUCCEEDED(hr))
{
hr = ReadFromByteStream(pStream, pBuffer, cbBuffer);
}
if (SUCCEEDED(hr))
{
hr = pContentInfo->ParseHeader(pBuffer, MIN_ASF_HEADER_SIZE);
}
if (SUCCEEDED(hr))
{
// Return the pointer to the caller.
*ppContentInfo = pContentInfo;
(*ppContentInfo)->AddRef();
}
SafeRelease(&pBuffer);
SafeRelease(&pContentInfo);
return hr;
}
Ta funkcja używa funkcji ReadFromByteStream
do odczytu ze strumienia bajtów do buforu danych:
// Read data from a byte stream into a media buffer.
//
// This function reads a maximum of cbMax bytes, or up to the size size of the
// buffer, whichever is smaller. If the end of the byte stream is reached, the
// actual amount of data read might be less than either of these values.
//
// To find out how much data was read, call IMFMediaBuffer::GetCurrentLength.
HRESULT ReadFromByteStream(
IMFByteStream *pStream, // Pointer to the byte stream.
IMFMediaBuffer *pBuffer, // Pointer to the media buffer.
DWORD cbMax // Maximum amount to read.
)
{
DWORD cbBufferMax = 0;
DWORD cbRead = 0;
BYTE *pData= NULL;
HRESULT hr = pBuffer->Lock(&pData, &cbBufferMax, NULL);
// Do not exceed the maximum size of the buffer.
if (SUCCEEDED(hr))
{
if (cbMax > cbBufferMax)
{
cbMax = cbBufferMax;
}
// Read up to cbMax bytes.
hr = pStream->Read(pData, cbMax, &cbRead);
}
// Update the size of the valid data in the buffer.
if (SUCCEEDED(hr))
{
hr = pBuffer->SetCurrentLength(cbRead);
}
if (pData)
{
pBuffer->Unlock();
}
return hr;
}
4. Utwórz dzielnik ASF
Następnie utwórz obiekt rozdzielacz ASF. Użyjesz modułu podziału ASF, aby przeanalizować obiekt danych ASF, który zawiera spakowane dane multimedialne dla pliku ASF.
Aby utworzyć obiekt rozdzielacza dla pliku ASF:
- Wywołaj funkcję MFCreateASFSplitter, aby utworzyć rozdzielacz ASF. Funkcja zwraca wskaźnik do interfejsu IMFASFSplitter.
- Wywołaj IMFASFSplitter::Initialize, aby zainicjować rozdzielacz ASF. Ta metoda przyjmuje wskaźnik do obiektu ContentInfo, który został utworzony w procedurze 3.
// Create and initialize the ASF splitter.
HRESULT CreateASFSplitter (IMFASFContentInfo* pContentInfo,
IMFASFSplitter** ppSplitter)
{
IMFASFSplitter *pSplitter = NULL;
// Create the splitter object.
HRESULT hr = MFCreateASFSplitter(&pSplitter);
// Initialize the splitter to work with specific ASF data.
if (SUCCEEDED(hr))
{
hr = pSplitter->Initialize(pContentInfo);
}
if (SUCCEEDED(hr))
{
// Return the object to the caller.
*ppSplitter = pSplitter;
(*ppSplitter)->AddRef();
}
SafeRelease(&pSplitter);
return hr;
}
5. Wybierz strumień do analizowania
Następnie wylicz strumienie w pliku ASF i wybierz pierwszy strumień wideo na potrzeby analizowania. Aby wyliczyć strumienie, należy użyć obiektu profilu ASF i wyszukać strumienie, które mają typ mediów wideo.
Aby wybrać strumień wideo:
- Wywołaj IMFASFContentInfo::GetProfile w obiekcie ContentInfo, aby utworzyć profil ASF. Między innymi profil opisuje strumienie w pliku ASF.
- Wywołaj IMFASFProfile::GetStreamCount, aby uzyskać liczbę strumieni w pliku ASF.
- Wywołaj IMFASFProfile::GetStream w pętli, aby wyliczyć strumienie. Metoda zwraca wskaźnik do interfejsu IMFASFStreamConfig. Zwraca również identyfikator strumienia.
- Wywołaj IMFASFStreamConfig::GetStreamType, aby uzyskać identyfikator GUID typu głównego dla strumienia. Jeśli identyfikator GUID typu głównego jest MFMediaType_Video, strumień zawiera wideo.
- Jeśli znaleziono strumień wideo w kroku 4, wywołaj IMFASFSplitter::SelectStreams, aby wybrać strumień. Ta metoda przyjmuje tablicę identyfikatorów strumienia. W tym samouczku rozmiar tablicy wynosi 1, ponieważ aplikacja przeanalizuje pojedynczy strumień.
Poniższy przykładowy kod wylicza strumienie w pliku ASF i wybiera pierwszy strumień wideo w rozdzielaczy ASF:
// Select the first video stream for parsing with the ASF splitter.
HRESULT SelectVideoStream(IMFASFContentInfo *pContentInfo,
IMFASFSplitter *pSplitter, BOOL *pbHasVideo)
{
DWORD cStreams = 0;
WORD wStreamID = 0;
IMFASFProfile *pProfile = NULL;
IMFASFStreamConfig *pStream = NULL;
// Get the ASF profile from the ContentInfo object.
HRESULT hr = pContentInfo->GetProfile(&pProfile);
// Loop through all of the streams in the profile.
if (SUCCEEDED(hr))
{
hr = pProfile->GetStreamCount(&cStreams);
}
if (SUCCEEDED(hr))
{
for (DWORD i = 0; i < cStreams; i++)
{
GUID streamType = GUID_NULL;
// Get the stream type and stream identifier.
hr = pProfile->GetStream(i, &wStreamID, &pStream);
if (FAILED(hr))
{
break;
}
hr = pStream->GetStreamType(&streamType);
if (FAILED(hr))
{
break;
}
if (streamType == MFMediaType_Video)
{
*pbHasVideo = TRUE;
break;
}
SafeRelease(&pStream);
}
}
// Select the video stream, if found.
if (SUCCEEDED(hr))
{
if (*pbHasVideo)
{
// SelectStreams takes an array of stream identifiers.
hr = pSplitter->SelectStreams(&wStreamID, 1);
}
}
SafeRelease(&pStream);
SafeRelease(&pProfile);
return hr;
}
6. Generowanie skompresowanych przykładów multimediów
Następnie użyj splittera ASF, aby przeanalizować obiekt danych ASF i pobrać pakiety danych dla wybranego strumienia wideo. Aplikacja odczytuje dane z pliku ASF w blokach o stałym rozmiarze i przekazuje dane do rozdzielacza ASF. Rozdzielacz analizuje dane i generuje Media Samples, które zawierają skompresowane dane wideo. Aplikacja sprawdza, czy każda próbka reprezentuje klatkę kluczową. Jeśli tak, aplikacja wyświetla kilka podstawowych informacji o przykładzie:
- Liczba buforów multimedialnych
- Całkowity rozmiar danych
- Sygnatura czasowa
Aby wygenerować skompresowane przykłady multimediów:
- Przydziel nowy bufor multimedialny.
- Odczytaj dane ze strumienia bajtów do bufora multimediów.
- Przekaż bufor multimedialny do metody IMFASFSplitter::ParseData. Metoda analizuje dane ASF w buforze.
- W pętli pobierz próbki multimedialne z rozdzielacza, wywołując IMFASFSplitter::GetNextSample. Jeśli parametr ppISample odbiera prawidłowy wskaźnik IMFSample, oznacza to, że rozdzielacz ASF analizuje co najmniej jeden pakiet danych. Jeśli ppISample odbiera wartość null, przerwij z pętli i wróć do kroku 1.
- Wyświetl informacje o przykładzie.
- Przerwij pętlę w następujących warunkach:
- Parametr ppISample odbiera wartość null.
- Parametr pdwStatusFlags nie otrzymuje flagi ASF_STATUSFLAGS_INCOMPLETE.
Powtórz te kroki do momentu osiągnięcia końca pliku. Poniższy kod przedstawia następujące kroki:
// Parse the video stream and display information about the video samples.
//
// The current read position of the byte stream must be at the start of the ASF
// Data Object.
HRESULT DisplayKeyFrames(IMFByteStream *pStream, IMFASFSplitter *pSplitter)
{
const DWORD cbReadSize = 2048; // Read size (arbitrary value)
IMFMediaBuffer *pBuffer = NULL;
IMFSample *pSample = NULL;
HRESULT hr = S_OK;
while (SUCCEEDED(hr))
{
// The parser must get a newly allocated buffer each time.
hr = MFCreateMemoryBuffer(cbReadSize, &pBuffer);
if (FAILED(hr))
{
break;
}
// Read data into the buffer.
hr = ReadFromByteStream(pStream, pBuffer, cbReadSize);
if (FAILED(hr))
{
break;
}
// Get the amound of data that was read.
DWORD cbData;
hr = pBuffer->GetCurrentLength(&cbData);
if (FAILED(hr))
{
break;
}
if (cbData == 0)
{
break; // End of file.
}
// Send the data to the ASF splitter.
hr = pSplitter->ParseData(pBuffer, 0, 0);
SafeRelease(&pBuffer);
if (FAILED(hr))
{
break;
}
// Pull samples from the splitter.
DWORD parsingStatus = 0;
do
{
WORD streamID;
hr = pSplitter->GetNextSample(&parsingStatus, &streamID, &pSample);
if (FAILED(hr))
{
break;
}
if (pSample == NULL)
{
// No samples yet. Parse more data.
break;
}
if (IsRandomAccessPoint(pSample))
{
DisplayKeyFrame(pSample);
}
SafeRelease(&pSample);
} while (parsingStatus & ASF_STATUSFLAGS_INCOMPLETE);
}
SafeRelease(&pSample);
SafeRelease(&pBuffer);
return hr;
}
Funkcja IsKeyFrame sprawdza, czy próbka jest kluczową ramką, pobierając wartość atrybutu MFSampleExtension_CleanPoint.
inline BOOL IsRandomAccessPoint(IMFSample *pSample)
{
// Check for the "clean point" attribute. Default to FALSE.
return MFGetAttributeUINT32(pSample, MFSampleExtension_CleanPoint, FALSE);
}
Dla celów ilustracyjnych, w tym samouczku przedstawiono niektóre informacje dotyczące każdej klatki kluczowej wideo, wywołując następującą funkcję:
void DisplayKeyFrame(IMFSample *pSample)
{
DWORD cBuffers = 0; // Buffer count
DWORD cbTotalLength = 0; // Buffer length
MFTIME hnsTime = 0; // Time stamp
// Print various information about the key frame.
if (SUCCEEDED(pSample->GetBufferCount(&cBuffers)))
{
wprintf_s(L"Buffer count: %d\n", cBuffers);
}
if (SUCCEEDED(pSample->GetTotalLength(&cbTotalLength)))
{
wprintf_s(L"Length: %d bytes\n", cbTotalLength);
}
if (SUCCEEDED(pSample->GetSampleTime(&hnsTime)))
{
// Convert the time stamp to seconds.
double sec = static_cast<double>(hnsTime / 10000) / 1000;
wprintf_s(L"Time stamp: %f sec.\n", sec);
}
wprintf_s(L"\n");
}
Typowa aplikacja używa pakietów danych do dekodowania, remultipleksowania, wysyłania przez sieć lub innych zadań.
7. Napisz funkcję Entry-Point
Teraz możesz połączyć poprzednie kroki w pełną aplikację. Przed użyciem dowolnego z obiektów Programu Media Foundation zainicjuj platformę Media Foundation, wywołując MFStartup. Po zakończeniu wywołaj MFShutdown. Aby uzyskać więcej informacji, inicjowanie programu Media Foundation.
int wmain(int argc, WCHAR* argv[])
{
if (argc != 2)
{
_s(L"Usage: %s input.wmv");
return 0;
}
// Start the Media Foundation platform.
HRESULT hr = MFStartup(MF_VERSION);
if (SUCCEEDED(hr))
{
PCWSTR pszFileName = argv[1];
BOOL bHasVideo = FALSE;
IMFByteStream *pStream = NULL;
IMFASFContentInfo *pContentInfo = NULL;
IMFASFSplitter *pSplitter = NULL;
// Open the file.
hr = MFCreateFile(MF_ACCESSMODE_READ, MF_OPENMODE_FAIL_IF_NOT_EXIST,
MF_FILEFLAGS_NONE, pszFileName, &pStream);
// Read the ASF header.
if (SUCCEEDED(hr))
{
hr = CreateContentInfo(pStream, &pContentInfo);
}
// Create the ASF splitter.
if (SUCCEEDED(hr))
{
hr = CreateASFSplitter(pContentInfo, &pSplitter);
}
// Select the first video stream.
if (SUCCEEDED(hr))
{
hr = SelectVideoStream(pContentInfo, pSplitter, &bHasVideo);
}
// Parse the ASF file.
if (SUCCEEDED(hr))
{
if (bHasVideo)
{
hr = DisplayKeyFrames(pStream, pSplitter);
}
else
{
wprintf_s(L"No video stream.\n");
}
}
SafeRelease(&pSplitter);
SafeRelease(&pContentInfo);
SafeRelease(&pStream);
// Shut down the Media Foundation platform.
MFShutdown();
}
if (FAILED(hr))
{
wprintf_s(L"Error: 0x%X\n", hr);
}
return 0;
}
Lista programów
Poniższy kod przedstawia pełną listę samouczka.
#include <stdio.h> // Standard I/O
#include <windows.h> // Windows headers
#include <mfapi.h> // Media Foundation platform
#include <wmcontainer.h> // ASF interfaces
#include <Mferror.h>
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mf")
#pragma comment(lib, "mfuuid")
template <class T> void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
// Read data from a byte stream into a media buffer.
//
// This function reads a maximum of cbMax bytes, or up to the size size of the
// buffer, whichever is smaller. If the end of the byte stream is reached, the
// actual amount of data read might be less than either of these values.
//
// To find out how much data was read, call IMFMediaBuffer::GetCurrentLength.
HRESULT ReadFromByteStream(
IMFByteStream *pStream, // Pointer to the byte stream.
IMFMediaBuffer *pBuffer, // Pointer to the media buffer.
DWORD cbMax // Maximum amount to read.
)
{
DWORD cbBufferMax = 0;
DWORD cbRead = 0;
BYTE *pData= NULL;
HRESULT hr = pBuffer->Lock(&pData, &cbBufferMax, NULL);
// Do not exceed the maximum size of the buffer.
if (SUCCEEDED(hr))
{
if (cbMax > cbBufferMax)
{
cbMax = cbBufferMax;
}
// Read up to cbMax bytes.
hr = pStream->Read(pData, cbMax, &cbRead);
}
// Update the size of the valid data in the buffer.
if (SUCCEEDED(hr))
{
hr = pBuffer->SetCurrentLength(cbRead);
}
if (pData)
{
pBuffer->Unlock();
}
return hr;
}
// Read the ASF Header Object from a byte stream and return a pointer to the
// populated ASF ContentInfo object.
//
// The current read position of the byte stream must be at the start of the
// ASF Header Object.
HRESULT CreateContentInfo(IMFByteStream *pStream,
IMFASFContentInfo **ppContentInfo)
{
const DWORD MIN_ASF_HEADER_SIZE = 30;
QWORD cbHeader = 0;
DWORD cbBuffer = 0;
IMFASFContentInfo *pContentInfo = NULL;
IMFMediaBuffer *pBuffer = NULL;
// Create the ASF ContentInfo object.
HRESULT hr = MFCreateASFContentInfo(&pContentInfo);
// Read the first 30 bytes to find the total header size.
if (SUCCEEDED(hr))
{
hr = MFCreateMemoryBuffer(MIN_ASF_HEADER_SIZE, &pBuffer);
}
if (SUCCEEDED(hr))
{
hr = ReadFromByteStream(pStream, pBuffer,MIN_ASF_HEADER_SIZE);
}
if (SUCCEEDED(hr))
{
hr = pContentInfo->GetHeaderSize(pBuffer, &cbHeader);
}
// Pass the first 30 bytes to the ContentInfo object.
if (SUCCEEDED(hr))
{
hr = pContentInfo->ParseHeader(pBuffer, 0);
}
SafeRelease(&pBuffer);
if (SUCCEEDED(hr))
{
cbBuffer = (DWORD)(cbHeader - MIN_ASF_HEADER_SIZE);
hr = MFCreateMemoryBuffer(cbBuffer, &pBuffer);
}
// Read the rest of the header and finish parsing the header.
if (SUCCEEDED(hr))
{
hr = ReadFromByteStream(pStream, pBuffer, cbBuffer);
}
if (SUCCEEDED(hr))
{
hr = pContentInfo->ParseHeader(pBuffer, MIN_ASF_HEADER_SIZE);
}
if (SUCCEEDED(hr))
{
// Return the pointer to the caller.
*ppContentInfo = pContentInfo;
(*ppContentInfo)->AddRef();
}
SafeRelease(&pBuffer);
SafeRelease(&pContentInfo);
return hr;
}
// Create and initialize the ASF splitter.
HRESULT CreateASFSplitter (IMFASFContentInfo* pContentInfo,
IMFASFSplitter** ppSplitter)
{
IMFASFSplitter *pSplitter = NULL;
// Create the splitter object.
HRESULT hr = MFCreateASFSplitter(&pSplitter);
// Initialize the splitter to work with specific ASF data.
if (SUCCEEDED(hr))
{
hr = pSplitter->Initialize(pContentInfo);
}
if (SUCCEEDED(hr))
{
// Return the object to the caller.
*ppSplitter = pSplitter;
(*ppSplitter)->AddRef();
}
SafeRelease(&pSplitter);
return hr;
}
// Select the first video stream for parsing with the ASF splitter.
HRESULT SelectVideoStream(IMFASFContentInfo *pContentInfo,
IMFASFSplitter *pSplitter, BOOL *pbHasVideo)
{
DWORD cStreams = 0;
WORD wStreamID = 0;
IMFASFProfile *pProfile = NULL;
IMFASFStreamConfig *pStream = NULL;
// Get the ASF profile from the ContentInfo object.
HRESULT hr = pContentInfo->GetProfile(&pProfile);
// Loop through all of the streams in the profile.
if (SUCCEEDED(hr))
{
hr = pProfile->GetStreamCount(&cStreams);
}
if (SUCCEEDED(hr))
{
for (DWORD i = 0; i < cStreams; i++)
{
GUID streamType = GUID_NULL;
// Get the stream type and stream identifier.
hr = pProfile->GetStream(i, &wStreamID, &pStream);
if (FAILED(hr))
{
break;
}
hr = pStream->GetStreamType(&streamType);
if (FAILED(hr))
{
break;
}
if (streamType == MFMediaType_Video)
{
*pbHasVideo = TRUE;
break;
}
SafeRelease(&pStream);
}
}
// Select the video stream, if found.
if (SUCCEEDED(hr))
{
if (*pbHasVideo)
{
// SelectStreams takes an array of stream identifiers.
hr = pSplitter->SelectStreams(&wStreamID, 1);
}
}
SafeRelease(&pStream);
SafeRelease(&pProfile);
return hr;
}
inline BOOL IsRandomAccessPoint(IMFSample *pSample)
{
// Check for the "clean point" attribute. Default to FALSE.
return MFGetAttributeUINT32(pSample, MFSampleExtension_CleanPoint, FALSE);
}
void DisplayKeyFrame(IMFSample *pSample)
{
DWORD cBuffers = 0; // Buffer count
DWORD cbTotalLength = 0; // Buffer length
MFTIME hnsTime = 0; // Time stamp
// Print various information about the key frame.
if (SUCCEEDED(pSample->GetBufferCount(&cBuffers)))
{
wprintf_s(L"Buffer count: %d\n", cBuffers);
}
if (SUCCEEDED(pSample->GetTotalLength(&cbTotalLength)))
{
wprintf_s(L"Length: %d bytes\n", cbTotalLength);
}
if (SUCCEEDED(pSample->GetSampleTime(&hnsTime)))
{
// Convert the time stamp to seconds.
double sec = static_cast<double>(hnsTime / 10000) / 1000;
wprintf_s(L"Time stamp: %f sec.\n", sec);
}
wprintf_s(L"\n");
}
// Parse the video stream and display information about the video samples.
//
// The current read position of the byte stream must be at the start of the ASF
// Data Object.
HRESULT DisplayKeyFrames(IMFByteStream *pStream, IMFASFSplitter *pSplitter)
{
const DWORD cbReadSize = 2048; // Read size (arbitrary value)
IMFMediaBuffer *pBuffer = NULL;
IMFSample *pSample = NULL;
HRESULT hr = S_OK;
while (SUCCEEDED(hr))
{
// The parser must get a newly allocated buffer each time.
hr = MFCreateMemoryBuffer(cbReadSize, &pBuffer);
if (FAILED(hr))
{
break;
}
// Read data into the buffer.
hr = ReadFromByteStream(pStream, pBuffer, cbReadSize);
if (FAILED(hr))
{
break;
}
// Get the amound of data that was read.
DWORD cbData;
hr = pBuffer->GetCurrentLength(&cbData);
if (FAILED(hr))
{
break;
}
if (cbData == 0)
{
break; // End of file.
}
// Send the data to the ASF splitter.
hr = pSplitter->ParseData(pBuffer, 0, 0);
SafeRelease(&pBuffer);
if (FAILED(hr))
{
break;
}
// Pull samples from the splitter.
DWORD parsingStatus = 0;
do
{
WORD streamID;
hr = pSplitter->GetNextSample(&parsingStatus, &streamID, &pSample);
if (FAILED(hr))
{
break;
}
if (pSample == NULL)
{
// No samples yet. Parse more data.
break;
}
if (IsRandomAccessPoint(pSample))
{
DisplayKeyFrame(pSample);
}
SafeRelease(&pSample);
} while (parsingStatus & ASF_STATUSFLAGS_INCOMPLETE);
}
SafeRelease(&pSample);
SafeRelease(&pBuffer);
return hr;
}
int wmain(int argc, WCHAR* argv[])
{
if (argc != 2)
{
_s(L"Usage: %s input.wmv");
return 0;
}
// Start the Media Foundation platform.
HRESULT hr = MFStartup(MF_VERSION);
if (SUCCEEDED(hr))
{
PCWSTR pszFileName = argv[1];
BOOL bHasVideo = FALSE;
IMFByteStream *pStream = NULL;
IMFASFContentInfo *pContentInfo = NULL;
IMFASFSplitter *pSplitter = NULL;
// Open the file.
hr = MFCreateFile(MF_ACCESSMODE_READ, MF_OPENMODE_FAIL_IF_NOT_EXIST,
MF_FILEFLAGS_NONE, pszFileName, &pStream);
// Read the ASF header.
if (SUCCEEDED(hr))
{
hr = CreateContentInfo(pStream, &pContentInfo);
}
// Create the ASF splitter.
if (SUCCEEDED(hr))
{
hr = CreateASFSplitter(pContentInfo, &pSplitter);
}
// Select the first video stream.
if (SUCCEEDED(hr))
{
hr = SelectVideoStream(pContentInfo, pSplitter, &bHasVideo);
}
// Parse the ASF file.
if (SUCCEEDED(hr))
{
if (bHasVideo)
{
hr = DisplayKeyFrames(pStream, pSplitter);
}
else
{
wprintf_s(L"No video stream.\n");
}
}
SafeRelease(&pSplitter);
SafeRelease(&pContentInfo);
SafeRelease(&pStream);
// Shut down the Media Foundation platform.
MFShutdown();
}
if (FAILED(hr))
{
wprintf_s(L"Error: 0x%X\n", hr);
}
return 0;
}
Tematy pokrewne