官方英文文档链接:https://docs.microsoft.com/en-us/windows/desktop/medfound/attributes-and-properties
基于05/31/2018
Attribute(属性) 是键值对,key 是 GUID,value 是一个 PROPVARIANT。Microsoft Media Foundation 中使用 attribute 用来配置对象,描述媒体格式,查询对象属性等等。
此文包含以下内容。
- About Attributes
- Serializing Attributes
- Implementing IMFAttributes
- Related topics
About Attributes
Attribute 是键值对,key 是 GUID,value 是一个 PROPVARIANT。Attribute 值仅限以下几种类型:
- Unsigned 32-bit integer (UINT32).
- Unsigned 64-bit integer (UINT64).
- 64-bit floating-point number.
- GUID.
- Null-terminated wide-character string.
- Byte array.
- IUnknown pointer.
这些类型定义在 MF_ATTRIBUTE_TYPE 枚举中。如果需要设置或检索 Attribute,使用 IMFAttributes 接口。这个接口在设置数据的时候会使用“类型安全”(就是根据数据类型调用不同的方法)的方法。例如要设置一个32位 int,就调用 IMFAttributes::SetUINT32。 Attribute keys 在一个对象中是唯一的,如果对同一个 key 设置了两次不同的数值,那么第二次会覆盖第一次。
很多 Media Foundation 接口都继承了 IMFAttributes 接口。应用程序应该通过对象设置这些属性,bjects that expose this interface have optional or mandatory attributes that the application should set on the object, or have attributes that the application can retrieve. 此外,某些方法将 IMFAttributes 指针作为参数传递,使应用程序能够进行设置。应用程序必须创建一个新的 attribute 对象来存储设置。要创建新的 attribute ,调用 MFCreateAttributes。
下面的代码包含两个方法。第一个方法创建了一个新的 attribute 对象来存储设置,这个属性叫做 MY_ATTRIBUTE ,值是一个 string。第二个函数获取此属性的值。
extern const GUID MY_ATTRIBUTE;HRESULT ShowCreateAttributeStore(IMFAttributes **ppAttributes)
{IMFAttributes *pAttributes = NULL;const UINT32 cElements = 10; // Starting size.// Create the empty attribute store.HRESULT hr = MFCreateAttributes(&pAttributes, cElements);// Set the MY_ATTRIBUTE attribute with a string value.if (SUCCEEDED(hr)){hr = pAttributes->SetString(MY_ATTRIBUTE,L"This is a string value");}// Return the IMFAttributes pointer to the caller.if (SUCCEEDED(hr)){*ppAttributes = pAttributes;(*ppAttributes)->AddRef();}SAFE_RELEASE(pAttributes);return hr;
}HRESULT ShowGetAttributes()
{IMFAttributes *pAttributes = NULL;WCHAR *pwszValue = NULL;UINT32 cchLength = 0;// Create the attribute store.HRESULT hr = ShowCreateAttributeStore(&pAttributes);// Get the attribute.if (SUCCEEDED(hr)){hr = pAttributes->GetAllocatedString(MY_ATTRIBUTE,&pwszValue,&cchLength);}CoTaskMemFree(pwszValue);SAFE_RELEASE(pAttributes);return hr;
}
有关 Media Foundation 属性(attribute)的完整列表,查看 Media Foundation Attributes。. The expected data type for each attribute is documented there.
Serializing Attributes(序列化属性)
Media Foundation 有两个用于属性序列化存储的方法。一个是将属性写入一个数组,另一个是将属性写入支持 IStream 接口的流。每个方法都有一个对应的函数来加载属性(就是反向,还原)
Operation | Byte Array | IStream |
---|---|---|
存储 | MFGetAttributesAsBlob | MFSerializeAttributesToStream |
加载 | MFInitAttributesFromBlob | MFDeserializeAttributesFromStream |
要将属性存储的内容写入数组,调用 MFGetAttributesAsBlob。值为 IUnknow 指针的属性将被忽略(?原话Attributes with IUnknown pointer values are ignored.)。要将属性加载回 attribute,调用 MFInitAttributesFromBlob。
若要将属性存储写入流,调用 MFSerializeAttributesToStream。这个方法可以封装发送 IUnknown 指针值。调用者必须提供实现了 IStream 接口的对象。要把属性从流加载回 attribute 对象,调用 MFDeserializeAttributesFromStream。
Implementing IMFAttributes
Media Foundation 提供了 IMFAttributes 的(stock implementation),包含在 MFCreateAttributes 中。大多数情况下应使用此实现。
不过这里有一种情况你需要自己去实现 IMFAttributes 接口:如果你要实现一个继承自 IMFAttributes 的接口,在这种情况下,你必须自己实现 IMFAttributes 的各个方法。
在这种情况下,建议建议包装 Media Foundation 对 IMFAttributes 的现有实现。下面的代码展示了一个类模板,该类包含一个 IMFAttributes 指针和所有的 IMFAttributes 方法(IUnknown除外)。
#include <assert.h>// Helper class to implement IMFAttributes. // This is an abstract class; the derived class must implement the IUnknown
// methods. This class is a wrapper for the standard attribute store provided
// in Media Foundation.// template parameter:
// The interface you are implementing, either IMFAttributes or an interface
// that inherits IMFAttributes, such as IMFActivatetemplate <class IFACE=IMFAttributes>
class CBaseAttributes : public IFACE
{
protected:IMFAttributes *m_pAttributes;// This version of the constructor does not initialize the // attribute store. The derived class must call Initialize() in // its own constructor.CBaseAttributes() : m_pAttributes(NULL){}// This version of the constructor initializes the attribute // store, but the derived class must pass an HRESULT parameter // to the constructor.CBaseAttributes(HRESULT& hr, UINT32 cInitialSize = 0) : m_pAttributes(NULL){hr = Initialize(cInitialSize);}// The next version of the constructor uses a caller-provided // implementation of IMFAttributes.// (Sometimes you want to delegate IMFAttributes calls to some // other object that implements IMFAttributes, rather than using // MFCreateAttributes.)CBaseAttributes(HRESULT& hr, IUnknown *pUnk){hr = Initialize(pUnk);}virtual ~CBaseAttributes(){if (m_pAttributes){m_pAttributes->Release();}}// Initializes the object by creating the standard Media Foundation attribute store.HRESULT Initialize(UINT32 cInitialSize = 0){if (m_pAttributes == NULL){return MFCreateAttributes(&m_pAttributes, cInitialSize); }else{return S_OK;}}// Initializes this object from a caller-provided attribute store.// 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 methodsSTDMETHODIMP 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);}// Helper functionsHRESULT 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: Stores the attributes in a byte array. // // ppBuf: Receives a pointer to the byte array. // pcbSize: Receives the size of the byte array.//// The caller must free the array using 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);}// Gets an attribute whose value represents the size of something (eg a video frame).HRESULT GetSize(REFGUID guidKey, UINT32* punWidth, UINT32* punHeight){assert(m_pAttributes);return MFGetAttributeSize(m_pAttributes, guidKey, punWidth, punHeight);}// Sets an attribute whose value represents the size of something (eg a video frame).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:// IUnknownSTDMETHODIMP 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 function to create an instance of the object.static HRESULT CreateInstance(MyObject **ppObject){HRESULT hr = S_OK;MyObject *pObject = new MyObject();if (pObject == NULL){return E_OUTOFMEMORY;}// Initialize the attribute store.hr = pObject->Initialize();if (FAILED(hr)){delete pObject;return hr;}*ppObject = pObject;(*ppObject)->AddRef();return S_OK;}
};
你必须调用 CBaseAttributes::Initialize
来创建一个 attribute store。在上面的代码中,这一步在一个静态的创建方法(static HRESULT CreateInstance(MyObject **ppObject);)中完成。
模板参数是一个接口类型,默认是 IMFAttributes 。如果要实例化继承自 IMFAttributes 的接口,例如 IMFActivate,设置模板参数为派生接口名称即可。