Search code examples
c++audiowindows-8windows-runtimems-media-foundation

Windows 8 passing a Stream to a MFSourceReader


I am trying to make a program that can take an .mp3 file from the user's Music Library, and convert it using the Windows Media Foundation's IMFSourceReader into an array containing PCM values for some exciting DSP action. However, I'm having problems.

My solution has two projects; a C#/XAML one containing UI code and a C++ Library containing some native code for working with Media Foundation and XAudio2 plus some public ref classes which the C# code can interact with.

I can't use MFCreateSourceReaderFromURL because it returns an ACCESS DENIED HRESULT even if you have the Music Library capability in the app manifest. So it looks like I'm going to have to use either MFCreateSourceReaderFromByteStream or MFCreateSourceReaderFromMediaSource. Now, it's easy enough to access the music file using Windows.Storage.KnownFolders.MusicLibrary and get a Stream or an IInputStream along with a few other stream types but how can I convert this to an IMFByteStream or an IMFMediaSource or just in some way decode the mp3 to a byte array with PCM values?

I have also entertained the idea of resaving the music file in the app folder so that I can use MFCreateSourceReaderFromURL or perhaps it would be possible to bypass working with Media Foundation directly and use something like Windows.Media.Transcoding to do the conversion to PCM. I'm not sure this is what the Transcoding namespace is for though.

I have spent hours reading through the Media Foundation documentation and I found a glimmer of hope with MFCreateMFByteStreamOnStream which can convert from COM's IStream to a byte stream but I will need to find/write a wrapper to convert my .net stream to an IStream. Before I set off on this task I just want to make sure that I am going in the right direction or is there a better way to do this?

Thanks in advance. Also apologies in advance if I have made a stupid mistake or missed some crucial aspect of working with these libraries.


Solution

  • OK everyone, I know you are all clamouring to answer this question but after 2 days of headscratching I have found a solution. You can pass an Windows::Storage::Streams::IRandomAccessStream^ object to MFCreateMFByteStreamOnStreamEx ( http://msdn.microsoft.com/en-us/library/windows/desktop/hh162754(v=vs.85).aspx ) by casting it to (IUnknown*) like so:

    ComPtr<IMFByteStream> spMFByteStream = nullptr; 
    
    MFCreateMFByteStreamOnStreamEx((IUnknown*)streamHandle, &spMFByteStream);
    
    //remember to add your own HRESULT error handling code
    
    ComPtr<IMFSourceReader> _sourceReader = nullptr;
    
    MFCreateSourceReaderFromByteStream(spMFByteStream.Get(),nullptr,&_sourceReader);
    

    With streamHandle being the IRandomAcessStream^ that you want to give to the Source Reader. Here is a code sample with it running

    Time for some toast.