教學課程:使用 WMContainer 物件讀取 ASF 檔案
本教學課程說明如何使用 ASF 分割器,從進階系統格式 (ASF) 檔案取得資料封包。 在本教學課程中,您將建立簡單的主控台應用程式,以讀取 ASF 檔案,並針對檔案中的第一個視訊串流產生壓縮媒體範例。 應用程式會顯示影片資料流程中主要畫面格的相關資訊。
本教學課程包含下列步驟:
- 先決條件
- 1.設定專案
- 2.開啟 ASF 檔案
- 3.讀取 ASF 標頭物件
- 4.建立 ASF 分隔器
- 5.選取用於剖析的資料流程
- 6.產生壓縮媒體範例
- 7.撰寫Entry-Point函式
- 程式清單
- 相關主題
本教學課程未涵蓋如何解碼應用程式從 ASF 分割器取得的壓縮資料。
必要條件
本教學課程假設您已句備下列條件:
- 您已熟悉 ASF 檔案的結構,以及 Media Foundation 所提供的元件,以使用 ASF 物件。 這些元件包括 ContentInfo 物件、分割器、多工器和設定檔。 如需詳細資訊,請參閱 WMContainer ASF 元件。
- 您已熟悉 媒體緩衝區 和位元組資料流程:具體而言,使用位元組資料流程的檔案作業、從位元組資料流程讀取到媒體緩衝區,以及將媒體緩衝區的內容寫入位元組資料流程。
1.設定專案
在您的原始程式檔中包含下列標頭:
#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>
連結至下列程式庫檔案:
- mfplat.lib
- mf.lib
- mfuuid.lib
宣告 SafeRelease 函式:
template <class T> void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
2.開啟 ASF 檔案
接下來,呼叫 MFCreateFile 函式以開啟指定的檔案。 方法會傳回包含檔案內容的位元組資料流程物件的指標。 檔案名是由使用者透過應用程式的命令列引數所指定。
下列範例程式碼會採用檔案名,並傳回可用來讀取檔案的位元組資料流程物件的指標。
// Open the file.
hr = MFCreateFile(MF_ACCESSMODE_READ, MF_OPENMODE_FAIL_IF_NOT_EXIST,
MF_FILEFLAGS_NONE, pszFileName, &pStream);
3.讀取 ASF 標頭物件
接下來,建立 ASF ContentInfo 物件 ,並用它來剖析指定檔案的 ASF 標頭物件。 ContentInfo 物件會儲存 ASF 標頭中的資訊,包括全域檔案屬性和每個資料流程的相關資訊。 您稍後會在教學課程中使用 ContentInfo 物件來初始化 ASF 分隔器,並取得視訊資料流程的資料流程編號。
若要建立 ASF ContentInfo 物件:
- 呼叫 MFCreateASFContentInfo 函式來建立 ContentInfo 物件。 方法會傳回 IMFASFContentInfo 介面的指標。
- 將 ASF 檔案的前 30 個位元組資料讀入媒體緩衝區。
- 將媒體緩衝區傳遞至 IMFASFContentInfo::GetHeaderSize 方法。 這個方法會傳回 ASF 檔案中 Header 物件的總大小。
- 將相同的媒體緩衝區傳遞至 IMFASFContentInfo::P arseHeader 方法。
- 將 Header 物件的其餘部分讀入新的媒體緩衝區。
- 將第二個緩衝區傳遞至 ParseHeader 方法。 在ParseHeader的cbOffsetWithinHeader參數中指定 30 位元組位移。 ParseHeader方法會使用從 Header 物件中包含的各種 ASF 物件收集到的資訊,初始化 ContentInfo 物件。
// 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;
}
此函式會使用 函 ReadFromByteStream
式從位元組資料流程讀取到媒體緩衝區:
// 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.建立 ASF 分隔器
接下來,建立 ASF Splitter 物件。 您將使用 ASF 分隔器來剖析 ASF 資料物件,其中包含 ASF 檔案的封包化媒體資料。
若要建立 ASF 檔案的分割器物件:
- 呼叫 MFCreateASFSplitter 函 式來建立 ASF 分隔器。 函式會傳回 IMFASFSplitter 介面的指標。
- 呼叫 IMFASFSplitter::Initialize 以初始化 ASF 分隔器。 這個方法會採用在程式 3 中建立之 ContentInfo 物件的指標。
// 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.選取用於剖析的資料流程
接下來,列舉 ASF 檔案中的資料流程,然後選取要剖析的第一個視訊資料流程。 若要列舉資料流程,您將使用 ASF 設定檔物件,並搜尋具有視訊媒體類型的串流。
若要選取影片串流:
- 在 ContentInfo 物件上呼叫 IMFASFContentInfo::GetProfile 以建立 ASF 設定檔。 除了其他資訊之外,設定檔會描述 ASF 檔案中的資料流程。
- 呼叫 IMFASFProfile::GetStreamCount 以取得 ASF 檔案中的資料流程數目。
- 在迴圈中呼叫 IMFASFProfile::GetStream 以列舉資料流程。 方法會傳回 IMFASFStreamConfig 介面的指標。 它也會傳回資料流程識別碼。
- 呼叫 IMFASFStreamConfig::GetStreamType 以取得資料流程的主要類型 GUID。 如果主要類型 GUID MFMediaType_Video,資料流程會包含影片。
- 如果您在步驟 4 中找到視訊串流,請呼叫 IMFASFSplitter::SelectStreams 以選取串流。 這個方法會採用資料流程識別碼的陣列。 在本教學課程中,陣列大小為 1,因為應用程式會剖析單一資料流程。
下列範例程式碼會列舉 ASF 檔案中的資料流程,並選取 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.產生壓縮媒體範例
接下來,使用 ASF 分隔器來剖析 ASF 資料物件,並取得所選視訊資料流程的資料封包。 應用程式會以固定大小的區塊從 ASF 檔案讀取資料,並將資料傳遞至 ASF 分隔器。 分割器會剖析資料,並產生包含壓縮視訊資料的 媒體範例 。 應用程式會檢查每個範例是否代表主要畫面格。 如果是,應用程式會顯示範例的一些基本資訊:
- 媒體緩衝區數目
- 資料的總大小
- 時間戳記
若要產生壓縮的媒體範例:
- 配置新的媒體緩衝區。
- 從位元組資料流程讀取資料到媒體緩衝區。
- 將媒體緩衝區傳遞至 IMFASFSplitter::P arseData 方法。 方法會剖析緩衝區中的 ASF 資料。
- 在迴圈中,呼叫 IMFASFSplitter::GetNextSample,從分割器取得媒體範例。 如果 ppISample 參數收到有效的 IMFSample 指標,表示 ASF 分隔器已剖析一或多個資料封包。 如果 ppISample 收到 Null值,請中斷迴圈並返回步驟 1。
- 顯示範例的相關資訊。
- 在下列情況下從 迴圈中斷:
- ppISample參數會接收Null值。
- pdwStatusFlags參數不會收到ASF_STATUSFLAGS_INCOMPLETE旗標。
重複這些步驟,直到您到達檔案的結尾為止。 下列程式碼顯示這些步驟:
// 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;
}
IsKeyFrame 函式會藉由取得 MFSampleExtension_CleanPoint 屬性的值,來測試範例是否為主要畫面格。
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");
}
一般應用程式會使用資料封包進行解碼、重新使用、透過網路傳送或其他工作。
7.撰寫Entry-Point函式
現在您可以將先前的步驟放在完整的應用程式中。 使用任何 Media Foundation 物件之前,請先呼叫 MFStartup來初始化 Media Foundation 平臺。 完成後,請呼叫 MFShutdown。 如需詳細資訊, 請初始化媒體基礎。
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;
}
程式清單
下列程式碼顯示教學課程的完整清單。
#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;
}
相關主題