SampleFormatMediaFoundation.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. /*
  2. * SampleFormatMediaSoundation.cpp
  3. * -------------------------------
  4. * Purpose: MediaFoundation sample import.
  5. * Notes :
  6. * Authors: Joern Heusipp
  7. * OpenMPT Devs
  8. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  9. */
  10. #include "stdafx.h"
  11. #include "Sndfile.h"
  12. #ifndef MODPLUG_NO_FILESAVE
  13. #include "../common/mptFileIO.h"
  14. #endif
  15. #include "../common/misc_util.h"
  16. #include "Tagging.h"
  17. #include "Loaders.h"
  18. #include "../common/FileReader.h"
  19. #include "modsmp_ctrl.h"
  20. #include "openmpt/soundbase/Copy.hpp"
  21. #include "../soundlib/ModSampleCopy.h"
  22. #include "../common/ComponentManager.h"
  23. #if defined(MPT_WITH_MEDIAFOUNDATION)
  24. #include <windows.h>
  25. #include <atlbase.h>
  26. #include <mfapi.h>
  27. #include <mfidl.h>
  28. #include <mfreadwrite.h>
  29. #include <mferror.h>
  30. #include <Propvarutil.h>
  31. #endif // MPT_WITH_MEDIAFOUNDATION
  32. OPENMPT_NAMESPACE_BEGIN
  33. #if defined(MPT_WITH_MEDIAFOUNDATION)
  34. struct PropVariant : PROPVARIANT
  35. {
  36. PropVariant() { PropVariantInit(this); }
  37. ~PropVariant() { PropVariantClear(this); }
  38. };
  39. // Implementing IMFByteStream is apparently not enough to stream raw bytes
  40. // data to MediaFoundation.
  41. // Additionally, one has to also implement a custom IMFAsyncResult for the
  42. // BeginRead/EndRead interface which allows transferring the number of read
  43. // bytes around.
  44. // To make things even worse, MediaFoundation fails to detect some AAC and MPEG
  45. // files if a non-file-based or read-only stream is used for opening.
  46. // The only sane option which remains if we do not have an on-disk filename
  47. // available:
  48. // 1 - write a temporary file
  49. // 2 - close it
  50. // 3 - open it using MediaFoundation.
  51. // We use FILE_ATTRIBUTE_TEMPORARY which will try to keep the file data in
  52. // memory just like regular allocated memory and reduce the overhead basically
  53. // to memcpy.
  54. static FileTags ReadMFMetadata(IMFMediaSource *mediaSource)
  55. {
  56. FileTags tags;
  57. CComPtr<IMFPresentationDescriptor> presentationDescriptor;
  58. if(!SUCCEEDED(mediaSource->CreatePresentationDescriptor(&presentationDescriptor)))
  59. {
  60. return tags;
  61. }
  62. DWORD streams = 0;
  63. if(!SUCCEEDED(presentationDescriptor->GetStreamDescriptorCount(&streams)))
  64. {
  65. return tags;
  66. }
  67. CComPtr<IMFMetadataProvider> metadataProvider;
  68. if(!SUCCEEDED(MFGetService(mediaSource, MF_METADATA_PROVIDER_SERVICE, IID_IMFMetadataProvider, (void**)&metadataProvider)))
  69. {
  70. return tags;
  71. }
  72. CComPtr<IMFMetadata> metadata;
  73. if(!SUCCEEDED(metadataProvider->GetMFMetadata(presentationDescriptor, 0, 0, &metadata)))
  74. {
  75. return tags;
  76. }
  77. PropVariant varPropNames;
  78. if(!SUCCEEDED(metadata->GetAllPropertyNames(&varPropNames)))
  79. {
  80. return tags;
  81. }
  82. for(DWORD propIndex = 0; propIndex < varPropNames.calpwstr.cElems; ++propIndex)
  83. {
  84. PropVariant propVal;
  85. LPWSTR propName = varPropNames.calpwstr.pElems[propIndex];
  86. if(S_OK != metadata->GetProperty(propName, &propVal))
  87. {
  88. break;
  89. }
  90. std::wstring stringVal;
  91. #if !MPT_OS_WINDOWS_WINRT
  92. // WTF, no PropVariantToString() in WinRT
  93. std::vector<WCHAR> wcharVal(256);
  94. for(;;)
  95. {
  96. HRESULT hrToString = PropVariantToString(propVal, wcharVal.data(), mpt::saturate_cast<UINT>(wcharVal.size()));
  97. if(hrToString == S_OK)
  98. {
  99. stringVal = wcharVal.data();
  100. break;
  101. } else if(hrToString == ERROR_INSUFFICIENT_BUFFER)
  102. {
  103. wcharVal.resize(mpt::exponential_grow(wcharVal.size()));
  104. } else
  105. {
  106. break;
  107. }
  108. }
  109. #endif // !MPT_OS_WINDOWS_WINRT
  110. if(stringVal.length() > 0)
  111. {
  112. if(propName == std::wstring(L"Author")) tags.artist = mpt::ToUnicode(stringVal);
  113. if(propName == std::wstring(L"Title")) tags.title = mpt::ToUnicode(stringVal);
  114. if(propName == std::wstring(L"WM/AlbumTitle")) tags.album = mpt::ToUnicode(stringVal);
  115. if(propName == std::wstring(L"WM/Track")) tags.trackno = mpt::ToUnicode(stringVal);
  116. if(propName == std::wstring(L"WM/Year")) tags.year = mpt::ToUnicode(stringVal);
  117. if(propName == std::wstring(L"WM/Genre")) tags.genre = mpt::ToUnicode(stringVal);
  118. }
  119. }
  120. return tags;
  121. }
  122. class ComponentMediaFoundation : public ComponentLibrary
  123. {
  124. MPT_DECLARE_COMPONENT_MEMBERS(ComponentMediaFoundation, "MediaFoundation")
  125. public:
  126. ComponentMediaFoundation()
  127. : ComponentLibrary(ComponentTypeSystem)
  128. {
  129. return;
  130. }
  131. bool DoInitialize() override
  132. {
  133. #if !MPT_OS_WINDOWS_WINRT
  134. if(!(true
  135. && AddLibrary("mf", mpt::LibraryPath::System(P_("mf")))
  136. && AddLibrary("mfplat", mpt::LibraryPath::System(P_("mfplat")))
  137. && AddLibrary("mfreadwrite", mpt::LibraryPath::System(P_("mfreadwrite")))
  138. && AddLibrary("propsys", mpt::LibraryPath::System(P_("propsys")))
  139. ))
  140. {
  141. return false;
  142. }
  143. #endif // !MPT_OS_WINDOWS_WINRT
  144. if(!SUCCEEDED(MFStartup(MF_VERSION)))
  145. {
  146. return false;
  147. }
  148. return true;
  149. }
  150. virtual ~ComponentMediaFoundation()
  151. {
  152. if(IsAvailable())
  153. {
  154. MFShutdown();
  155. }
  156. }
  157. };
  158. #endif // MPT_WITH_MEDIAFOUNDATION
  159. #ifdef MODPLUG_TRACKER
  160. std::vector<FileType> CSoundFile::GetMediaFoundationFileTypes()
  161. {
  162. std::vector<FileType> result;
  163. #if defined(MPT_WITH_MEDIAFOUNDATION)
  164. ComponentHandle<ComponentMediaFoundation> mf;
  165. if(!IsComponentAvailable(mf))
  166. {
  167. return result;
  168. }
  169. std::map<std::wstring, FileType> guidMap;
  170. HKEY hkHandlers = NULL;
  171. LSTATUS regResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows Media Foundation\\ByteStreamHandlers", 0, KEY_READ, &hkHandlers);
  172. if(regResult != ERROR_SUCCESS)
  173. {
  174. return result;
  175. }
  176. for(DWORD handlerIndex = 0; ; ++handlerIndex)
  177. {
  178. WCHAR handlerTypeBuf[256];
  179. MemsetZero(handlerTypeBuf);
  180. regResult = RegEnumKeyW(hkHandlers, handlerIndex, handlerTypeBuf, 256);
  181. if(regResult != ERROR_SUCCESS)
  182. {
  183. break;
  184. }
  185. std::wstring handlerType = handlerTypeBuf;
  186. if(handlerType.length() < 1)
  187. {
  188. continue;
  189. }
  190. HKEY hkHandler = NULL;
  191. regResult = RegOpenKeyExW(hkHandlers, handlerTypeBuf, 0, KEY_READ, &hkHandler);
  192. if(regResult != ERROR_SUCCESS)
  193. {
  194. continue;
  195. }
  196. std::vector<WCHAR> valueNameBuf(16384);
  197. std::vector<BYTE> valueData(16384);
  198. for(DWORD valueIndex = 0; ; ++valueIndex)
  199. {
  200. std::fill(valueNameBuf.begin(), valueNameBuf.end(), WCHAR{0});
  201. DWORD valueNameBufLen = 16384;
  202. DWORD valueType = 0;
  203. std::fill(valueData.begin(), valueData.end(), BYTE{0});
  204. DWORD valueDataLen = 16384;
  205. regResult = RegEnumValueW(hkHandler, valueIndex, valueNameBuf.data(), &valueNameBufLen, NULL, &valueType, valueData.data(), &valueDataLen);
  206. if(regResult != ERROR_SUCCESS)
  207. {
  208. break;
  209. }
  210. if(valueNameBufLen <= 0 || valueType != REG_SZ || valueDataLen <= 0)
  211. {
  212. continue;
  213. }
  214. std::wstring guid = std::wstring(valueNameBuf.data());
  215. mpt::ustring description = mpt::ToUnicode(ParseMaybeNullTerminatedStringFromBufferWithSizeInBytes<std::wstring>(valueData.data(), valueDataLen));
  216. description = mpt::replace(description, U_("Byte Stream Handler"), U_("Files"));
  217. description = mpt::replace(description, U_("ByteStreamHandler"), U_("Files"));
  218. guidMap[guid]
  219. .ShortName(U_("mf"))
  220. .Description(description)
  221. ;
  222. if(handlerType[0] == L'.')
  223. {
  224. guidMap[guid].AddExtension(mpt::PathString::FromWide(handlerType.substr(1)));
  225. } else
  226. {
  227. guidMap[guid].AddMimeType(mpt::ToCharset(mpt::Charset::ASCII, handlerType));
  228. }
  229. }
  230. RegCloseKey(hkHandler);
  231. hkHandler = NULL;
  232. }
  233. RegCloseKey(hkHandlers);
  234. hkHandlers = NULL;
  235. for(const auto &it : guidMap)
  236. {
  237. result.push_back(it.second);
  238. }
  239. #endif // MPT_WITH_MEDIAFOUNDATION
  240. return result;
  241. }
  242. #endif // MODPLUG_TRACKER
  243. bool CSoundFile::ReadMediaFoundationSample(SAMPLEINDEX sample, FileReader &file, bool mo3Decode)
  244. {
  245. #if !defined(MPT_WITH_MEDIAFOUNDATION)
  246. MPT_UNREFERENCED_PARAMETER(sample);
  247. MPT_UNREFERENCED_PARAMETER(file);
  248. MPT_UNREFERENCED_PARAMETER(mo3Decode);
  249. return false;
  250. #else
  251. ComponentHandle<ComponentMediaFoundation> mf;
  252. if(!IsComponentAvailable(mf))
  253. {
  254. return false;
  255. }
  256. file.Rewind();
  257. // When using MF to decode MP3 samples in MO3 files, we need the mp3 file extension
  258. // for some of them or otherwise MF refuses to recognize them.
  259. mpt::PathString tmpfileExtension = (mo3Decode ? P_("mp3") : P_("tmp"));
  260. OnDiskFileWrapper diskfile(file, tmpfileExtension);
  261. if(!diskfile.IsValid())
  262. {
  263. return false;
  264. }
  265. #define MPT_MF_CHECKED(x) do { \
  266. HRESULT hr = (x); \
  267. if(!SUCCEEDED(hr)) \
  268. { \
  269. return false; \
  270. } \
  271. } while(0)
  272. CComPtr<IMFSourceResolver> sourceResolver;
  273. MPT_MF_CHECKED(MFCreateSourceResolver(&sourceResolver));
  274. MF_OBJECT_TYPE objectType = MF_OBJECT_INVALID;
  275. CComPtr<IUnknown> unknownMediaSource;
  276. MPT_MF_CHECKED(sourceResolver->CreateObjectFromURL(diskfile.GetFilename().ToWide().c_str(), MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE | MF_RESOLUTION_READ, NULL, &objectType, &unknownMediaSource));
  277. if(objectType != MF_OBJECT_MEDIASOURCE)
  278. {
  279. return false;
  280. }
  281. CComPtr<IMFMediaSource> mediaSource;
  282. MPT_MF_CHECKED(unknownMediaSource->QueryInterface(&mediaSource));
  283. FileTags tags = ReadMFMetadata(mediaSource);
  284. CComPtr<IMFSourceReader> sourceReader;
  285. MPT_MF_CHECKED(MFCreateSourceReaderFromMediaSource(mediaSource, NULL, &sourceReader));
  286. CComPtr<IMFMediaType> partialType;
  287. MPT_MF_CHECKED(MFCreateMediaType(&partialType));
  288. MPT_MF_CHECKED(partialType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio));
  289. MPT_MF_CHECKED(partialType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM));
  290. MPT_MF_CHECKED(sourceReader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, NULL, partialType));
  291. CComPtr<IMFMediaType> uncompressedAudioType;
  292. MPT_MF_CHECKED(sourceReader->GetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, &uncompressedAudioType));
  293. MPT_MF_CHECKED(sourceReader->SetStreamSelection((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, TRUE));
  294. UINT32 numChannels = 0;
  295. MPT_MF_CHECKED(uncompressedAudioType->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &numChannels));
  296. UINT32 samplesPerSecond = 0;
  297. MPT_MF_CHECKED(uncompressedAudioType->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &samplesPerSecond));
  298. UINT32 bitsPerSample = 0;
  299. MPT_MF_CHECKED(uncompressedAudioType->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, &bitsPerSample));
  300. if(numChannels <= 0 || numChannels > 2)
  301. {
  302. return false;
  303. }
  304. if(samplesPerSecond <= 0)
  305. {
  306. return false;
  307. }
  308. if(bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 && bitsPerSample != 32)
  309. {
  310. return false;
  311. }
  312. std::vector<char> rawData;
  313. for(;;)
  314. {
  315. CComPtr<IMFSample> mfSample;
  316. DWORD mfSampleFlags = 0;
  317. CComPtr<IMFMediaBuffer> buffer;
  318. MPT_MF_CHECKED(sourceReader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, NULL, &mfSampleFlags, NULL, &mfSample));
  319. if(mfSampleFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)
  320. {
  321. break;
  322. }
  323. if(mfSampleFlags & MF_SOURCE_READERF_ENDOFSTREAM)
  324. {
  325. break;
  326. }
  327. MPT_MF_CHECKED(mfSample->ConvertToContiguousBuffer(&buffer));
  328. {
  329. BYTE *data = NULL;
  330. DWORD dataSize = 0;
  331. MPT_MF_CHECKED(buffer->Lock(&data, NULL, &dataSize));
  332. mpt::append(rawData, mpt::byte_cast<char*>(data), mpt::byte_cast<char*>(data + dataSize));
  333. MPT_MF_CHECKED(buffer->Unlock());
  334. if(rawData.size() / numChannels / (bitsPerSample / 8) > MAX_SAMPLE_LENGTH)
  335. {
  336. break;
  337. }
  338. }
  339. }
  340. std::string sampleName = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags));
  341. if(rawData.size() / numChannels / (bitsPerSample / 8) > MAX_SAMPLE_LENGTH)
  342. {
  343. return false;
  344. }
  345. SmpLength length = mpt::saturate_cast<SmpLength>(rawData.size() / numChannels / (bitsPerSample/8));
  346. DestroySampleThreadsafe(sample);
  347. if(!mo3Decode)
  348. {
  349. m_szNames[sample] = sampleName;
  350. Samples[sample].Initialize();
  351. Samples[sample].nC5Speed = samplesPerSecond;
  352. }
  353. Samples[sample].nLength = length;
  354. Samples[sample].uFlags.set(CHN_16BIT, bitsPerSample >= 16);
  355. Samples[sample].uFlags.set(CHN_STEREO, numChannels == 2);
  356. Samples[sample].AllocateSample();
  357. if(!Samples[sample].HasSampleData())
  358. {
  359. return false;
  360. }
  361. if(bitsPerSample == 24)
  362. {
  363. if(numChannels == 2)
  364. {
  365. CopyStereoInterleavedSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt24<0, littleEndian24> > >(Samples[sample], rawData.data(), rawData.size());
  366. } else
  367. {
  368. CopyMonoSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt24<0, littleEndian24> > >(Samples[sample], rawData.data(), rawData.size());
  369. }
  370. } else if(bitsPerSample == 32)
  371. {
  372. if(numChannels == 2)
  373. {
  374. CopyStereoInterleavedSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt32<0, littleEndian32> > >(Samples[sample], rawData.data(), rawData.size());
  375. } else
  376. {
  377. CopyMonoSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt32<0, littleEndian32> > >(Samples[sample], rawData.data(), rawData.size());
  378. }
  379. } else
  380. {
  381. // just copy
  382. std::copy(rawData.data(), rawData.data() + rawData.size(), mpt::byte_cast<char*>(Samples[sample].sampleb()));
  383. }
  384. #undef MPT_MF_CHECKED
  385. if(!mo3Decode)
  386. {
  387. Samples[sample].Convert(MOD_TYPE_IT, GetType());
  388. Samples[sample].PrecomputeLoops(*this, false);
  389. }
  390. return true;
  391. #endif
  392. }
  393. OPENMPT_NAMESPACE_END