音频和视频质量
Media Foundation 旨在应对高清内容带来的挑战。 在整个平台中改进的音频和视频质量,现在可以为下一代高清内容提供出色的体验。
-
与 DXVA 1.0 相比,DirectX 视频加速 (DXVA) 2.0 提供更高效的视频加速,视频解码更可靠、更精简,在视频处理中扩展了硬件的使用。 使用 DXVA 2.0,Windows 可以处理一些要求最苛刻的高清内容,并提供高质量和改进的故障复原能力。
-
在整个视频管道中保留颜色空间信息。 用户可以以完全保真度欣赏视频内容。 颜色信息和交错图像现在传递到硬件进行单通道合成。 保留颜色空间信息还可以减少不必要的颜色空间转换,从而释放更多周期来处理要求苛刻的 HD 内容。
-
增强的视频呈现器 (EVR) 提供更好的计时支持、增强的视频处理和改进的故障复原能力。 改进了全屏播放支持,并且窗口模式下的视频撕裂已最小化。
-
Media Foundation 使用多媒体类计划程序服务 (MMCSS) ,这是 Windows Vista 中的一项新系统服务。 MMCSS 使多媒体应用程序能够确保其时间敏感型处理能够优先访问 CPU 资源。
Media Foundation:基本概念
壹、流 Streams
具有统一类型的媒体数据序,流几乎可以包含任何类型的数据,包括音频、视频、文本、脚本命令和静止图像,
一个媒体文件包含一个音频流,或者正好包含一个视频流和一个音频流。 但是,媒体文件可能包含多个相同类型的流(视频文件可能包含几种不同语言的音频流)
贰、压缩 Compression
压缩 是指通过删除冗余信息来减小数据流大小的任何过程。 压缩算法分为两大类:
-
无损 压缩。 使用无损算法,重新构造的数据与原始数据相同。
-
有损 压缩。 使用有损算法,重新构造的数据是原始数据的近似值,但不是完全匹配。
叁、媒体容器 Media Containers
媒体文件通常至少包含以下一些元素:
-
描述流数、每个流的格式等的文件标头。
-
启用对内容的随机访问的索引。
-
描述内容 (的元数据,例如艺术家或作品) 。
-
数据包标头,用于启用网络传输或随机访问。
媒体容器的典型结构
肆、格式 Formats
在数字媒体中,术语 格式 不明确。 格式可以引用 编码类型(如 H.264 视频)或 容器(如 MP4)。 对于普通用户来说,这种区别通常会让人感到困惑。 为媒体格式提供的名称并不总是有帮助的。 例如, MP3 引用编码格式 (MPEG-1 音频第 3 层) 和文件格式。
但是,这一区别很重要,因为读取媒体文件实际上涉及两个阶段:
-
首先,必须分析容器。 在大多数情况下,在此步骤完成之前,无法知道流的数量和每个流的格式。
-
接下来,如果流已压缩,则必须使用适当的解码器对其进行解码。
下图显示了用于读取媒体文件的组件:
-
对未压缩的音频/视频数据进行编码。
-
将压缩的数据放入特定的容器格式。
下图显示了用于写入媒体文件的组件:
Media Foundation体系结构概述
Media Foundation体系结构的高级视图:
Media Foundation提供两种编程模型:
1、左侧显示第一个模型使用媒体数据到管道,应用程序初始化管道(例如,通过提供要播放的文件的 URL),然后调用方法来控制流式处理。
2、右侧显示的第二个模型,应用程序从源拉取数据,或将数据推送到目标 (或两者都) 。 如果需要处理数据,此模型特别有用,因为应用程序可以直接访问数据流。
壹、基元和平台 Primitives and Platform
主题 | 说明 |
---|---|
Attributes | 属性和属性是存储在对象上的键/值对 |
Media Types | 媒体类型描述数字媒体流的格式 |
[Media Buffers] | 媒体缓冲区管理内存块,以便可以在对象之间共享内存块 |
[Media Samples] | 媒体示例是包含媒体缓冲区列表的对象 |
一、Attributes
1、说明
属性是键/值对,其中键为 GUID,值为 PROPVARIANT 属性值限制为以下数据类型:
-
无符号 32 位整数 (UINT32)
-
无符号 64 位整数 (UINT64)
-
64 位浮点数
-
GUID
-
以 NULL 结尾的宽字符字符串
-
字节数组
-
IUnknown 指针
这些类型在 MF_ATTRIBUTE_TYPE 枚举中定义。 若要设置或检索属性值,请使用 IMFAttributes 接口, 例如,若要设置 32 位整数,请调用 IMFAttributes::SetUINT32。 属性键在 对象中是唯一的。 如果设置两个具有相同键的不同值,则第二个值将覆盖第一个值
多个媒体基础接口继承 IMFAttributes 接口。 公开此接口的对象具有应用程序应在对象上设置的可选或必需属性,或者具有应用程序可以检索的属性。 此外,某些方法和函数将 IMFAttributes 指针作为参数,使应用程序能够设置配置信息。 应用程序必须创建属性存储来保存配置属性。 若要创建空的属性存储,请调用 MFCreateAttributes
extern const GUID MY_ATTRIBUTE;
HRESULT ShowCreateAttributeStore(IMFAttributes **ppAttributes)
{
IMFAttributes *pAttributes = NULL;
const UINT32 cElements = 10; // Starting size.
// 创建空属性存储
HRESULT hr = MFCreateAttributes(&pAttributes, cElements);
// 使用字符串值设置 MY_ATTRIBUTE 属性
if (SUCCEEDED(hr))
{
hr = pAttributes->SetString(
MY_ATTRIBUTE,
L"This is a string value"
);
}
// 将IMFAttributes 指针返回给调用者
if (SUCCEEDED(hr))
{
*ppAttributes = pAttributes;
(*ppAttributes)->AddRef();
}
SAFE_RELEASE(pAttributes);
return hr;
}
HRESULT ShowGetAttributes()
{
IMFAttributes *pAttributes = NULL;
WCHAR *pwszValue = NULL;
UINT32 cchLength = 0;
// 创建属性存储
HRESULT hr = ShowCreateAttributeStore(&pAttributes);
// 获取属性
if (SUCCEEDED(hr))
{
hr = pAttributes->GetAllocatedString(
MY_ATTRIBUTE,
&pwszValue,
&cchLength
);
}
CoTaskMemFree(pwszValue);
SAFE_RELEASE(pAttributes);
return hr;
}
2、序列化属性
Media Foundation 有两个用于序列化属性存储的函数。 一个将属性写入字节数组,另一个将属性写入支持 IStream 接口的流。 每个函数都有一个加载数据的相应函数。
Operation | 字节数组 | IStream |
---|---|---|
保存 | MFGetAttributesAsBlob | MFSerializeAttributesToStream |
加载 | MFInitAttributesFromBlob | MFDeserializeAttributesFromStream |
-
若要将属性存储的内容写入字节数组,请调用 MFGetAttributesAsBlob。 忽略具有 IUnknown 指针值的属性。
-
若要将属性加载回属性存储中,请调用 MFInitAttributesFromBlob。
-
若要将属性存储写入流,请调用 MFSerializeAttributesToStream。 此函数可以封送 IUnknown 指针值。 调用方必须提供实现 IStream 接口的流对象。
-
若要从流加载属性存储,请调用 MFDeserializeAttributesFromStream。
3、实现 IMFAttributes
Media Foundation提供 IMFAttributes 的库存实现,该实现通过调用 MFCreateAttributes 函数获得。 在大多数情况下,应使用此实现,而不是提供自己的自定义实现。
有一种情况可能需要实现 IMFAttributes 接口:如果实现继承 IMFAttributes 的第二个接口。 在这种情况下,必须为第二个接口继承的 IMFAttributes 方法提供实现。
以下代码显示了一个类模板,该模板保存 IMFAttributes 指针并包装每个 IMFAttributes 方法( IUnknown 方法除外)
#include <assert.h>
// 用于实现 IMFAttributes 的辅助类
// 这是一个抽象类; 派生类必须实现 IUnknown 方法
// 此类是 Media Foundation 中提供的标准属性存储的包装器
// 模板参数
// IMFAttributes 或继承 IMFAttributes 的接口,例如 IMFActivate
template <class IFACE=IMFAttributes>
class CBaseAttributes : public IFACE
{
protected:
IMFAttributes *m_pAttributes;
// 此版本的构造函数不会初始化属性存储。 派生类必须在其自己的构造函数中调用Initialize()
CBaseAttributes() : m_pAttributes(NULL)
{
}
// 此版本的构造函数初始化属性存储,但派生类必须将 HRESULT 参数传递给构造函数
CBaseAttributes(HRESULT& hr, UINT32 cInitialSize = 0) : m_pAttributes(NULL)
{
hr = Initialize(cInitialSize);
}
// 构造函数的下一个版本使用调用者提供的 IMFAttributes 实现
// 代理IMFAttributes通过实现IMFAttributes 而不是使用MFCreateAttributes
CBaseAttributes(HRESULT& hr, IUnknown *pUnk)
{
hr = Initialize(pUnk);
}
virtual ~CBaseAttributes()
{
if (m_pAttributes)
{
m_pAttributes->Release();
}
}
// 通过创建标准媒体基础属性存储来初始化对象
HRESULT Initialize(UINT32 cInitialSize = 0)
{
if (m_pAttributes == NULL)
{
return MFCreateAttributes(&m_pAttributes, cInitialSize);
}
else
{
return S_OK;
}
}
// 从调用者提供的属性存储初始化此对象
// pUnk: Pointer to an object that exposes IMFAttributes.
HRESULT Initialize(IUnknown *pUnk)
{
if (m_pAttributes)
{
m_pAttributes->Release();
m_pAttributes = NULL;
}
return pUnk->QueryInterface(IID_PPV_ARGS(&m_pAttributes));
}
public:
// IMFAttributes methods
STDMETHODIMP GetItem(REFGUID guidKey, PROPVARIANT* pValue)
{
assert(m_pAttributes);
return m_pAttributes->GetItem(guidKey, pValue);
}
STDMETHODIMP GetItemType(REFGUID guidKey, MF_ATTRIBUTE_TYPE* pType)
{
assert(m_pAttributes);
return m_pAttributes->GetItemType(guidKey, pType);
}
STDMETHODIMP CompareItem(REFGUID guidKey, REFPROPVARIANT Value, BOOL* pbResult)
{
assert(m_pAttributes);
return m_pAttributes->CompareItem(guidKey, Value, pbResult);
}
STDMETHODIMP Compare(
IMFAttributes* pTheirs,
MF_ATTRIBUTES_MATCH_TYPE MatchType,
BOOL* pbResult
)
{
assert(m_pAttributes);
return m_pAttributes->Compare(pTheirs, MatchType, pbResult);
}
STDMETHODIMP GetUINT32(REFGUID guidKey, UINT32* punValue)
{
assert(m_pAttributes);
return m_pAttributes->GetUINT32(guidKey, punValue);
}
STDMETHODIMP GetUINT64(REFGUID guidKey, UINT64* punValue)
{
assert(m_pAttributes);
return m_pAttributes->GetUINT64(guidKey, punValue);
}
STDMETHODIMP GetDouble(REFGUID guidKey, double* pfValue)
{
assert(m_pAttributes);
return m_pAttributes->GetDouble(guidKey, pfValue);
}
STDMETHODIMP GetGUID(REFGUID guidKey, GUID* pguidValue)
{
assert(m_pAttributes);
return m_pAttributes->GetGUID(guidKey, pguidValue);
}
STDMETHODIMP GetStringLength(REFGUID guidKey, UINT32* pcchLength)
{
assert(m_pAttributes);
return m_pAttributes->GetStringLength(guidKey, pcchLength);
}
STDMETHODIMP GetString(REFGUID guidKey, LPWSTR pwszValue, UINT32 cchBufSize, UINT32* pcchLength)
{
assert(m_pAttributes);
return m_pAttributes->GetString(guidKey, pwszValue, cchBufSize, pcchLength);
}
STDMETHODIMP GetAllocatedString(REFGUID guidKey, LPWSTR* ppwszValue, UINT32* pcchLength)
{
assert(m_pAttributes);
return m_pAttributes->GetAllocatedString(guidKey, ppwszValue, pcchLength);
}
STDMETHODIMP GetBlobSize(REFGUID guidKey, UINT32* pcbBlobSize)
{
assert(m_pAttributes);
return m_pAttributes->GetBlobSize(guidKey, pcbBlobSize);
}
STDMETHODIMP GetBlob(REFGUID guidKey, UINT8* pBuf, UINT32 cbBufSize, UINT32* pcbBlobSize)
{
assert(m_pAttributes);
return m_pAttributes->GetBlob(guidKey, pBuf, cbBufSize, pcbBlobSize);
}
STDMETHODIMP GetAllocatedBlob(REFGUID guidKey, UINT8** ppBuf, UINT32* pcbSize)
{
assert(m_pAttributes);
return m_pAttributes->GetAllocatedBlob(guidKey, ppBuf, pcbSize);
}
STDMETHODIMP GetUnknown(REFGUID guidKey, REFIID riid, LPVOID* ppv)
{
assert(m_pAttributes);
return m_pAttributes->GetUnknown(guidKey, riid, ppv);
}
STDMETHODIMP SetItem(REFGUID guidKey, REFPROPVARIANT Value)
{
assert(m_pAttributes);
return m_pAttributes->SetItem(guidKey, Value);
}
STDMETHODIMP DeleteItem(REFGUID guidKey)
{
assert(m_pAttributes);
return m_pAttributes->DeleteItem(guidKey);
}
STDMETHODIMP DeleteAllItems()
{
assert(m_pAttributes);
return m_pAttributes->DeleteAllItems();
}
STDMETHODIMP SetUINT32(REFGUID guidKey, UINT32 unValue)
{
assert(m_pAttributes);
return m_pAttributes->SetUINT32(guidKey, unValue);
}
STDMETHODIMP SetUINT64(REFGUID guidKey,UINT64 unValue)
{
assert(m_pAttributes);
return m_pAttributes->SetUINT64(guidKey, unValue);
}
STDMETHODIMP SetDouble(REFGUID guidKey, double fValue)
{
assert(m_pAttributes);
return m_pAttributes->SetDouble(guidKey, fValue);
}
STDMETHODIMP SetGUID(REFGUID guidKey, REFGUID guidValue)
{
assert(m_pAttributes);
return m_pAttributes->SetGUID(guidKey, guidValue);
}
STDMETHODIMP SetString(REFGUID guidKey, LPCWSTR wszValue)
{
assert(m_pAttributes);
return m_pAttributes->SetString(guidKey, wszValue);
}
STDMETHODIMP SetBlob(REFGUID guidKey, const UINT8* pBuf, UINT32 cbBufSize)
{
assert(m_pAttributes);
return m_pAttributes->SetBlob(guidKey, pBuf, cbBufSize);
}
STDMETHODIMP SetUnknown(REFGUID guidKey, IUnknown* pUnknown)
{
assert(m_pAttributes);
return m_pAttributes->SetUnknown(guidKey, pUnknown);
}
STDMETHODIMP LockStore()
{
assert(m_pAttributes);
return m_pAttributes->LockStore();
}
STDMETHODIMP UnlockStore()
{
assert(m_pAttributes);
return m_pAttributes->UnlockStore();
}
STDMETHODIMP GetCount(UINT32* pcItems)
{
assert(m_pAttributes);
return m_pAttributes->GetCount(pcItems);
}
STDMETHODIMP GetItemByIndex(UINT32 unIndex, GUID* pguidKey, PROPVARIANT* pValue)
{
assert(m_pAttributes);
return m_pAttributes->GetItemByIndex(unIndex, pguidKey, pValue);
}
STDMETHODIMP CopyAllItems(IMFAttributes* pDest)
{
assert(m_pAttributes);
return m_pAttributes->CopyAllItems(pDest);
}
// 辅助函数
HRESULT SerializeToStream(DWORD dwOptions, IStream* pStm)
// dwOptions: Flags from MF_ATTRIBUTE_SERIALIZE_OPTIONS
{
assert(m_pAttributes);
return MFSerializeAttributesToStream(m_pAttributes, dwOptions, pStm);
}
HRESULT DeserializeFromStream(DWORD dwOptions, IStream* pStm)
{
assert(m_pAttributes);
return MFDeserializeAttributesFromStream(m_pAttributes, dwOptions, pStm);
}
// SerializeToBlob:将属性存储在字节数组中
// ppBuf:接收指向字节数组的指针
// pcbSize:接收字节数组的大小
// 调用者必须使用 CoTaskMemFree 释放数组
HRESULT SerializeToBlob(UINT8 **ppBuffer, UINT32 *pcbSize)
{
assert(m_pAttributes);
if (ppBuffer == NULL)
{
return E_POINTER;
}
if (pcbSize == NULL)
{
return E_POINTER;
}
*ppBuffer = NULL;
*pcbSize = 0;
UINT32 cbSize = 0;
BYTE *pBuffer = NULL;
HRESULT hr = MFGetAttributesAsBlobSize(m_pAttributes, &cbSize);
if (FAILED(hr))
{
return hr;
}
pBuffer = (BYTE*)CoTaskMemAlloc(cbSize);
if (pBuffer == NULL)
{
return E_OUTOFMEMORY;
}
hr = MFGetAttributesAsBlob(m_pAttributes, pBuffer, cbSize);
if (SUCCEEDED(hr))
{
*ppBuffer = pBuffer;
*pcbSize = cbSize;
}
else
{
CoTaskMemFree(pBuffer);
}
return hr;
}
HRESULT DeserializeFromBlob(const UINT8* pBuffer, UINT cbSize)
{
assert(m_pAttributes);
return MFInitAttributesFromBlob(m_pAttributes, pBuffer, cbSize);
}
HRESULT GetRatio(REFGUID guidKey, UINT32* pnNumerator, UINT32* punDenominator)
{
assert(m_pAttributes);
return MFGetAttributeRatio(m_pAttributes, guidKey, pnNumerator, punDenominator);
}
HRESULT SetRatio(REFGUID guidKey, UINT32 unNumerator, UINT32 unDenominator)
{
assert(m_pAttributes);
return MFSetAttributeRatio(m_pAttributes, guidKey, unNumerator, unDenominator);
}
// 获取一个属性,该属性的值表示某物(例如视频帧)的大小
HRESULT GetSize(REFGUID guidKey, UINT32* punWidth, UINT32* punHeight)
{
assert(m_pAttributes);
return MFGetAttributeSize(m_pAttributes, guidKey, punWidth, punHeight);
}
// 设置一个属性,其值表示某物(例如视频帧)的大小
HRESULT SetSize(REFGUID guidKey, UINT32 unWidth, UINT32 unHeight)
{
assert(m_pAttributes);
return MFSetAttributeSize (m_pAttributes, guidKey, unWidth, unHeight);
}
};
模板派生类:
#include <shlwapi.h>
class MyObject : public CBaseAttributes<>
{
MyObject() : m_nRefCount(1) { }
~MyObject() { }
long m_nRefCount;
public:
// IUnknown
STDMETHODIMP MyObject::QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] =
{
QITABENT(MyObject, IMFAttributes),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) MyObject::AddRef()
{
return InterlockedIncrement(&m_nRefCount);
}
STDMETHODIMP_(ULONG) MyObject::Release()
{
ULONG uCount = InterlockedDecrement(&m_nRefCount);
if (uCount == 0)
{
delete this;
}
return uCount;
}
// 创建对象实例的静态函数
static HRESULT CreateInstance(MyObject **ppObject)
{
HRESULT hr = S_OK;
MyObject *pObject = new MyObject();
if (pObject == NULL)
{
return E_OUTOFMEMORY;
}
// 初始化属性存储
hr = pObject->Initialize();
if (FAILED(hr))
{
delete pObject;
return hr;
}
*ppObject = pObject;
(*ppObject)->AddRef();
return S_OK;
}
};
二、Media Types
媒体类型描述媒体流的格式。 在 Microsoft Media Foundation 中,媒体类型由 IMFMediaType 接口表示。 此接口继承 IMFAttributes 接口。 媒体类型的详细信息指定为属性。
若要创建新的媒体类型,请调用 MFCreateMediaType 函数。 此函数返回指向 IMFMediaType 接口的指针。 媒体类型最初没有属性。 若要设置格式的详细信息,请设置相关属性。
1、主要类型和子类型
-
主要类型是一个 GUID,用于定义媒体流中数据的总体类别。 主要类型包括视频和音频。 若要指定主要类型,请设置 MF_MT_MAJOR_TYPE 属性。 IMFMediaType::GetMajorType 方法返回此属性的值。
-
子类型进一步定义格式。 例如,在视频主要类型中,存在 RGB-24、RGB-32、YUY2 等子类型。 在音频中,有 PCM 音频、IEEE 浮点音频等。 子类型提供的信息比主类型更多,但它不定义有关格式的所有内容。 例如,视频子类型不定义图像大小或帧速率。 若要指定子类型,请设置 MF_MT_SUBTYPE 属性。
2、主要媒体类型
要类型和子类型由 GUID 标识,并存储在以下属性中:
属性 | 说明 |
---|---|
MF_MT_MAJOR_TYPE | 主要类型。 |
MF_MT_SUBTYPE | 亚。 |
定义了以下主要类型:
主要类型 | 说明 | 子类型 |
---|---|---|
MFMediaType_Audio | 音频。 | 音频子类型 GUID。 |
MFMediaType_Binary | 二进制流。 | 无。 |
MFMediaType_FileTransfer | 包含数据文件的流。 | 无。 |
MFMediaType_HTML | HTML 流。 | 无。 |
MFMediaType_Image | 静止图像流。 | WIC GUID 和 CLSD。 |
MFMediaType_Metadata | 元数据流。 | 无。 |
MFMediaType_Protected | 受保护的媒体。 | 子类型指定内容保护方案。 |
MFMediaType_Perception | 来自相机传感器或处理单元的流,用于推理和理解原始视频数据,并提供对其中环境或人类的理解。 | 无。 |
MFMediaType_SAMI | 同步的可访问媒体交换 (SAMI) 标题。 | 无。 |
MFMediaType_Script | 脚本流。 | 无。 |
MFMediaType_Stream | 多路复用流或基本流。 | 流子类型 GUID |
MFMediaType_Video | 视频。 | 视频子类型 GUID。 |
3、音频类型
(1)、音频子类型 GUID
定义了以下音频子类型 GUID。 若要指定子类型,请在媒体类型上设置 MF_MT_SUBTYPE 属性。 除非另有说明,否则这些常量在头文件 mfapi.h 中定义。
使用这些子类型时,请将 MF_MT_MAJOR_TYPE 属性设置为 MFMediaType_Audio。
GUID | 描述 | 格式标记 (FOURCC) |
---|---|---|
MEDIASUBTYPE_RAW_AAC1 | 高级音频编码 (AAC) 。 此子类型用于 AVI 文件中包含的 AAC,其音频格式标记等于 0x00FF。 有关详细信息,请参阅 AAC 解码器。 在 wmcodecdsp.h 中定义 | WAVE_FORMAT_RAW_AAC1 (0x00FF) |
MFAudioFormat_AAC | 高级音频编码 (AAC) 。 [!注意] 等效于 MEDIASUBTYPE_MPEG_HEAAC,在 wmcodecdsp.h 中定义。 流可以包含音频数据传输流中的原始 AAC 数据或 AAC 数据, (ADTS) 流。 有关详细信息,请参阅: AAC 解码器MPEG-4 文件源 | WAVE_FORMAT_MPEG_HEAAC (0x1610) |
MFAudioFormat_ADTS | 未使用。 | WAVE_FORMAT_MPEG_ADTS_AAC (0x1600) |
MFAudioFormat_ALAC | Apple 无损音频编解码器 在 Windows 10 及更高版本中受支持。 | WAVE_FORMAT_ALAC (0x6C61) |
MFAudioFormat_AMR_NB | 自适应多速率音频 在 Windows 8.1 及更高版本中受支持。 | WAVE_FORMAT_AMR_NB |
MFAudioFormat_AMR_WB | 自适应多速率宽带音频 在 Windows 8.1 及更高版本中受支持。 | WAVE_FORMAT_AMR_WB |
MFAudioFormat_AMR_WP | 在 Windows 8.1 及更高版本中受支持。 | WAVE_FORMAT_AMR_WP |
MFAudioFormat_Dolby_AC3 | Dolby Digital (AC-3) 。 与 ksuuids.h 中定义的 MEDIASUBTYPE_DOLBY_AC3 相同的 GUID 值 | 无。 |
MFAudioFormat_Dolby_AC3_SPDIF | 通过 Sony/Philips 数字接口 (S/PDIF) 的 Dolby AC-3 音频。 此 GUID 值与以下子类型相同: KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL,在 ksmedia.h 中定义。MEDIASUBTYPE_DOLBY_AC3_SPDIF,在 uuids.h 中定义。 | WAVE_FORMAT_DOLBY_AC3_SPDIF (0x0092) |
MFAudioFormat_Dolby_DDPlus | Dolby Digital Plus。 与 wmcodecdsp.h 中定义的 MEDIASUBTYPE_DOLBY_DDPLUS 相同的 GUID 值。 | 无 |
MFAudioFormat_DRM | 与安全音频路径一起使用的加密音频数据。 | WAVE_FORMAT_DRM (0x0009) |
MFAudioFormat_DTS | 数字剧院系统 (DTS) 音频。 | WAVE_FORMAT_DTS (0x0008) |
MFAudioFormat_FLAC | 免费无损音频编解码器 在 Windows 10 及更高版本中受支持。 | WAVE_FORMAT_FLAC (0xF1AC) |
MFAudioFormat_Float | 未压缩的 IEEE 浮点音频。 | WAVE_FORMAT_IEEE_FLOAT (0x0003) |
MFAudioFormat_Float_SpatialObjects | 未压缩的 IEEE 浮点音频。 | 无 |
MFAudioFormat_MP3 | MPEG 音频第 3 层 (MP3) 。 | WAVE_FORMAT_MPEGLAYER3 (0x0055) |
MFAudioFormat_MPEG | MPEG-1 音频有效负载。 | WAVE_FORMAT_MPEG (0x0050) |
MFAudioFormat_MSP1 | Windows Media Audio 9 语音编解码器。 | WAVE_FORMAT_WMAVOICE9 (0x000A) |
MFAudioFormat_Opus | Opus 在 Windows 10 及更高版本中受支持。 | WAVE_FORMAT_OPUS (0x704F) |
MFAudioFormat_PCM | 未压缩的 PCM 音频。 | WAVE_FORMAT_PCM (1) |
MFAudioFormat_QCELP | QCELP (Qualcomm Code 激发线性预测) 音频。 | 无 |
MFAudioFormat_WMASPDIF | 基于 S/PDIF 的 Windows Media Audio 9 专业编解码器。 | WAVE_FORMAT_WMASPDIF (0x0164) |
MFAudioFormat_WMAudio_Lossless | Windows Media Audio 9 无损编解码器或 Windows Media Audio 9.1 编解码器。 | WAVE_FORMAT_WMAUDIO_LOSSLESS (0x0163) |
MFAudioFormat_WMAudioV8 | Windows Media Audio 8 编解码器、Windows Media Audio 9 编解码器或 Windows Media Audio 9.1 编解码器。 | WAVE_FORMAT_WMAUDIO2 (0x0161) |
MFAudioFormat_WMAudioV9 | Windows Media Audio 9 专业编解码器或 Windows Media Audio 9.1 专业编解码器。 | WAVE_FORMAT_WMAUDIO3 (0x0162) |
此表的第三列中列出的格式标记用于 WAVEFORMATEX 结构中,并在头文件 mmreg.h 中定义。给定音频格式标记后,可以按如下所示创建音频子类型 GUID:
-
从 MFAudioFormat_Base 值开始,该值在 mfaph.i 中定义。
-
将此 GUID 的第一个 DWORD 替换为格式标记。
(2)、未压缩的音频媒体类型
若要创建完整的未压缩音频类型,请在 IMFMediaType 接口指针上至少设置以下属性。
Attribute | 说明 |
---|---|
MF_MT_MAJOR_TYPE | 主要类型。 设置为 MFMediaType_Audio。 |
MF_MT_SUBTYPE | 亚。 请参阅 音频子类型 GUID。 |
MF_MT_AUDIO_NUM_CHANNELS | 音频通道数。 |
MF_MT_AUDIO_SAMPLES_PER_SECOND | 每秒音频样本数。 |
MF_MT_AUDIO_BLOCK_ALIGNMENT | 块对齐方式。 |
MF_MT_AUDIO_AVG_BYTES_PER_SECOND | 每秒的平均字节数。 |
MF_MT_AUDIO_BITS_PER_SAMPLE | 每个音频样本的位数。 |
MF_MT_ALL_SAMPLES_INDEPENDENT | 指定每个音频样本是否独立。 对于MFAudioFormat_PCM和MFAudioFormat_Float格式,设置为 TRUE 。 |
此外,某些音频格式需要以下属性。
Attribute | 说明 |
---|---|
MF_MT_AUDIO_VALID_BITS_PER_SAMPLE | 每个音频示例中音频数据的有效位数。 如果音频样本具有填充,即每个音频样本中的有效位数小于样本大小,则设置此属性。 |
MF_MT_AUDIO_CHANNEL_MASK | 音频通道分配给扬声器位置。 为多通道音频流(如 5.1)设置此属性。 单声道或立体声音频不需要此属性。 |
为未压缩的 PCM 音频创建媒体类型
HRESULT CreatePCMAudioType(
UINT32 sampleRate, // Samples per second
UINT32 bitsPerSample, // Bits per sample
UINT32 cChannels, // Number of channels
IMFMediaType **ppType // Receives a pointer to the media type.
)
{
HRESULT hr = S_OK;
IMFMediaType *pType = NULL;
// Calculate derived values.
UINT32 blockAlign = cChannels * (bitsPerSample / 8);
UINT32 bytesPerSecond = blockAlign * sampleRate;
// Create the empty media type.
hr = MFCreateMediaType(&pType);
if (FAILED(hr))
{
goto done;
}
// Set attributes on the type.
hr = pType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
if (FAILED(hr))
{
goto done;
}
hr = pType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
if (FAILED(hr))
{
goto done;
}
hr = pType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, cChannels);
if (FAILED(hr))
{
goto done;
}
hr = pType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, sampleRate);
if (FAILED(hr))
{
goto done;
}
hr = pType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, blockAlign);
if (FAILED(hr))
{
goto done;
}
hr = pType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, bytesPerSecond);
if (FAILED(hr))
{
goto done;
}
hr = pType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample);
if (FAILED(hr))
{
goto done;
}
hr = pType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
if (FAILED(hr))
{
goto done;
}
// Return the type to the caller.
*ppType = pType;
(*ppType)->AddRef();
done:
SafeRelease(&pType);
return hr;
}
采用编码的音频格式作为输入,并创建匹配的 PCM 音频类型
//-------------------------------------------------------------------
// ConvertAudioTypeToPCM
//
// Given an audio media type (which might describe a compressed audio
// format), returns a media type that describes the equivalent
// uncompressed PCM format.
//-------------------------------------------------------------------
HRESULT ConvertAudioTypeToPCM(
IMFMediaType *pType, // Pointer to an encoded audio type.
IMFMediaType **ppType // Receives a matching PCM audio type.
)
{
HRESULT hr = S_OK;
GUID majortype = { 0 };
GUID subtype = { 0 };
UINT32 cChannels = 0;
UINT32 samplesPerSec = 0;
UINT32 bitsPerSample = 0;
hr = pType->GetMajorType(&majortype);
if (FAILED(hr))
{
return hr;
}
if (majortype != MFMediaType_Audio)
{
return MF_E_INVALIDMEDIATYPE;
}
// Get the audio subtype.
hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype);
if (FAILED(hr))
{
return hr;
}
if (subtype == MFAudioFormat_PCM)
{
// This is already a PCM audio type. Return the same pointer.
*ppType = pType;
(*ppType)->AddRef();
return S_OK;
}
// Get the sample rate and other information from the audio format.
cChannels = MFGetAttributeUINT32(pType, MF_MT_AUDIO_NUM_CHANNELS, 0);
samplesPerSec = MFGetAttributeUINT32(pType, MF_MT_AUDIO_SAMPLES_PER_SECOND, 0);
bitsPerSample = MFGetAttributeUINT32(pType, MF_MT_AUDIO_BITS_PER_SAMPLE, 16);
// Note: Some encoded audio formats do not contain a value for bits/sample.
// In that case, use a default value of 16. Most codecs will accept this value.
if (cChannels == 0 || samplesPerSec == 0)
{
return MF_E_INVALIDTYPE;
}
// Create the corresponding PCM audio type.
hr = CreatePCMAudioType(samplesPerSec, bitsPerSample, cChannels, ppType);
return hr;
}
(3)、AAC 媒体类型
本主题介绍如何在 Media Foundation 中指定高级音频编码 (AAC) 流的格式。
为 AAC 音频定义了两个子类型:
子类型 | 说明 | 标头 |
---|---|---|
MFAudioFormat_AAC | 原始 AAC 或 ADTS AAC。 | mfapi.h |
MEDIASUBTYPE_RAW_AAC1 | 原始 AAC。 | wmcodecdsp.h |
MFAudioFormat_AAC
对于此子类型,媒体类型在应用光谱带复制之前提供采样率和通道数, (SBR) 和参数立体声 (PS) 工具(如果存在)。 SBR 工具的效果是将解码的采样率相对于核心 AAC-LC 采样率翻倍。 PS 工具的作用是解码单声道核心 AAC-LC 流中的立体声。
此子类型等效于 wmcodecdsp.h 中定义的 MEDIASUBTYPE_MPEG_HEAAC。
MEDIASUBTYPE_RAW_AAC1
此子类型用于包含在音频格式标记等于 WAVE_FORMAT_RAW_AAC1 (0x00FF) 的 AVI 文件中的 AAC。
对于此子类型,媒体类型在应用 SBR 和 PS 工具后提供采样率和通道数(如果存在)。
以下媒体类型属性适用于 AAC 音频。
Attribute | 说明 |
---|---|
MF_MT_MAJOR_TYPE | 主要类型。 必须 MFMediaType_Audio。 |
MF_MT_SUBTYPE | 音频子类型。 有关详细信息,请参阅前面的说明。 |
MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION | 音频配置文件和级别。 此属性的值是 audioProfileLevelIndication 字段,由 ISO/IEC 14496-3 定义。 如果未知,则设置为零或0xFE (“未指定音频配置文件”) 。 |
MF_MT_AUDIO_AVG_BYTES_PER_SECOND | 编码的 AAC 流的比特率(以每秒字节为单位)。 |
MF_MT_AAC_PAYLOAD_TYPE | 负载类型。 仅适用于 MFAudioFormat_AAC。 MF_MT_AAC_PAYLOAD_TYPE 是可选的。 如果未指定此属性,则使用默认值 0,该值指定流仅包含raw_data_block元素。 |
MF_MT_AUDIO_BITS_PER_SAMPLE | 解码的 PCM 音频的位深度。 |
MF_MT_AUDIO_CHANNEL_MASK | 将音频通道分配给扬声器位置。 |
MF_MT_AUDIO_NUM_CHANNELS | 通道数,包括低频 (LFE) 通道(如果存在)。 此值的解释取决于媒体子类型,如前所述。 |
MF_MT_AUDIO_SAMPLES_PER_SECOND | 采样率,以每秒样本数为单位。 此值的解释取决于媒体子类型,如前所述。 |
MF_MT_USER_DATA | 此属性的值取决于子类型: MFAudioFormat_AAC:包含 HEAACWAVEINFO 结构的部分,该部分显示在 WAVEFORMATEX 结构 (,即 wfx 成员) 之后。 这后跟由 ISO/IEC 14496-3 定义的 AudioSpecificConfig () 数据。MEDIASUBTYPE_RAW_AAC1:包含 AudioSpecificConfig () 数据。 |
4、视频类型
(1)、视频子类型 GUID
未压缩的RGB格式
GUID | 描述 |
---|---|
MFVideoFormat_RGB8 | RGB,每像素 8 位 (bpp) 。 (与 D3DFMT_P8.) 相同的内存布局 |
MFVideoFormat_RGB555 | RGB 555,16 bpp。 (与 D3DFMT_X1R5G5B5.) 相同的内存布局 |
MFVideoFormat_RGB565 | RGB 565,16 bpp。 (与 D3DFMT_R5G6B5.) 相同的内存布局 |
MFVideoFormat_RGB24 | RGB,24 bpp。 |
MFVideoFormat_RGB32 | RGB,32 bpp。 |
MFVideoFormat_ARGB32 | RGB,带 alpha 通道的 32 bpp。 |
MFVideoFormat_A2R10G10B10 | RGB,每种颜色为 10 bpp,alpha 为 2 bpp。 (内存布局与 D3DFMT_A2B10G10R10) 相同 |
MFVideoFormat_A16B16G16R16F | RGB,带 alpha 通道的 16 bpp。 (内存布局与 D3DFMT_A16B16G16R16F) 相同 |
注:这些子类型与以前的 SDK(如 DirectShow)中使用的 RGB 子类型 GUID 不匹配
YUV格式:8 位和 Palettized
Packed 内存分布: yuv yuv yuv
Planar 内存分别: yyy uuu vvv
GUID | 格式 | 采样 | Packed或Planar | 每个通道的位数 |
---|---|---|---|---|
MFVideoFormat_AI44 | AI44 | 4:4:4 | Packed | Palettized |
MFVideoFormat_AYUV | AYUV | 4:4:4 | Packed | 8 |
MFVideoFormat_I420 | I420 | 4:2:0 | Planar | 8 |
MFVideoFormat_IYUV | IYUV | 4:2:0 | Planar | 8 |
MFVideoFormat_NV11 | NV11 | 4:1:1 | Planar | 8 |
MFVideoFormat_NV12 | NV12 | 4:2:0 | Planar | 8 |
MFVideoFormat_NV21 | NV21 | 4:2:0 | Planar | 8 |
MFVideoFormat_UYVY | UYVY | 4:2:2 | Packed | 8 |
MFVideoFormat_Y41P | Y41P | 4:1:1 | Packed | 8 |
MFVideoFormat_Y41T | Y41T | 4:1:1 | Packed | 8 |
MFVideoFormat_Y42T | Y42T | 4:2:2 | Packed | 8 |
MFVideoFormat_YUY2 | YUY2 | 4:2:2 | Packed | 8 |
MFVideoFormat_YVU9 | YVU9 | 8:4:4 | Planar | 9 |
MFVideoFormat_YV12 | YV12 | 4:2:0 | Planar | 8 |
MFVideoFormat_YVYU | YVYU | 4:2:2 | Packed | 8 |
注:I420 和 IYUV 在内存中具有相同的布局,但分配了不同的子类型 GUID。 子类型 GUID 对应于 FOURCC 代码“I420”和“IYUV”
(2)、未压缩的视频媒体类型
若要创建完整的未压缩视频类型,请在 IMFMediaType 接口指针上设置以下属性。
属性 | 说明 |
---|---|
MF_MT_MAJOR_TYPE | 主要类型。 设置为 MFMediaType_Video。 |
MF_MT_SUBTYPE | 亚。 请参阅 视频子类型 GUID。 |
MF_MT_DEFAULT_STRIDE | 图面步幅。 步幅是从一行像素到下一行像素所需的字节数。 如果步幅(以字节为单位)与视频宽度(以字节为单位)不同,请设置此属性。 否则,可以省略此属性。 |
MF_MT_FRAME_RATE | 帧速率。 |
MF_MT_FRAME_SIZE | 帧大小。 |
MF_MT_INTERLACE_MODE | 交错模式。 |
MF_MT_ALL_SAMPLES_INDEPENDENT | 指定每个样本是否独立。 对于未压缩的格式,设置为 TRUE 。 |
MF_MT_PIXEL_ASPECT_RATIO | 像素纵横比。 |
此外,如果知道正确的值,请设置以下属性。 (否则,请省略这些 attributes)
属性 | 说明 |
---|---|
MF_MT_VIDEO_PRIMARIES | 颜色主色。 |
MF_MT_TRANSFER_FUNCTION | 传输函数。 |
MF_MT_YUV_MATRIX | 传输矩阵。 |
MF_MT_VIDEO_CHROMA_SITING | 色度定位。 |
MF_MT_VIDEO_NOMINAL_RANGE | 名义范围。 |
创建描述视频标准的媒体类型,并且标准定义色度定位
函数 | 说明 |
---|---|
MFAverageTimePerFrameToFrameRate | 根据平均帧持续时间计算帧速率。 |
MFCalculateImageSize | 计算未压缩视频格式的图像大小。 |
MFFrameRateToAverageTimePerFrame | 计算给定帧速率的视频帧的平均持续时间。 |
MFGetStrideForBitmapInfoHeader | 返回视频格式的最小图面步幅。 有关详细信息,请参阅 Image Stride。 |
MFInitVideoFormat | 为某些标准视频格式(如 NTSC 电视)初始化 MFVIDEOFORMAT 结构。 然后,可以使用 结构初始化媒体类型。 |
MFIsFormatYUV | 查询视频格式是否为 YUV 格式。 |
示例
填充未压缩视频格式的最常见信息。 函数返回 IMFMediaType 接口指针。 然后,可以根据需要向媒体类型添加其他属性。
HRESULT CreateUncompressedVideoType(
DWORD fccFormat, // FOURCC or D3DFORMAT value.
UINT32 width,
UINT32 height,
MFVideoInterlaceMode interlaceMode,
const MFRatio& frameRate,
const MFRatio& par,
IMFMediaType **ppType
)
{
if (ppType == NULL)
{
return E_POINTER;
}
GUID subtype = MFVideoFormat_Base;
LONG lStride = 0;
UINT cbImage = 0;
IMFMediaType *pType = NULL;
// Set the subtype GUID from the FOURCC or D3DFORMAT value.
subtype.Data1 = fccFormat;
HRESULT hr = MFCreateMediaType(&pType);
if (FAILED(hr))
{
goto done;
}
hr = pType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
if (FAILED(hr))
{
goto done;
}
hr = pType->SetGUID(MF_MT_SUBTYPE, subtype);
if (FAILED(hr))
{
goto done;
}
hr = pType->SetUINT32(MF_MT_INTERLACE_MODE, interlaceMode);
if (FAILED(hr))
{
goto done;
}
hr = MFSetAttributeSize(pType, MF_MT_FRAME_SIZE, width, height);
if (FAILED(hr))
{
goto done;
}
// Calculate the default stride value.
hr = pType->SetUINT32(MF_MT_DEFAULT_STRIDE, UINT32(lStride));
if (FAILED(hr))
{
goto done;
}
// Calculate the image size in bytes.
hr = MFCalculateImageSize(subtype, width, height, &cbImage);
if (FAILED(hr))
{
goto done;
}
hr = pType->SetUINT32(MF_MT_SAMPLE_SIZE, cbImage);
if (FAILED(hr))
{
goto done;
}
hr = pType->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, TRUE);
if (FAILED(hr))
{
goto done;
}
hr = pType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
if (FAILED(hr))
{
goto done;
}
// Frame rate
hr = MFSetAttributeRatio(pType, MF_MT_FRAME_RATE, frameRate.Numerator,
frameRate.Denominator);
if (FAILED(hr))
{
goto done;
}
// Pixel aspect ratio
hr = MFSetAttributeRatio(pType, MF_MT_PIXEL_ASPECT_RATIO, par.Numerator,
par.Denominator);
if (FAILED(hr))
{
goto done;
}
// Return the pointer to the caller.
*ppType = pType;
(*ppType)->AddRef();
done:
SafeRelease(&pType);
return hr;
}
下一个示例采用编码的视频格式作为输入,并创建匹配的未压缩视频类型。 例如,此类型适合在编码器或解码器上设置。
HRESULT ConvertVideoTypeToUncompressedType(
IMFMediaType *pType, // Pointer to an encoded video type.
const GUID& subtype, // Uncompressed subtype (eg, RGB-32, AYUV)
IMFMediaType **ppType // Receives a matching uncompressed video type.
)
{
IMFMediaType *pTypeUncomp = NULL;
HRESULT hr = S_OK;
GUID majortype = { 0 };
MFRatio par = { 0 };
hr = pType->GetMajorType(&majortype);
if (majortype != MFMediaType_Video)
{
return MF_E_INVALIDMEDIATYPE;
}
// Create a new media type and copy over all of the items.
// This ensures that extended color information is retained.
if (SUCCEEDED(hr))
{
hr = MFCreateMediaType(&pTypeUncomp);
}
if (SUCCEEDED(hr))
{
hr = pType->CopyAllItems(pTypeUncomp);
}
// Set the subtype.
if (SUCCEEDED(hr))
{
hr = pTypeUncomp->SetGUID(MF_MT_SUBTYPE, subtype);
}
// Uncompressed means all samples are independent.
if (SUCCEEDED(hr))
{
hr = pTypeUncomp->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
}
// Fix up PAR if not set on the original type.
if (SUCCEEDED(hr))
{
hr = MFGetAttributeRatio(
pTypeUncomp,
MF_MT_PIXEL_ASPECT_RATIO,
(UINT32*)&par.Numerator,
(UINT32*)&par.Denominator
);
// Default to square pixels.
if (FAILED(hr))
{
hr = MFSetAttributeRatio(
pTypeUncomp,
MF_MT_PIXEL_ASPECT_RATIO,
1, 1
);
}
}
if (SUCCEEDED(hr))
{
*ppType = pTypeUncomp;
(*ppType)->AddRef();
}
SafeRelease(&pTypeUncomp);
return hr;
}
(3)、YUV 视频
RGB 颜色使用三个值进行编码:红色、绿色和蓝色,这三个 RGB 值构成数学坐标系,称为颜色空间
YUV指一系列颜色空间,所有这些空间都独立于颜色信息对亮度信息进行编码
Y (luma):颜色的亮度值,亮度派生自 线性 RGB 值,而 luma 派生自 非线性 (伽玛更正) RGB 值,亮度是真实亮度的更接近度量值,但由于技术原因,luma 更实用。 主要符号经常省略,但 YUV 颜色空间始终使用 luma,而不是亮度。
Luma 通过采用红色、绿色和蓝色分量的加权平均值来派生自 RGB 颜色。 对于标准定义电视,使用以下公式:
Y' = 0.299R + 0.587G + 0.114B
考虑到现代电视技术,较新的公式用于高清电视:
Y' = 0.2125R + 0.7154G + 0.0721B
UV: 影像色彩及饱和度
U = B - Y'
V = R - Y'
YUV优点
-
彩色电影兼容黑白电视
-
编码降低码率(有损)
Y‘CbCr
组件 | 范围 |
---|---|
Y' | 16–235 |
Cb/Cr | 16–240,128 表示零 |
假定 Y'CbCr 组件的精度为 8 位。 下面是使用 LUma 的 BT.601 定义的 Y'CbCr 的确切派生:
-
"开始"菜单范围 [0...1] 中的 RGB 值。 换句话说,纯黑色是 0,纯白色是 1。 重要的是,这些是) RGB 值更正的非线性 (伽马值。
-
计算 luma。 对于 BT.601,Y' = 0.299R + 0.587G + 0.114B,如前所述。
-
(B - Y') 和 (R - Y') 计算中间色差值。 对于 (B - Y') ,这些值的范围为 +/- 0.886,对于 (R - Y') ,这些值为 +/- 0.701。
-
按如下所示缩放色差值:
Pb = (0.5 / (1 - 0.114) ) × (B - Y')
Pr = (0.5 / (1 - 0.299) ) × (R - Y')