チュートリアル: WMContainer オブジェクトを使用した ASF ファイルの読み取り
このチュートリアルでは、 ASF スプリッターを使用して Advanced Systems Format (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 メソッドに同じメディア バッファーを渡します。
- ヘッダー オブジェクトの残りの部分を新しいメディア バッファーに読み取ります。
- 2 番目のバッファーを 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 つのストリームを解析するため、配列サイズは 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 スプリッターが 1 つ以上のデータ パケットを解析したことを意味します。 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 を呼び出します。 詳細については、「 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;
}
プログラムの一覧
次のコードは、チュートリアルの完全な一覧を示しています。
#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;
}
関連トピック