// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved #include "stdafx.h" #include "tedtranscode.h" #include "tedobj.h" #include "tedutil.h" #include #include #include #include #define SAFE_COTASKMEMFREE(p) CoTaskMemFree(p); p = NULL; const LPCWSTR CTedTranscodeTopologyBuilder::m_kszTranscodeProfileFile = L"TranscodeProfiles.xml"; const CTedTranscodeTopologyBuilder::StringGuidMap CTedTranscodeTopologyBuilder::m_kaContainerMap[] = { { L"MFTranscodeContainerType_ASF", MFTranscodeContainerType_ASF }, { L"MFTranscodeContainerType_MPEG4", MFTranscodeContainerType_MPEG4 }, }; #define ContainerMapSize (sizeof(m_kaContainerMap) / sizeof(StringGuidMap)) const CTedTranscodeTopologyBuilder::StringGuidMap CTedTranscodeTopologyBuilder::m_kaAudioSubtypeMap[] = { { L"MFAudioFormat_WMAudioV9", MFAudioFormat_WMAudioV9 }, { L"MFAudioFormat_WMAudioV8", MFAudioFormat_WMAudioV8 }, { L"MFAudioFormat_WMAudio_Lossless", MFAudioFormat_WMAudio_Lossless }, { L"MFAudioFormat_MSP1", MFAudioFormat_MSP1 }, { L"MFAudioFormat_MP3", MFAudioFormat_MP3 }, { L"MFAudioFormat_PCM", MFAudioFormat_PCM }, { L"MFAudioFormat_Float", MFAudioFormat_Float }, { L"MFAudioFormat_AAC", MFAudioFormat_AAC }, }; #define AudioSubtypeMapSize (sizeof(m_kaAudioSubtypeMap) / sizeof(StringGuidMap)) const CTedTranscodeTopologyBuilder::StringGuidMap CTedTranscodeTopologyBuilder::m_kaVideoSubtypeMap[] = { { L"MFVideoFormat_WMV3", MFVideoFormat_WMV3 }, { L"MFVideoFormat_WMV2", MFVideoFormat_WMV2 }, { L"MFVideoFormat_WMV1", MFVideoFormat_WMV1 }, { L"MFVideoFormat_MSS2", MFVideoFormat_MSS2 }, { L"MFVideoFormat_MSS1", MFVideoFormat_MSS1 }, { L"MFVideoFormat_MPEG2", MFVideoFormat_MPEG2 }, { L"MFVideoFormat_M4S2", WMMEDIASUBTYPE_M4S2 }, { L"MFVideoFormat_H264", MFVideoFormat_H264 }, }; #define VideoSubtypeMapSize (sizeof(m_kaVideoSubtypeMap) / sizeof(StringGuidMap)) const CTedTranscodeTopologyBuilder::StringAttributeMap CTedTranscodeTopologyBuilder::m_kaAudioAttributeMap[] = { { L"AudioFormat", NULL, MF_MT_SUBTYPE, AttributeType_AudioSubtype }, { L"AudioBitsPerSample", NULL, MF_MT_AUDIO_BITS_PER_SAMPLE, AttributeType_UINT32 }, { L"AudioSamplesPerSecond", NULL, MF_MT_AUDIO_SAMPLES_PER_SECOND, AttributeType_UINT32 }, { L"AudioNumChannels", NULL, MF_MT_AUDIO_NUM_CHANNELS, AttributeType_UINT32 }, { L"AudioBlockAlignment", NULL, MF_MT_AUDIO_BLOCK_ALIGNMENT, AttributeType_UINT32 }, { L"AudioAvgBytesPerSecond", NULL, MF_MT_AUDIO_AVG_BYTES_PER_SECOND, AttributeType_UINT32 }, { L"AudioEncodingProfile", NULL, MF_TRANSCODE_ENCODINGPROFILE, AttributeType_String }, }; #define AudioAttributeMapSize (sizeof(m_kaAudioAttributeMap) / sizeof(StringAttributeMap)) const CTedTranscodeTopologyBuilder::StringAttributeMap CTedTranscodeTopologyBuilder::m_kaVideoAttributeMap[] = { { L"VideoFormat", NULL, MF_MT_SUBTYPE, AttributeType_VideoSubtype }, { L"VideoBitrate", NULL, MF_MT_AVG_BITRATE, AttributeType_UINT32 }, { L"VideoEncodeComplexity", NULL, MF_TRANSCODE_QUALITYVSSPEED, AttributeType_UINT32 }, { L"VideoFrameWidth", L"VideoFrameHeight", MF_MT_FRAME_SIZE, AttributeType_Ratio }, { L"VideoFrameRateNumerator", L"VideoFrameRateDenominator", MF_MT_FRAME_RATE, AttributeType_Ratio }, { L"VideoPixelAspectRatioNumerator", L"VideoPixelAspectRatioDenominator", MF_MT_PIXEL_ASPECT_RATIO, AttributeType_Ratio }, { L"VideoEncodingProfile", NULL, MF_TRANSCODE_ENCODINGPROFILE, AttributeType_String }, }; #define VideoAttributeMapSize (sizeof(m_kaVideoAttributeMap) / sizeof(StringAttributeMap)) const CTedTranscodeTopologyBuilder::StringAttributeMap CTedTranscodeTopologyBuilder::m_kaContainerAttributeMap[] = { { L"ConatinerType", NULL, MF_TRANSCODE_CONTAINERTYPE, AttributeType_ContainerType }, { L"SkipMetadataTransfer", NULL, MF_TRANSCODE_SKIP_METADATA_TRANSFER, AttributeType_UINT32 }, }; CTedTranscodeTopologyBuilder::CTedTranscodeTopologyBuilder(LPCWSTR szSource, HRESULT* phr) { HRESULT hr; CComPtr spResolver; CComPtr spSourceUnk; MF_OBJECT_TYPE ObjectType; IFC( MFCreateSourceResolver(&spResolver) ); IFC( spResolver->CreateObjectFromURL(szSource, MF_RESOLUTION_MEDIASOURCE, NULL, &ObjectType, &spSourceUnk) ); hr = spSourceUnk->QueryInterface(IID_IMFMediaSource, (void**) &m_spSource); if(E_NOINTERFACE == hr) { hr = MF_E_UNSUPPORTED_BYTESTREAM_TYPE; } IFC( hr ); IFC( LoadTranscodeProfiles() ); Cleanup: *phr = hr; } CTedTranscodeTopologyBuilder::~CTedTranscodeTopologyBuilder() { } size_t CTedTranscodeTopologyBuilder::GetProfileCount() { return m_arrProfileNames.GetCount(); } CAtlStringW CTedTranscodeTopologyBuilder::GetProfileName(size_t iElement) { return m_arrProfileNames[iElement]; } HRESULT CTedTranscodeTopologyBuilder::BuildTranscodeTopology(const CAtlStringW& strProfileName, LPCWSTR szOutputFilePath, IMFTopology** ppTopology) { size_t iProfile = m_arrProfiles.GetCount(); for(size_t i = 0; i < m_arrProfileNames.GetCount(); i++) { if(strProfileName == m_arrProfileNames[i]) { iProfile = i; break; } } assert(iProfile < m_arrProfiles.GetCount()); if(iProfile >= m_arrProfiles.GetCount()) { return MF_E_NOT_FOUND; } return MFCreateTranscodeTopology(m_spSource, szOutputFilePath, m_arrProfiles[iProfile], ppTopology); } HRESULT CTedTranscodeTopologyBuilder::LoadTranscodeProfiles() { HRESULT hr; CComPtr spLoader; LPWSTR szNextObject; IFC( CoCreateInstance(CLSID_CXMLDataLoader, NULL, CLSCTX_INPROC_SERVER, IID_ITedDataLoader, (void**) &spLoader) ); IFC( spLoader->LoadFromFile(m_kszTranscodeProfileFile, L"TedTranscodeProfiles") ); BOOL fHasNext; IFC( spLoader->HasNextObject(&fHasNext) ); while(fHasNext) { hr = spLoader->GetNextObject(&szNextObject); if(S_FALSE == hr) { hr = S_OK; break; } else { IFC( hr ); } if(wcscmp(szNextObject, L"TedTranscodeProfile") != 0) { IFC( E_INVALIDARG ); } IFC( LoadTranscodeProfile(spLoader) ); IFC( spLoader->HasNextObject(&fHasNext) ); } Cleanup: if(FAILED(hr)) { hr = TED_E_TRANSCODE_PROFILES_FILE_INVALID; } return hr; } HRESULT CTedTranscodeTopologyBuilder::LoadTranscodeProfile(ITedDataLoader* pLoader) { HRESULT hr; LPWSTR szData = NULL; CComPtr spTranscodeProfile; CComPtr spAudioAttributes; UINT32 cAudioAttributes; CComPtr spVideoAttributes; UINT32 cVideoAttributes; IFC( MFCreateTranscodeProfile(&spTranscodeProfile) ); IFC( pLoader->LoadData(L"ProfileName", &szData, 0) ); m_arrProfileNames.Add(szData); SAFE_COTASKMEMFREE( szData ); IFC( LoadContainerAttributes(pLoader, spTranscodeProfile) ); IFC( MFCreateAttributes(&spAudioAttributes, 6) ); IFC( LoadAttributes(pLoader, spAudioAttributes, m_kaAudioAttributeMap, AudioAttributeMapSize) ); IFC( spAudioAttributes->GetCount(&cAudioAttributes) ); if(cAudioAttributes > 0) { IFC( spAudioAttributes->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio) ); IFC( MakeCompleteAudioAttributes(spAudioAttributes) ); IFC( spTranscodeProfile->SetAudioAttributes(spAudioAttributes) ); } IFC( MFCreateAttributes(&spVideoAttributes, 6) ); IFC( LoadAttributes(pLoader, spVideoAttributes, m_kaVideoAttributeMap, VideoAttributeMapSize) ); IFC( spVideoAttributes->GetCount(&cVideoAttributes) ); if(cVideoAttributes > 0) { IFC( spVideoAttributes->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video) ); IFC( spTranscodeProfile->SetVideoAttributes(spVideoAttributes) ); } IFC( FillProfileWithSourceType(spTranscodeProfile) ); m_arrProfiles.Add(spTranscodeProfile); Cleanup: SAFE_COTASKMEMFREE( szData ); return hr; } HRESULT CTedTranscodeTopologyBuilder::MakeCompleteAudioAttributes(IMFAttributes* pAudioAttributes) { HRESULT hr; GUID guidSubtype; CComPtr spTypeCollection; DWORD cTypes; BOOL fFound = FALSE; UINT8* pbExtra = NULL; UINT32 cbExtra; IFC( pAudioAttributes->GetGUID(MF_MT_SUBTYPE, &guidSubtype) ); IFC( MFTranscodeGetAudioOutputAvailableTypes(guidSubtype, MFT_ENUM_FLAG_ALL, NULL, &spTypeCollection) ); IFC( spTypeCollection->GetElementCount(&cTypes) ); for(DWORD i = 0; i < cTypes; i++) { CComPtr spUnkType; CComPtr spType; BOOL fMatch = FALSE; IFC( spTypeCollection->GetElement(i, &spUnkType) ); IFC( spUnkType->QueryInterface(IID_IMFMediaType, (LPVOID*)&spType) ); IFC( pAudioAttributes->Compare(spType, MF_ATTRIBUTES_MATCH_OUR_ITEMS, &fMatch) ); if(fMatch) { IFC( spType->GetAllocatedBlob(MF_MT_USER_DATA, &pbExtra, &cbExtra) ); IFC( pAudioAttributes->SetBlob(MF_MT_USER_DATA, pbExtra, cbExtra) ); fFound = TRUE; break; } } if(!fFound) { IFC( TED_E_INVALID_TRANSCODE_PROFILE ) } Cleanup: CoTaskMemFree( pbExtra ); return hr; } HRESULT CTedTranscodeTopologyBuilder::LoadContainerAttributes(ITedDataLoader* pLoader, IMFTranscodeProfile* pProfile) { HRESULT hr; CComPtr spAttributes; LPWSTR szData = NULL; GUID gidContainerType; IFC( MFCreateAttributes(&spAttributes, 1) ); IFC( pLoader->LoadData(L"ContainerType", &szData, 0) ); IFC( StringToContainerType(szData, &gidContainerType) ); IFC( spAttributes->SetGUID(MF_TRANSCODE_CONTAINERTYPE, gidContainerType) ); SAFE_COTASKMEMFREE( szData ); hr = pLoader->LoadData(L"SkipMetadataTransfer", &szData, 0); if(SUCCEEDED(hr)) { BOOL fSkipMetadata = (0 != _wtoi(szData) ); IFC( spAttributes->SetUINT32(MF_TRANSCODE_SKIP_METADATA_TRANSFER, fSkipMetadata) ); SAFE_COTASKMEMFREE( szData ); } IFC( spAttributes->SetUINT32(MF_TRANSCODE_TOPOLOGYMODE, MF_TRANSCODE_TOPOLOGYMODE_HARDWARE_ALLOWED) ); IFC( pProfile->SetContainerAttributes(spAttributes) ); Cleanup: SAFE_COTASKMEMFREE( szData ); return hr; } HRESULT CTedTranscodeTopologyBuilder::LoadAttributes(ITedDataLoader* pLoader, IMFAttributes* pAttributes, const StringAttributeMap* pMap, size_t cMapElements) { HRESULT hr = S_OK; for(size_t i = 0; i < cMapElements; i++) { switch(pMap[i].eAttributeType) { case AttributeType_AudioSubtype: IFC( TryLoadAudioSubtypeAttribute(pLoader, pMap[i].szName, pAttributes, pMap[i].gidAttribute) ); break; case AttributeType_VideoSubtype: IFC( TryLoadVideoSubtypeAttribute(pLoader, pMap[i].szName, pAttributes, pMap[i].gidAttribute) ); break; case AttributeType_UINT32: IFC( TryLoadUINT32Attribute(pLoader, pMap[i].szName, pAttributes, pMap[i].gidAttribute) ); break; case AttributeType_String: IFC( TryLoadStringAttribute(pLoader, pMap[i].szName, pAttributes, pMap[i].gidAttribute) ); break; case AttributeType_Ratio: IFC( TryLoadRatioAttribute(pLoader, pMap[i].szName, pMap[i].szSecondName, pAttributes, pMap[i].gidAttribute) ); break; } } Cleanup: return hr; } HRESULT CTedTranscodeTopologyBuilder::StringToContainerType(LPCWSTR szName, GUID* pgidContainerType) { return FindGuidInMap(szName, pgidContainerType, m_kaContainerMap, ContainerMapSize); } HRESULT CTedTranscodeTopologyBuilder::StringToAudioSubtype(LPCWSTR szSubtype, GUID* pgidSubtype) { return FindGuidInMap(szSubtype, pgidSubtype, m_kaAudioSubtypeMap, AudioSubtypeMapSize); } HRESULT CTedTranscodeTopologyBuilder::StringToVideoSubtype(LPCWSTR szSubtype, GUID* pgidSubtype) { return FindGuidInMap(szSubtype, pgidSubtype, m_kaVideoSubtypeMap, VideoSubtypeMapSize); } HRESULT CTedTranscodeTopologyBuilder::FindGuidInMap(LPCWSTR szName, GUID* pgidValue, const StringGuidMap* pMap, DWORD cMapElements) { HRESULT hr = MF_E_NOT_FOUND; for(DWORD i = 0; i < cMapElements; i++) { if(wcscmp(szName, pMap[i].szName) == 0) { *pgidValue = pMap[i].gidValue; hr = S_OK; } } return hr; } HRESULT CTedTranscodeTopologyBuilder::TryLoadUINT32Attribute(ITedDataLoader* pLoader, LPCWSTR szName, IMFAttributes* pAttributes, GUID gidAttributeName) { HRESULT hr; LPWSTR szData = NULL; hr = pLoader->LoadData(szName, &szData, 0); if(SUCCEEDED(hr)) { UINT32 unValue = wcstoul(szData, NULL, 10); IFC( pAttributes->SetUINT32(gidAttributeName, unValue) ); } hr = S_OK; Cleanup: SAFE_COTASKMEMFREE( szData ); return hr; } HRESULT CTedTranscodeTopologyBuilder::TryLoadRatioAttribute(ITedDataLoader* pLoader, LPCWSTR szNameFirst, LPCWSTR szNameSecond, IMFAttributes* pAttributes, GUID gidAttributeName) { HRESULT hr; LPWSTR szData = NULL; hr = pLoader->LoadData(szNameFirst, &szData, 0); if(SUCCEEDED(hr)) { UINT32 unFirstValue = wcstoul(szData, NULL, 10); SAFE_COTASKMEMFREE( szData ); IFC( pLoader->LoadData(szNameSecond, &szData, 0) ); UINT32 unSecondValue = wcstoul(szData, NULL, 10); IFC( MFSetAttributeRatio(pAttributes, gidAttributeName, unFirstValue, unSecondValue) ); } hr = S_OK; Cleanup: SAFE_COTASKMEMFREE( szData ); return hr; } HRESULT CTedTranscodeTopologyBuilder::TryLoadStringAttribute(ITedDataLoader* pLoader, LPCWSTR szName, IMFAttributes* pAttributes, GUID gidAttributeName) { HRESULT hr; LPWSTR szData = NULL; hr = pLoader->LoadData(szName, &szData, 0); if(SUCCEEDED(hr)) { IFC( pAttributes->SetString(gidAttributeName, szData) ); } hr = S_OK; Cleanup: SAFE_COTASKMEMFREE( szData ); return hr; } HRESULT CTedTranscodeTopologyBuilder::TryLoadAudioSubtypeAttribute(ITedDataLoader* pLoader, LPCWSTR szName, IMFAttributes* pAttributes, GUID gidAttributeName) { HRESULT hr; LPWSTR szData = NULL; hr = pLoader->LoadData(szName, &szData, 0); if(SUCCEEDED(hr)) { GUID gidSubtype; IFC( StringToAudioSubtype(szData, &gidSubtype) ); IFC( pAttributes->SetGUID(gidAttributeName, gidSubtype) ); } hr = S_OK; Cleanup: SAFE_COTASKMEMFREE( szData ); return hr; } HRESULT CTedTranscodeTopologyBuilder::TryLoadVideoSubtypeAttribute(ITedDataLoader* pLoader, LPCWSTR szName, IMFAttributes* pAttributes, GUID gidAttributeName) { HRESULT hr; LPWSTR szData = NULL; hr = pLoader->LoadData(szName, &szData, 0); if(SUCCEEDED(hr)) { GUID gidSubtype; IFC( StringToVideoSubtype(szData, &gidSubtype) ); IFC( pAttributes->SetGUID(gidAttributeName, gidSubtype) ); } hr = S_OK; Cleanup: SAFE_COTASKMEMFREE( szData ); return hr; } HRESULT CTedTranscodeTopologyBuilder::FillProfileWithSourceType(IMFTranscodeProfile* pProfile) { HRESULT hr; CComPtr spAudioAttributes; CComPtr spVideoAttributes; IFC( pProfile->GetAudioAttributes(&spAudioAttributes) ); if(spAudioAttributes) { CComPtr spmtAudio; IFC( GetSourceMediaType(MFMediaType_Audio, &spmtAudio) ); if(spmtAudio) { IFC( CopyDesiredAttributes(spmtAudio, spAudioAttributes, m_kaAudioAttributeMap, AudioAttributeMapSize) ); } } IFC( pProfile->GetVideoAttributes(&spVideoAttributes) ); if(spVideoAttributes) { CComPtr spmtVideo; IFC( GetSourceMediaType(MFMediaType_Video, &spmtVideo) ); if(spmtVideo) { IFC( CopyDesiredAttributes(spmtVideo, spVideoAttributes, m_kaVideoAttributeMap, VideoAttributeMapSize) ); } } Cleanup: return hr; } HRESULT CTedTranscodeTopologyBuilder::GetSourceMediaType(REFGUID gidMajorType, IMFMediaType** ppMediaType) { HRESULT hr; CComPtr spPD; DWORD cSDs; IFC( m_spSource->CreatePresentationDescriptor(&spPD) ); IFC( spPD->GetStreamDescriptorCount(&cSDs) ); for(DWORD i = 0; i < cSDs; i++) { CComPtr spSD; CComPtr spMTH; GUID gidMTHMajorType; BOOL fSelected; IFC( spPD->GetStreamDescriptorByIndex(i, &fSelected, &spSD) ); IFC( spSD->GetMediaTypeHandler(&spMTH) ); IFC( spMTH->GetMajorType(&gidMTHMajorType) ); if(gidMTHMajorType == gidMajorType) { IFC( spMTH->GetCurrentMediaType(ppMediaType) ); } } Cleanup: return hr; } HRESULT CTedTranscodeTopologyBuilder::CopyDesiredAttributes(IMFMediaType* pSourceType, IMFAttributes* pTargetAttributes, const StringAttributeMap* pMap, size_t cMapElements) { HRESULT hr = S_OK; for(size_t i = 0; i < cMapElements; i++) { PROPVARIANT varValue; PropVariantInit(&varValue); hr = pSourceType->GetItem(pMap[i].gidAttribute, &varValue); if(SUCCEEDED(hr)) { if(FAILED(pTargetAttributes->GetItem(pMap[i].gidAttribute, NULL))) { pTargetAttributes->SetItem(pMap[i].gidAttribute, varValue); } } IFC( PropVariantClear(&varValue) ); } Cleanup: return hr; }