MediaFoundation采集

音频和视频质量

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 层) 和文件格式。

但是,这一区别很重要,因为读取媒体文件实际上涉及两个阶段:

  1. 首先,必须分析容器。 在大多数情况下,在此步骤完成之前,无法知道流的数量和每个流的格式。

  2. 接下来,如果流已压缩,则必须使用适当的解码器对其进行解码。

下图显示了用于读取媒体文件的组件:

  1. 对未压缩的音频/视频数据进行编码。

  2. 将压缩的数据放入特定的容器格式。

下图显示了用于写入媒体文件的组件:

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

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_AACMF_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 的确切派生:

  1. "开始"菜单范围 [0...1] 中的 RGB 值。 换句话说,纯黑色是 0,纯白色是 1。 重要的是,这些是) RGB 值更正的非线性 (伽马值。

  2. 计算 luma。 对于 BT.601,Y' = 0.299R + 0.587G + 0.114B,如前所述。

  3. (B - Y') 和 (R - Y') 计算中间色差值。 对于 (B - Y') ,这些值的范围为 +/- 0.886,对于 (R - Y') ,这些值为 +/- 0.701。

  4. 按如下所示缩放色差值:

    Pb = (0.5 / (1 - 0.114) ) × (B - Y')

    Pr = (0.5 / (1 - 0.299) ) × (R - Y')

  • 21
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值