教程:使用 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 文件中标头对象的总大小。
- 将同一媒体缓冲区传递给 IMFASFContentInfo::P arseHeader 方法。
- 将标头对象的其余部分读入新的媒体缓冲区。
- 将第二个缓冲区传递给 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 拆分器 对象。 你将使用 ASF 拆分器来分析 ASF 数据对象,该对象包含 ASF 文件的数据包化媒体数据。
若要为 ASF 文件创建拆分器对象,请执行以下操作:
- 调用 MFCreateASFSplitter 函数以创建 ASF 拆分器。 函数返回指向 IMFASFSplitter 接口的 指针。
- 调用 IMFASFSplitter::Initialize 以初始化 ASF 拆分器。 此方法采用指向 ContentInfo 对象的指针,该对象是在过程 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. 选择要分析的流
接下来,枚举 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;
}
相关主题