Руководство. Чтение ASF-файла с помощью объектов WMContainer
В этом руководстве показано, как получить пакеты данных из файла ASF с помощью разделитировщика ASF. В этом руководстве вы создадите простое консольное приложение, которое считывает ASF-файл и создает сжатые примеры мультимедиа для первого видеопотока в файле. Приложение отображает сведения о ключевых кадрах в видеопотоке.
Этот учебник содержит следующие действия.
- Предварительные условия
- 1. Настройка проекта
- 2. Открытие ФАЙЛА ASF
- 3. Чтение объекта заголовка ASF
- 4. Создайте разделитель ASF
- 5. Выбор потока для синтаксического анализа
- 6. Создание примеров сжатых носителей
- 7. Запись функции Entry-Point
- Список программ
- Связанные темы
В этом руководстве не описывается, как декодировать сжатые данные, которые приложение получает из разделительного метода ASF.
Предварительные требования
В этом учебнике предполагается следующее:
- Вы знакомы со структурой ФАЙЛА ASF и компонентами, предоставляемыми Media Foundation для работы с объектами ASF. К этим компонентам относятся объект ContentInfo, разделитель, мультиплексор и профиль. Дополнительные сведения см. в разделе Компоненты ASF WMContainer.
- Вы знакомы с буферами мультимедиа и потоками байтов. В частности, операции с файлами с использованием потока байтов, чтение из потока байтов в буфер мультимедиа и запись содержимого буфера мультимедиа в байтовый поток.
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 .
- Считывает первые 30 байт данных из ASF-файла в буфер мультимедиа.
- Передайте буфер мультимедиа в метод IMFASFContentInfo::GetHeaderSize . Этот метод возвращает общий размер объекта заголовка в ASF-файле.
- Передайте тот же буфер мультимедиа методу IMFASFContentInfo::P arseHeader .
- Считывает оставшуюся часть объекта заголовка в новый буфер мультимедиа.
- Передайте второй буфер методу ParseHeader . Укажите 30-байтовую смещение в параметре cbOffsetWithinHeaderобъекта ParseHeader. Метод ParseHeader инициализирует объект ContentInfo сведениями, собранными из различных объектов ASF, содержащихся в объекте header.
// 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 и выполняется поиск потоков с типом видео media.
Чтобы выбрать видеопоток, выполните следующие действия:
- Вызовите IMFASFContentInfo::GetProfile в объекте ContentInfo, чтобы создать профиль 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, инициализируйте платформу Media Foundation, вызвав MFStartup. Когда все будет готово, вызовите 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;
}
Связанные темы