Использование средства чтения источника для обработки данных мультимедиа
В этом разделе описывается, как использовать средство чтения источника для обработки данных мультимедиа.
Чтобы использовать средство чтения источника, выполните следующие основные действия.
- Создайте экземпляр средства чтения источника.
- Перечислите возможные форматы выходных данных.
- Задайте фактический формат выходных данных для каждого потока.
- Обработка данных из источника.
Оставшаяся часть этого раздела подробно описывает эти действия.
- Создание инструмента чтения источника
- Перечисление форматов вывода
- настройка форматов выходных данных
- обработка данных мультимедиа
- очистка конвейера данных
- Получение длительности файла
- в поиске
- скорость воспроизведения
- аппаратное ускорение
- связанные темы
Создание средства чтения источника
Чтобы создать экземпляр средства чтения источника, вызовите одну из следующих функций:
Функция | Описание |
---|---|
MFCreateSourceReaderFromURL |
Принимает URL-адрес в качестве входных данных. Эта функция использует разрешение источника для создания мультимедийного источника из URL-адреса. |
MFCreateSourceReaderFromByteStream |
Принимает указатель на поток байтов. Эта функция также использует резольвер источника для создания источника мультимедиа. |
MFCreateSourceReaderFromMediaSource |
Принимает указатель на источник мультимедиа, который уже создан. Эта функция полезна для источников мультимедиа, которые не может создать сопоставитель источника, такие устройства записи или пользовательские источники мультимедиа. |
Как правило, для файлов мультимедиа используйте MFCreateSourceReaderFromURL. Для устройств, таких как веб-камеры, используйте MFCreateSourceReaderFromMediaSource. (Дополнительные сведения об устройствах захвата в Microsoft Media Foundation см. в разделе Аудио/видеозахват.)
Каждая из этих функций принимает необязательный IMFAttributes указатель, который используется для задания различных параметров для читателя источника, как описано в справочных разделах для этих функций. Чтобы получить поведение по умолчанию, задайте для этого параметра значение NULL. Каждая функция возвращает указатель IMFSourceReader в качестве выходного параметра. Перед вызовом любой из этих функций необходимо вызвать функции CoInitialize(Ex) и MFStartup.
Следующий код создает средство чтения источника из URL-адреса.
int __cdecl wmain(int argc, __in_ecount(argc) PCWSTR* argv)
{
if (argc < 2)
{
return 1;
}
const WCHAR *pszURL = argv[1];
// Initialize the COM runtime.
HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
if (SUCCEEDED(hr))
{
// Initialize the Media Foundation platform.
hr = MFStartup(MF_VERSION);
if (SUCCEEDED(hr))
{
// Create the source reader.
IMFSourceReader *pReader;
hr = MFCreateSourceReaderFromURL(pszURL, NULL, &pReader);
if (SUCCEEDED(hr))
{
ReadMediaFile(pReader);
pReader->Release();
}
// Shut down Media Foundation.
MFShutdown();
}
CoUninitialize();
}
}
Перечисление форматов выходных данных
Каждый источник мультимедиа имеет по крайней мере один поток. Например, видеофайл может содержать видеопоток и аудиопоток. Формат каждого потока описывается с помощью типа мультимедиа, представленного интерфейсом IMFMediaType. Дополнительные сведения о типах носителей смотрите в Типы носителей. Необходимо проверить тип носителя, чтобы понять формат данных, полученных из средства чтения источника.
Изначально каждый поток имеет формат по умолчанию, который можно найти, вызвав метод IMFSourceReader::GetCurrentMediaType:
Для каждого потока источник мультимедиа предлагает список возможных типов носителей для этого потока. Количество типов зависит от источника. Если источник представляет файл мультимедиа, обычно существует только один тип для каждого потока. С другой стороны, веб-камера может иметь возможность потоковой передачи видео в нескольких разных форматах. В этом случае приложение может выбрать формат, используемый в списке типов носителей.
Чтобы получить один из типов мультимедиа для потока, вызовите метод IMFSourceReader::GetNativeMediaType. Этот метод принимает два параметра индекса: индекс потока и индекс для списка типов носителей для потока. Чтобы перечислить все типы для потока, увеличьте индекс списка при сохранении константы индекса потока. Если индекс списка выходит за пределы, GetNativeMediaType возвращает MF_E_NO_MORE_TYPES.
HRESULT EnumerateTypesForStream(IMFSourceReader *pReader, DWORD dwStreamIndex)
{
HRESULT hr = S_OK;
DWORD dwMediaTypeIndex = 0;
while (SUCCEEDED(hr))
{
IMFMediaType *pType = NULL;
hr = pReader->GetNativeMediaType(dwStreamIndex, dwMediaTypeIndex, &pType);
if (hr == MF_E_NO_MORE_TYPES)
{
hr = S_OK;
break;
}
else if (SUCCEEDED(hr))
{
// Examine the media type. (Not shown.)
pType->Release();
}
++dwMediaTypeIndex;
}
return hr;
}
Чтобы перечислить типы носителей для каждого потока, увеличьте индекс потока. Когда индекс потока выходит за пределы, GetNativeMediaType возвращает MF_E_INVALIDSTREAMNUMBER.
HRESULT EnumerateMediaTypes(IMFSourceReader *pReader)
{
HRESULT hr = S_OK;
DWORD dwStreamIndex = 0;
while (SUCCEEDED(hr))
{
hr = EnumerateTypesForStream(pReader, dwStreamIndex);
if (hr == MF_E_INVALIDSTREAMNUMBER)
{
hr = S_OK;
break;
}
++dwStreamIndex;
}
return hr;
}
Настройка форматов выходных данных
Чтобы изменить формат выходных данных, вызовите метод IMFSourceReader::SetCurrentMediaType. Этот метод принимает индекс потока и тип носителя:
hr = pReader->SetCurrentMediaType(dwStreamIndex, pMediaType);
Тип носителя зависит от того, нужно ли вставить декодатор.
- Чтобы получить данные непосредственно из источника, не декодируя его, используйте один из типов, возвращаемых GetNativeMediaType.
- Чтобы декодировать поток, создайте новый тип носителя, описывающий нужный формат без сжатия.
В случае декодера создайте тип носителя следующим образом:
- Вызовите MFCreateMediaType для создания нового типа носителя.
- Задайте атрибут MF_MT_MAJOR_TYPE, чтобы указать звук или видео.
- Задайте атрибут MF_MT_SUBTYPE, чтобы указать подтип декодирования формата. (См. идентификаторы GUID звукового подтипа и идентификаторы GUID видео .)
- Вызов IMFSourceReader::SetCurrentMediaType.
Средство чтения источника автоматически загружает декодатор. Чтобы получить полные сведения о декодируемом формате, вызовите IMFSourceReader::GetCurrentMediaType после вызова SetCurrentMediaType
Следующий код настраивает видеопоток для RGB-32 и аудиопоток для звука PCM.
HRESULT ConfigureDecoder(IMFSourceReader *pReader, DWORD dwStreamIndex)
{
IMFMediaType *pNativeType = NULL;
IMFMediaType *pType = NULL;
// Find the native format of the stream.
HRESULT hr = pReader->GetNativeMediaType(dwStreamIndex, 0, &pNativeType);
if (FAILED(hr))
{
return hr;
}
GUID majorType, subtype;
// Find the major type.
hr = pNativeType->GetGUID(MF_MT_MAJOR_TYPE, &majorType);
if (FAILED(hr))
{
goto done;
}
// Define the output type.
hr = MFCreateMediaType(&pType);
if (FAILED(hr))
{
goto done;
}
hr = pType->SetGUID(MF_MT_MAJOR_TYPE, majorType);
if (FAILED(hr))
{
goto done;
}
// Select a subtype.
if (majorType == MFMediaType_Video)
{
subtype= MFVideoFormat_RGB32;
}
else if (majorType == MFMediaType_Audio)
{
subtype = MFAudioFormat_PCM;
}
else
{
// Unrecognized type. Skip.
goto done;
}
hr = pType->SetGUID(MF_MT_SUBTYPE, subtype);
if (FAILED(hr))
{
goto done;
}
// Set the uncompressed format.
hr = pReader->SetCurrentMediaType(dwStreamIndex, NULL, pType);
if (FAILED(hr))
{
goto done;
}
done:
SafeRelease(&pNativeType);
SafeRelease(&pType);
return hr;
}
Обработка данных мультимедиа
Чтобы получить данные мультимедиа из источника, вызовите метод IMFSourceReader::ReadSample, как показано в следующем коде.
DWORD streamIndex, flags;
LONGLONG llTimeStamp;
hr = pReader->ReadSample(
MF_SOURCE_READER_ANY_STREAM, // Stream index.
0, // Flags.
&streamIndex, // Receives the actual stream index.
&flags, // Receives status flags.
&llTimeStamp, // Receives the time stamp.
&pSample // Receives the sample or NULL.
);
Первым параметром является индекс потока, для которого требуется получить данные. Вы также можете указать MF_SOURCE_READER_ANY_STREAM, чтобы получить следующие доступные данные из любого потока. Второй параметр содержит необязательные флаги; см. MF_SOURCE_READER_CONTROL_FLAG список этих элементов. Третий параметр получает индекс потока, который фактически создает данные. Эти сведения потребуются, если для первого параметра задано значение MF_SOURCE_READER_ANY_STREAM. Четвертый параметр получает флаги состояния, указывающие на различные события, которые могут возникать при чтении данных, например изменения формата в потоке. Список флагов состояния см. в MF_SOURCE_READER_FLAG.
Если источник мультимедиа может создавать данные для запрошенного потока, последний параметр ReadSample получает указатель на интерфейс IMFSample объекта медиапримера. Используйте образец медиа для:
- Получите указатель на мультимедийные данные.
- Получите время презентации и длительность выборки.
- Получение атрибутов, описывающих взаимодействие, доминирование полей и другие аспекты примера.
Содержимое данных мультимедиа зависит от формата потока. Для несжатого видеопотока каждый образец мультимедиа содержит один видеокадр. Для несжатого аудиопотока каждый образец мультимедиа содержит последовательность аудиокадров.
Метод ReadSample может возвращать S_OK, но не возвращать образец носителя в параметре pSample. Например, когда вы достигнете конца файла, ReadSample задает флаг MF_SOURCE_READERF_ENDOFSTREAM в dwFlags и задает pSample значение NULL. В этом случае метод ReadSample возвращает S_OK, так как ошибка не произошла, даже если параметр pSample имеет значение NULL. Таким образом, всегда проверяйте значение переменной pSample перед разыменованием.
В следующем коде показано, как вызвать ReadSample в цикле и проверить сведения, возвращаемые методом, до конца файла мультимедиа.
HRESULT ProcessSamples(IMFSourceReader *pReader)
{
HRESULT hr = S_OK;
IMFSample *pSample = NULL;
size_t cSamples = 0;
bool quit = false;
while (!quit)
{
DWORD streamIndex, flags;
LONGLONG llTimeStamp;
hr = pReader->ReadSample(
MF_SOURCE_READER_ANY_STREAM, // Stream index.
0, // Flags.
&streamIndex, // Receives the actual stream index.
&flags, // Receives status flags.
&llTimeStamp, // Receives the time stamp.
&pSample // Receives the sample or NULL.
);
if (FAILED(hr))
{
break;
}
wprintf(L"Stream %d (%I64d)\n", streamIndex, llTimeStamp);
if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
{
wprintf(L"\tEnd of stream\n");
quit = true;
}
if (flags & MF_SOURCE_READERF_NEWSTREAM)
{
wprintf(L"\tNew stream\n");
}
if (flags & MF_SOURCE_READERF_NATIVEMEDIATYPECHANGED)
{
wprintf(L"\tNative type changed\n");
}
if (flags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)
{
wprintf(L"\tCurrent type changed\n");
}
if (flags & MF_SOURCE_READERF_STREAMTICK)
{
wprintf(L"\tStream tick\n");
}
if (flags & MF_SOURCE_READERF_NATIVEMEDIATYPECHANGED)
{
// The format changed. Reconfigure the decoder.
hr = ConfigureDecoder(pReader, streamIndex);
if (FAILED(hr))
{
break;
}
}
if (pSample)
{
++cSamples;
}
SafeRelease(&pSample);
}
if (FAILED(hr))
{
wprintf(L"ProcessSamples FAILED, hr = 0x%x\n", hr);
}
else
{
wprintf(L"Processed %d samples\n", cSamples);
}
SafeRelease(&pSample);
return hr;
}
Очистка конвейера данных
Во время обработки данных декодатор или другое преобразование может буферировать входные образцы. На следующей схеме приложение вызывает ReadSample и получает пример с временем презентации, равным t1. Декодатор содержит примеры для t2 и t3.
При следующем вызове ReadSampleсредство чтения источника может предоставить t4 декодировщику и вернуть t2 приложению.
Если вы хотите декодировать все примеры, которые в данный момент буферифированы в декодировщике, не передавая новые примеры декодировщику, задайте флаг MF_SOURCE_READER_CONTROLF_DRAIN в параметре dwControlFlags параметра ReadSample. Продолжайте выполнять это в цикле, пока ReadSample не возвращает NULL указатель выборки. В зависимости от того, как декодер буферизует образцы, это может происходить либо сразу, либо после нескольких вызовов ReadSample.
Получение длительности файла
Чтобы получить длительность файла мультимедиа, вызовите метод IMFSourceReader::GetPresentationAttribute и запросите атрибут MF_PD_DURATION, как показано в следующем коде.
HRESULT GetDuration(IMFSourceReader *pReader, LONGLONG *phnsDuration)
{
PROPVARIANT var;
HRESULT hr = pReader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE,
MF_PD_DURATION, &var);
if (SUCCEEDED(hr))
{
hr = PropVariantToInt64(var, phnsDuration);
PropVariantClear(&var);
}
return hr;
}
Показанная здесь функция получает длительность в 100-наносекундах единиц. Разделите на 10 000 000, чтобы получить длительность в секундах.
Ищущий
Источник мультимедиа, получающий данные из локального файла, обычно может стремиться к произвольным позициям в файле. Устройства записи, такие как веб-камеры, как правило, не могут искать, так как данные живут. Источник, который передает данные по сети, может поддерживать поиск в зависимости от используемого протокола потоковой передачи.
Чтобы узнать, поддерживает ли источник мультимедиа поиск, вызовите IMFSourceReader::GetPresentationAttribute и запросите атрибут MF_SOURCE_READER_MEDIASOURCE_CHARACTERISTICS, как показано в следующем коде:
HRESULT GetSourceFlags(IMFSourceReader *pReader, ULONG *pulFlags)
{
ULONG flags = 0;
PROPVARIANT var;
PropVariantInit(&var);
HRESULT hr = pReader->GetPresentationAttribute(
MF_SOURCE_READER_MEDIASOURCE,
MF_SOURCE_READER_MEDIASOURCE_CHARACTERISTICS,
&var);
if (SUCCEEDED(hr))
{
hr = PropVariantToUInt32(var, &flags);
}
if (SUCCEEDED(hr))
{
*pulFlags = flags;
}
PropVariantClear(&var);
return hr;
}
Эта функция получает набор флагов возможностей из источника. Эти флаги определены в перечислении MFMEDIASOURCE_CHARACTERISTICS. Два флага связаны с поиском:
Флаг | Описание |
---|---|
MFMEDIASOURCE_CAN_SEEK |
Источник может искать. |
MFMEDIASOURCE_HAS_SLOW_SEEK |
Поиск может занять много времени. Например, источнику может потребоваться скачать весь файл, прежде чем он сможет искать. Для того, чтобы источник возвращал этот флаг, нет строгих критериев. |
Следующий код проверяет флаг MFMEDIASOURCE_CAN_SEEK.
BOOL SourceCanSeek(IMFSourceReader *pReader)
{
BOOL bCanSeek = FALSE;
ULONG flags;
if (SUCCEEDED(GetSourceFlags(pReader, &flags)))
{
bCanSeek = ((flags & MFMEDIASOURCE_CAN_SEEK) == MFMEDIASOURCE_CAN_SEEK);
}
return bCanSeek;
}
Чтобы искать, вызовите метод IMFSourceReader::SetCurrentPosition, как показано в следующем коде.
HRESULT SetPosition(IMFSourceReader *pReader, const LONGLONG& hnsPosition)
{
PROPVARIANT var;
HRESULT hr = InitPropVariantFromInt64(hnsPosition, &var);
if (SUCCEEDED(hr))
{
hr = pReader->SetCurrentPosition(GUID_NULL, var);
PropVariantClear(&var);
}
return hr;
}
Первый параметр предоставляет формат времени, который используется для указания позиции поиска. В Media Foundation все источники мультимедиа должны поддерживать единицы измерения в 100 наносекунд, что указывается значением GUID_NULL. Второй параметр — это PROPVARIANT, содержащий позицию поиска. Для 100-наносекундных единиц времени тип данных LONGLONG.
Помните, что не каждый медиаисточник обеспечивает поиск по кадрам с точностью. Точность поиска зависит от нескольких факторов, таких как интервал ключевого кадра, независимо от того, содержит ли файл мультимедиа индекс и имеет ли данные константную или переменную скорость битов. Таким образом, после того, как вы ищете позицию в файле, нет гарантии, что метка времени на следующем примере точно соответствует запрошенной позиции. Как правило, фактическое положение не будет позже запрашиваемого, поэтому вы можете отбросить выборки, пока не достигнете желаемой точки в потоке.
Скорость воспроизведения
Хотя скорость воспроизведения можно задать с помощью средства чтения источника, обычно это не очень полезно, по следующим причинам:
- Источник данных не поддерживает обратное воспроизведение, даже если источник мультимедиа это позволяет.
- Приложение управляет временем отображения, поэтому оно может реализовать быстрое или медленное воспроизведение без изменения скорости на источнике.
- Некоторые источники мультимедиа поддерживают режим прореживания , при котором источник предоставляет меньше образцов — обычно только ключевые кадры. Однако если вы хотите удалить неключевые кадры, можно проверить каждый образец для атрибута MFSampleExtension_CleanPoint.
Чтобы задать частоту воспроизведения с помощью средства чтения источника, вызовите метод IMFSourceReader::GetServiceForStream, чтобы получить интерфейсы IMFRateSupport и IMFRateControl из источника мультимедиа.
Аппаратное ускорение
Модуль чтения источников совместим с Microsoft DirectX Video Acceleration (DXVA) 2.0 для декодирования видео с аппаратным ускорением. Чтобы использовать DXVA с средством чтения источника, выполните следующие действия.
- Создайте устройство Microsoft Direct3D.
- Вызовите функцию DXVA2CreateDirect3DDeviceManager9, чтобы создать диспетчер устройств Direct3D. Эта функция получает указатель на интерфейс IDirect3DDeviceManager9.
- Вызовите метод IDirect3DDeviceManager9::ResetDevice с указателем на устройство Direct3D.
- Создайте хранилище атрибутов, вызвав функциюMFCreateAttributes.
- Создайте средство чтения источника. Передайте хранилище атрибутов в pAttributes параметр функции создания.
При предоставлении устройства Direct3D средство чтения источника выделяет примеры видео, совместимые с API видеопроцессоров DXVA. Вы можете использовать обработку видео DXVA для выполнения аппаратного деинтерлакирования или смешивания видео. Дополнительные сведения см. в разделе обработки видео DXVA. Кроме того, если декодатор поддерживает DXVA 2.0, он будет использовать устройство Direct3D для выполнения аппаратного ускорения декодирования.
Важный
Начиная с Windows 8, IMFDXGIDeviceManager можно использовать вместо IDirect3DeviceManager9. Для приложений Магазина Windows необходимо использовать IMFDXGIDeviceManager. Дополнительные сведения см. в API Видео Direct3D 11.
Связанные разделы