Introduction
This code converts audio from MP3 or WMA into PCM or WAV format, on Windows 8. It's written in VB but the same techniques apply to C#. It can convert file:///
files stored locally on the hard disk, and also works with http://
files from the internet, and also mms://
streams.
It uses the Media Foundation Source Reader API.
Windows comes with built-in decoders for MP3 and WMA formats. It exposes these decoders through several APIs:
- MediaElement - All UI frameworks have
a
MediaElement
control - XAML for Windows 8, WPF, Silverlight, WinForms. They let you play audio and video. But they don't offer a way to get at the raw audio data.
- MediaTranscoder - This API was introduced in Windows 8. It lets you transcode audio and video, e.g. changing video resolution, but its only audio target formats are MP3 and WMA: no WAV. There's an SDK-sample showing how to use it.
- Audio Compression Manager - This legacy C++ API exposes decoding of MP3 files,
but it's not allowed in Windows Store apps.
- Windows Media Format SDK - This C++/COM API is for all things WMA-related,
but it's not allowed in Windows Store apps.
- Media Foundation Source Reader - The Media Foundation C++/COM API,
introduced in Vista, is the successor to the old DirectShow. "Source Reader" is a standalone decoding part of it. Microsoft
have a C++ sample showing how to use it to convert to WAV;
this current article is largely based on that sample. Tamir Khason wrote a great article about using some parts of Media Foundation
from C#: http://www.codeproject.com/Articles/239378/Video-encoder-and-metadata-reading-by-using-Window
- XAudio2 - This C++/COM API is the successor
to the old DirectSound. It's aimed at game developers.
NAudio. As a .NET developer, if you want audio, your first port of call should normally be http://naudio.codeplex.com/.
NAudio is a high quality library, available through NuGet, licensed under MS-Pl, for adding audio to VB and C# apps.
I wrote this article because (as of September 2012) NAudio is built on the Audio Compression Manager and Windows Media Format APIs, neither of which
are allowed in Windows Store apps. I needed to find a different solution.
Using the Code
COM interop and getting started
The IMFSourceReader
COM object is the entry point into the Media Foundation Source Reader API. The APIs are only exposed by Microsoft to C++/COM.
If you want to use them from VB or C# you need a lot of COM PInvoke interop.
<MTAThread>
Sub Main()
MFStartup(MF_VERSION)
Dim pReader As IMFSourceReader = Nothing
MFCreateSourceReaderFromURL(source, Nothing, pReader)
...
Runtime.InteropServices.Marshal.ReleaseComObject(pReader) : pReader = Nothing
MFShutdown()
End Sub
In an audio application, you deal with large quantities of data, and it's important to release resources in a timely fashion. VB and C# use the
IDispose
mechanism
for this, and rely on .NET garbage collection for everything else. C++/COM uses
IUnknown.Release
and reference-counting instead. It's difficult to bridge
the gap between the two. For background information I suggest this MSDN article "Improving
Interop Performance" and this C++ Team Blog article "Mixing deterministic
and non-deterministic cleanup" and this blog by Lim Bio Liong "RCW Internal
Reference Count".
We will be concerned with the COM object created in the above code, which maintains an reference count, and the .NET Runtime Callable Wrapper (RCW) for it which
has its own internal reference count. There's a one-to-one relationship between an RCW and a COM object's
IUnknown
IntPtr
. The first time the IUnknown IntPtr
enters
managed code (e.g. through allocating a new COM object, or Marshal.ObjectFromIUnknown
) then the RCW is created, its internal reference count is set to 1, and it calls
IUnknown.AddRef
just once. On subsequent times that the same IUnknown
IntPtr
enters managed code, then the RCW's internal reference count is incremented, but it doesn't
call IUnknown.AddRef
. The RCW's internal reference count gets decremented through natural garbage collection; you can also decrement it manually with
Marshal.ReleaseComObject
,
or force it straight to 0 with Marshal.FinalReleaseComObject
. When the RCW's internal reference count drops to 0 then it calls IUnknown.Release just once. Any further methods
on it will fail with the message "COM object that has been separated from its underlying RCW cannot be used".
Suggested practice: Make a wrapper class which implements IDisposable
and which has a private field for the COM object (or more precisely, for its RCW).
Never let your clients have direct access to the COM object. It's fine for local variables in your wrapper-class to reference the same RCW if needed. In
IDisposable.Dispose
,
call Marshal.ReleaseComObject
, set the field to Nothing, and make sure that your wrapper never accesses methods or fields of that field again. I haven't done
that for this current article: I wanted to show how to use the Media Foundation APIs themselves, and leave it to a COM expert to write a good wrapper.
Permissions to read local files on Windows 8
In desktop apps, the above call to MFCreateSourceReaderFromURL
will work fine for file:/// URLs, and for http:// and mms:// URLs, and you can also pass it relative or absolute filenames.
On Windows 8 app-store apps, MFCreateSourceReaderFromURL
will work for internet http:// and mms:// URLs (if you have Internet Client privileges), and for ms-appx:/// URLs. But if you try it on filenames or file:/// URLs you'll get an access-denied error, even for files that you have access permissions to. Instead for local files you have to use MFCreateMFByteStreamOnStreamEx
:
Dim pReader As IMFSourceReader = Nothing
Dim src_file = Await Windows.Storage.StorageFile.GetFileFromPathAsync(src_uri.LocalPath)
Using src_stream = Await src_file.OpenAsync(Windows.Storage.FileAccessMode.Read)
Dim src_bytestream As IMFByteStream = Nothing
MFCreateMFByteStreamOnStreamEx(src_stream, src_bytestream)
MFCreateSourceReaderFromByteStream(src_bytestream, Nothing, pReader)
Runtime.InteropServices.Marshal.ReleaseComObject(src_bytestream)
End Using
...
Runtime.InteropServices.Marshal.ReleaseComObject(pReader) : pReader = Nothing
Picking the right audio format
We request the IMFSourceReader
if it's able to give us decoded audio in the format we want (PCM data, 44100Hz, 2 channels, 16 bits per sample).
It then tells us which media format it ended up picking for us.
pReader.SetStreamSelection(MF_SOURCE_READER_ALL_STREAMS, False)
pReader.SetStreamSelection(MF_SOURCE_READER_FIRST_AUDIO_STREAM, True)
Dim pRequestedType As IMFMediaType = Nothing : MFCreateMediaType(pRequestedType)
pRequestedType.SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio)
pRequestedType.SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM)
pRequestedType.SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, 2)
pRequestedType.SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16)
pRequestedType.SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100)
pReader.SetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, Nothing, pRequestedType)
Runtime.InteropServices.Marshal.ReleaseComObject(pRequestedType) : pRequestedType = Nothing
Dim pAudioType As IMFMediaType = Nothing : pReader.GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, pAudioType)
Dim cbBlockSize = 0 : pAudioType.GetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, cbBlockSize)
Dim cbBytesPerSecond = 0 : pAudioType.GetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, cbBytesPerSecond)
Dim pWav As IntPtr, cbFormat As Integer : MFCreateWaveFormatExFromMFMediaType(pAudioType, pWav, cbFormat)
Dim wfx = New Byte(cbFormat - 1) {} : Runtime.InteropServices.Marshal.Copy(pWav, wfx, 0, cbFormat)
pReader.SetStreamSelection(MF_SOURCE_READER_FIRST_AUDIO_STREAM, True)
Runtime.InteropServices.Marshal.ReleaseComObject(pAudioType) : pAudioType = Nothing
Writing the WAV file. Wav files have a straightforward format: a simple header, followed by the raw PCM data.
Dim header As Byte() =
{CByte(AscW("R"c)), CByte(AscW("I"c)), CByte(AscW("F"c)), CByte(AscW("F"c)), 0, 0, 0, 0,
CByte(AscW("W"c)), CByte(AscW("A"c)), CByte(AscW("V"c)), CByte(AscW("E"c)),
CByte(AscW("f"c)), CByte(AscW("m"c)), CByte(AscW("t"c)), CByte(AscW(" "c)),
CByte(cbFormat And 255), CByte((cbFormat >> 8) And 255),
CByte((cbFormat >> 16) And 255), CByte((cbFormat >> 24) And 255)}
Dim dataHeader As Byte() =
{CByte(AscW("d"c)), CByte(AscW("a"c)), _
CByte(AscW("t"c)), CByte(AscW("a"c)), 0, 0, 0, 0}
Await dest.WriteAsync(header, 0, header.Length)
Await dest.WriteAsync(wfx, 0, wfx.Length)
Await dest.WriteAsync(dataHeader, 0, dataHeader.Length)
Runtime.InteropServices.Marshal.FreeCoTaskMem(pWav)
Dim cbHeader = header.Length + cbFormat + dataHeader.Length
Dim cbAudioData = 0
Dim cbRiffFileSize = cbHeader + cbAudioData - 8
dest.Seek(4, IO.SeekOrigin.Begin) : Await dest.WriteAsync(BitConverter.GetBytes(cbRiffFileSize), 0, 4)
dest.Seek(cbHeader - 4, IO.SeekOrigin.Begin) : Await dest.WriteAsync(BitConverter.GetBytes(cbAudioData), 0, 4)
Getting PCM Data out of IMFSourceReader
All that remains is the loop to get audio data out of IMFSourceReader
:
Do
Dim pSample As IMFSample = Nothing, dwFlags As Integer : _
pReader.ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, 0, dwFlags, 0, pSample)
If dwFlags <> 0 Then pSample = Nothing : Exit Do
Dim pBuffer As IMFMediaBuffer = Nothing : pSample.ConvertToContiguousBuffer(pBuffer)
Dim pAudioData As IntPtr, cbBuffer As Integer : pBuffer.Lock(pAudioData, Nothing, cbBuffer)
Dim buf = New Byte(cbBuffer - 1) {} : Runtime.InteropServices.Marshal.Copy(pAudioData, buf, 0, cbBuffer)
Await dest.WriteAsync(buf, 0, cbBuffer) : cbAudioData += cbBuffer
pBuffer.Unlock()
Runtime.InteropServices.Marshal.ReleaseComObject(pBuffer) : pBuffer = Nothing
Runtime.InteropServices.Marshal.ReleaseComObject(pSample) : pSample = Nothing
Loop
Invoke interop libraries for Media Foundation
All that's left is a huge pinvoke interop library. It took me several days to piece all this together.
I'm not a pinvoke expert by any means. I bet there are bugs in this definitions, and I'm sure they don't embody best-practice.
Module Interop
<Runtime.InteropServices.DllImport("mfplat.dll", ExactSpelling:=True, PreserveSig:=False)>
Sub MFStartup(Version As Integer, Optional dwFlags As Integer = 0)
End Sub
<Runtime.InteropServices.DllImport("mfplat.dll", ExactSpelling:=True, PreserveSig:=False)>
Public Sub MFShutdown()
End Sub
<Runtime.InteropServices.DllImport("mfplat.dll", ExactSpelling:=True, PreserveSig:=False)>
Public Sub MFCreateMediaType(ByRef ppMFType As IMFMediaType)
End Sub
<Runtime.InteropServices.DllImport("mfplat.dll", ExactSpelling:=True, PreserveSig:=False)>
Public Sub MFCreateWaveFormatExFromMFMediaType(pMFType As IMFMediaType, _
ByRef ppWF As IntPtr, ByRef pcbSize As Integer, Optional Flags As Integer = 0)
End Sub
<Runtime.InteropServices.DllImport("mfreadwrite.dll", ExactSpelling:=True, PreserveSig:=False)>
Public Sub MFCreateSourceReaderFromURL(<Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPWStr)> pwszURL As String, _
pAttributes As IntPtr, ByRef ppSourceReader As IMFSourceReader)
End Sub
<Runtime.InteropServices.DllImport("mfreadwrite.dll", ExactSpelling:=True, PreserveSig:=False)>
Public Sub MFCreateSourceReaderFromByteStream(pByteStream As IMFByteStream, _
pAttributes As IntPtr, ByRef ppSourceReader As IMFSourceReader)
End Sub
<Runtime.InteropServices.DllImport("mfplat.dll", ExactSpelling:=True, PreserveSig:=False)>
Public Sub MFCreateMFByteStreamOnStreamEx(<Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.IUnknown)> punkStream As Object, _
ByRef ppByteStream As IMFByteStream)
End Sub
Public Const MF_SOURCE_READER_ALL_STREAMS As Integer = &HFFFFFFFE
Public Const MF_SOURCE_READER_FIRST_AUDIO_STREAM As Integer = &HFFFFFFFD
Public Const MF_SDK_VERSION As Integer = &H2
Public Const MF_API_VERSION As Integer = &H70
Public Const MF_VERSION As Integer = (MF_SDK_VERSION << 16) Or MF_API_VERSION
Public ReadOnly MF_MT_MAJOR_TYPE As New Guid("48eba18e-f8c9-4687-bf11-0a74c9f96a8f")
Public ReadOnly MF_MT_SUBTYPE As New Guid("f7e34c9a-42e8-4714-b74b-cb29d72c35e5")
Public ReadOnly MF_MT_AUDIO_BLOCK_ALIGNMENT As New Guid("322de230-9eeb-43bd-ab7a-ff412251541d")
Public ReadOnly MF_MT_AUDIO_AVG_BYTES_PER_SECOND As New Guid("1aab75c8-cfef-451c-ab95-ac034b8e1731")
Public ReadOnly MF_MT_AUDIO_NUM_CHANNELS As New Guid("37e48bf5-645e-4c5b-89de-ada9e29b696a")
Public ReadOnly MF_MT_AUDIO_SAMPLES_PER_SECOND As New Guid("5faeeae7-0290-4c31-9e8a-c534f68d9dba")
Public ReadOnly MF_MT_AUDIO_BITS_PER_SAMPLE As New Guid("f2deb57f-40fa-4764-aa33-ed4f2d1ff669")
Public ReadOnly MFMediaType_Audio As New Guid("73647561-0000-0010-8000-00AA00389B71")
Public ReadOnly MFAudioFormat_PCM As New Guid("00000001-0000-0010-8000-00AA00389B71")
<Runtime.InteropServices.ComImport, Runtime.InteropServices.InterfaceType( _
Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown), _
Runtime.InteropServices.Guid("70ae66f2-c809-4e4f-8915-bdcb406b7993")>
Public Interface IMFSourceReader
Sub GetStreamSelection(<Runtime.InteropServices.In> _
dwStreamIndex As Integer, <Runtime.InteropServices.Out> ByRef pSelected As Boolean)
Sub SetStreamSelection(<Runtime.InteropServices.In> _
dwStreamIndex As Integer, <Runtime.InteropServices.In> pSelected As Boolean)
Sub GetNativeMediaType(<Runtime.InteropServices.In> dwStreamIndex As Integer, _
<Runtime.InteropServices.In> dwMediaTypeIndex As Integer, _
<Runtime.InteropServices.Out> ByRef ppMediaType As IntPtr)
Sub GetCurrentMediaType(<Runtime.InteropServices.In> dwStreamIndex As Integer, _
<Runtime.InteropServices.Out> ByRef ppMediaType As IMFMediaType)
Sub SetCurrentMediaType(<Runtime.InteropServices.In> dwStreamIndex _
As Integer, pdwReserved As IntPtr, <Runtime.InteropServices.In> pMediaType As IMFMediaType)
Sub SetCurrentPosition(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidTimeFormat As Guid, <Runtime.InteropServices.In> varPosition As Object)
Sub ReadSample(<Runtime.InteropServices.In> dwStreamIndex As Integer, _
<Runtime.InteropServices.In> dwControlFlags As Integer, _
<Runtime.InteropServices.Out> ByRef pdwActualStreamIndex As Integer, _
<Runtime.InteropServices.Out> ByRef pdwStreamFlags As Integer, _
<Runtime.InteropServices.Out> ByRef pllTimestamp As UInt64, _
<Runtime.InteropServices.Out> ByRef ppSample As IMFSample)
Sub Flush(<Runtime.InteropServices.In> dwStreamIndex As Integer)
Sub GetServiceForStream(<Runtime.InteropServices.In> dwStreamIndex As Integer, _
<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidService As Guid, _
<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> riid As Guid, _
<Runtime.InteropServices.Out> ByRef ppvObject As IntPtr)
Sub GetPresentationAttribute(<Runtime.InteropServices.In> _
dwStreamIndex As Integer, <Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidAttribute As Guid, <Runtime.InteropServices.Out> pvarAttribute As IntPtr)
End Interface
<Runtime.InteropServices.ComImport, Runtime.InteropServices.InterfaceType( _
Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown), _
Runtime.InteropServices.Guid("2CD2D921-C447-44A7-A13C-4ADABFC247E3")>
Public Interface IMFAttributes
Sub GetItem(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, pValue As IntPtr)
Sub GetItemType(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, ByRef pType As Integer)
Sub CompareItem(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, _
Value As IntPtr, <Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.Bool)> ByRef pbResult As Boolean)
Sub Compare(<Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.Interface)> _
pTheirs As IMFAttributes, MatchType As Integer, _
<Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.Bool)> ByRef pbResult As Boolean)
Sub GetUINT32(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, ByRef punValue As Integer)
Sub GetUINT64(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, ByRef punValue As Long)
Sub GetDouble(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, ByRef pfValue As Double)
Sub GetGUID(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, ByRef pguidValue As Guid)
Sub GetStringLength(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, ByRef pcchLength As Integer)
Sub GetString(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, _
<Runtime.InteropServices.Out, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPWStr)> pwszValue As Text.StringBuilder, _
cchBufSize As Integer, ByRef pcchLength As Integer)
Sub GetAllocatedString(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, <Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPWStr)> ByRef ppwszValue As String, ByRef pcchLength As Integer)
Sub GetBlobSize(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, ByRef pcbBlobSize As Integer)
Sub GetBlob(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, _
<Runtime.InteropServices.Out, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPArray)> pBuf As Byte(), _
cbBufSize As Integer, ByRef pcbBlobSize As Integer)
Sub GetAllocatedBlob(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, ByRef ip As IntPtr, ByRef pcbSize As Integer)
Sub GetUnknown(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, _
<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> riid As Guid, _
<Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.IUnknown)> ByRef ppv As Object)
Sub SetItem(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, Value As IntPtr)
Sub DeleteItem(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid)
Sub DeleteAllItems()
Sub SetUINT32(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, unValue As Integer)
Sub SetUINT64(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, unValue As Long)
Sub SetDouble(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, fValue As Double)
Sub SetGUID(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, _
<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidValue As Guid)
Sub SetString(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, _
<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPWStr)> wszValue As String)
Sub SetBlob(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, _
<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPArray, SizeParamIndex:=2)> pBuf As Byte(), cbBufSize As Integer)
Sub SetUnknown(<Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, <Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.IUnknown)> pUnknown As Object)
Sub LockStore()
Sub UnlockStore()
Sub GetCount(ByRef pcItems As Integer)
Sub GetItemByIndex(unIndex As Integer, ByRef pguidKey As Guid, pValue As IntPtr)
Sub CopyAllItems(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.Interface)> pDest As IMFAttributes)
End Interface
<Runtime.InteropServices.ComImport, Runtime.InteropServices.InterfaceType( _
Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown), _
Runtime.InteropServices.Guid("44AE0FA8-EA31-4109-8D2E-4CAE4997C555")>
Public Interface IMFMediaType
Inherits IMFAttributes
Overloads Sub GetItem(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, pValue As IntPtr)
Overloads Sub GetItemType(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, ByRef pType As Integer)
Overloads Sub CompareItem(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, Value As IntPtr, <Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.Bool)> ByRef pbResult As Boolean)
Overloads Sub Compare(<Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.Interface)> _
pTheirs As IMFAttributes, MatchType As Integer, _
<Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.Bool)> ByRef pbResult As Boolean)
Overloads Sub GetUINT32(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, ByRef punValue As Integer)
Overloads Sub GetUINT64(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, ByRef punValue As Long)
Overloads Sub GetDouble(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, ByRef pfValue As Double)
Overloads Sub GetGUID(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, ByRef pguidValue As Guid)
Overloads Sub GetStringLength(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, ByRef pcchLength As Integer)
Overloads Sub GetString(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, _
<Runtime.InteropServices.Out, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPWStr)> pwszValue _
As Text.StringBuilder, cchBufSize As Integer, ByRef pcchLength As Integer)
Overloads Sub GetAllocatedString(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, <Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPWStr)> _
ByRef ppwszValue As String, ByRef pcchLength As Integer)
Overloads Sub GetBlobSize(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, ByRef pcbBlobSize As Integer)
Overloads Sub GetBlob(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, <Runtime.InteropServices.Out, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPArray)> pBuf As Byte(), _
cbBufSize As Integer, ByRef pcbBlobSize As Integer)
Overloads Sub GetAllocatedBlob(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, ByRef ip As IntPtr, ByRef pcbSize As Integer)
Overloads Sub GetUnknown(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, <Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> riid As Guid, _
<Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.IUnknown)> ByRef ppv As Object)
Overloads Sub SetItem(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, Value As IntPtr)
Overloads Sub DeleteItem(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid)
Overloads Sub DeleteAllItems()
Overloads Sub SetUINT32(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, unValue As Integer)
Overloads Sub SetUINT64(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, unValue As Long)
Overloads Sub SetDouble(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, fValue As Double)
Overloads Sub SetGUID(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, _
<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidValue As Guid)
Overloads Sub SetString(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, _
<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPWStr)> wszValue As String)
Overloads Sub SetBlob(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, _
<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPArray, SizeParamIndex:=2)> pBuf As Byte(), cbBufSize As Integer)
Overloads Sub SetUnknown(<Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, _
<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.IUnknown)> pUnknown As Object)
Overloads Sub LockStore()
Overloads Sub UnlockStore()
Overloads Sub GetCount(ByRef pcItems As Integer)
Overloads Sub GetItemByIndex(unIndex As Integer, ByRef pguidKey As Guid, pValue As IntPtr)
Overloads Sub CopyAllItems(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.Interface)> pDest As IMFAttributes)
Sub GetMajorType(ByRef pguidMajorType As Guid)
Sub IsCompressedFormat(<Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.Bool)> ByRef pfCompressed As Boolean)
<Runtime.InteropServices.PreserveSig> Function IsEqual(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.Interface)> _
pIMediaType As IMFMediaType, ByRef pdwFlags As Integer) As Integer
Sub GetRepresentation(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.Struct)> guidRepresentation As Guid, _
ByRef ppvRepresentation As IntPtr)
Sub FreeRepresentation(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.Struct)> _
guidRepresentation As Guid, <Runtime.InteropServices.In> pvRepresentation As IntPtr)
End Interface
<Runtime.InteropServices.ComImport, Runtime.InteropServices.InterfaceType( _
Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown), _
Runtime.InteropServices.Guid("c40a00f2-b93a-4d80-ae8c-5a1c634f58e4")>
Public Interface IMFSample
Inherits IMFAttributes
Overloads Sub GetItem(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, pValue As IntPtr)
Overloads Sub GetItemType(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, ByRef pType As Integer)
Overloads Sub CompareItem(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, _
Value As IntPtr, <Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.Bool)> ByRef pbResult As Boolean)
Overloads Sub Compare(<Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.Interface)> _
pTheirs As IMFAttributes, MatchType As Integer, _
<Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.Bool)> ByRef pbResult As Boolean)
Overloads Sub GetUINT32(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, ByRef punValue As Integer)
Overloads Sub GetUINT64(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, ByRef punValue As Long)
Overloads Sub GetDouble(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, ByRef pfValue As Double)
Overloads Sub GetGUID(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, ByRef pguidValue As Guid)
Overloads Sub GetStringLength(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, ByRef pcchLength As Integer)
Overloads Sub GetString(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, _
<Runtime.InteropServices.Out, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPWStr)> _
pwszValue As Text.StringBuilder, cchBufSize As Integer, ByRef pcchLength As Integer)
Overloads Sub GetAllocatedString(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, <Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPWStr)> _
ByRef ppwszValue As String, ByRef pcchLength As Integer)
Overloads Sub GetBlobSize(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, ByRef pcbBlobSize As Integer)
Overloads Sub GetBlob(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, _
<Runtime.InteropServices.Out, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPArray)> pBuf As Byte(), _
cbBufSize As Integer, ByRef pcbBlobSize As Integer)
Overloads Sub GetAllocatedBlob(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, ByRef ip As IntPtr, ByRef pcbSize As Integer)
Overloads Sub GetUnknown(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, _
<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> riid As Guid, _
<Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.IUnknown)> ByRef ppv As Object)
Overloads Sub SetItem(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, Value As IntPtr)
Overloads Sub DeleteItem(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid)
Overloads Sub DeleteAllItems()
Overloads Sub SetUINT32(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, unValue As Integer)
Overloads Sub SetUINT64(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, unValue As Long)
Overloads Sub SetDouble(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, fValue As Double)
Overloads Sub SetGUID(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, _
<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidValue As Guid)
Overloads Sub SetString(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> _
guidKey As Guid, <Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPWStr)> _
wszValue As String)
Overloads Sub SetBlob(<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, _
<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPArray, SizeParamIndex:=2)> pBuf As Byte(), cbBufSize As Integer)
Overloads Sub SetUnknown(<Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.LPStruct)> guidKey As Guid, _
<Runtime.InteropServices.In, Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.IUnknown)> pUnknown As Object)
Overloads Sub LockStore()
Overloads Sub UnlockStore()
Overloads Sub GetCount(ByRef pcItems As Integer)
Overloads Sub GetItemByIndex(unIndex As Integer, ByRef pguidKey As Guid, pValue As IntPtr)
Overloads Sub CopyAllItems(<Runtime.InteropServices.In, _
Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.Interface)> pDest As IMFAttributes)
Sub GetSampleFlags(ByRef pdwSampleFlags As Integer)
Sub SetSampleFlags(dwSampleFlags As Integer)
Sub GetSampleTime(ByRef phnsSampletime As Long)
Sub SetSampleTime(hnsSampleTime As Long)
Sub GetSampleDuration(ByRef phnsSampleDuration As Long)
Sub SetSampleDuration(hnsSampleDuration As Long)
Sub GetBufferCount(ByRef pdwBufferCount As Integer)
Sub GetBufferByIndex(dwIndex As Integer, ByRef ppBuffer As IMFMediaBuffer)
Sub ConvertToContiguousBuffer(ByRef ppBuffer As IMFMediaBuffer)
Sub AddBuffer(pBuffer As IMFMediaBuffer)
Sub RemoveBuferByindex(dwIndex As Integer)
Sub RemoveAllBuffers()
Sub GetTotalLength(ByRef pcbTotalLength As Integer)
Sub CopyToByffer(pBuffer As IMFMediaBuffer)
End Interface
<Runtime.InteropServices.ComImport, Runtime.InteropServices.InterfaceType( _
Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown), _
Runtime.InteropServices.Guid("045FA593-8799-42b8-BC8D-8968C6453507")>
Public Interface IMFMediaBuffer
Sub Lock(ByRef ppbBuffer As IntPtr, ByRef pcbMaxLength As Integer, ByRef pcbCurrentLength As Integer)
Sub Unlock()
Sub GetCurrentLength(ByRef pcbCurrentLength As Integer)
Sub SetCurrentLength(cbCurrentLength As Integer)
Sub GetMaxLength(ByRef pcbMaxLength As Integer)
End Interface
<Runtime.InteropServices.ComImport, Runtime.InteropServices.InterfaceType( _
Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown), _
Runtime.InteropServices.Guid("ad4c1b00-4bf7-422f-9175-756693d9130d")>
Public Interface IMFByteStream
Sub GetCapabilities(ByRef pdwCapabiities As Integer)
Sub GetLength(ByRef pqwLength As Long)
Sub SetLength(qwLength As Long)
Sub GetCurrentPosition(ByRef pqwPosition As Long)
Sub SetCurrentPosition(qwPosition As Long)
Sub IsEndOfStream(<Runtime.InteropServices.MarshalAs( _
Runtime.InteropServices.UnmanagedType.Bool)> ByRef pfEndOfStream As Boolean)
Sub Read(pb As IntPtr, cb As Integer, ByRef pcbRead As Integer)
Sub BeginRead(pb As IntPtr, cb As Integer, pCallback As IntPtr, punkState As IntPtr)
Sub EndRead(pResult As IntPtr, ByRef pcbRead As Integer)
Sub Write(pb As IntPtr, cb As Integer, ByRef pcbWritten As Integer)
Sub BeginWrite(pb As IntPtr, cb As Integer, pCallback As IntPtr, punkState As IntPtr)
Sub EndWrite(pResult As IntPtr, ByRef pcbWritten As Integer)
Sub Seek(SeekOrigin As Integer, llSeekOffset As Long, dwSeekFlags As Integer, ByRef pqwCurrentPosition As Long)
Sub Flush()
Sub Close()
End Interface
End Module
Notes
Using Await with MediaElement
In the accompanying code, I provide a Windows 8 app-store app that demonstrates
IMFSourceReader
. It uses a MediaElement
to play the WAV file that we produce.
Normally you deal with MediaElements by signing up event-handlers for their Completed events. I don't like callbacks or event-handlers. I much prefer
to Await
a MediaElement
. So I created my own helper function:
Await MediaElement.EndedAsync()
MediaElement1.SetSource(stream, "audio/wav")
MediaElement1.Play()
Await MediaElement1.EndedAsync()
The new VB/C# Await
and Async
keywords, in Visual Studio 2012, let you make any type awaitable even if it isn't already awaitable itself through you
providing a helper function. Here's mine:
<Extension>
Function EndedAsync(m As MediaElement) As Task
Dim tcs As New TaskCompletionSource(Of Object)
Dim handlerEnded As RoutedEventHandler = Nothing
Dim handlerChanged As RoutedEventHandler = Nothing
Dim handlerFailed As ExceptionRoutedEventHandler = Nothing
handlerEnded =
Sub(s, e)
RemoveHandler m.MediaEnded, handlerEnded
RemoveHandler m.CurrentStateChanged, handlerChanged
RemoveHandler m.MediaFailed, handlerFailed
tcs.TrySetResult(Nothing)
End Sub
handlerChanged =
Sub(s, e)
If m.CurrentState <> MediaElementState.Closed AndAlso m.CurrentState _
<> MediaElementState.Stopped Then Return
RemoveHandler m.MediaEnded, handlerEnded
RemoveHandler m.CurrentStateChanged, handlerChanged
RemoveHandler m.MediaFailed, handlerFailed
tcs.TrySetResult(Nothing)
End Sub
handlerFailed =
Sub(s, e)
RemoveHandler m.MediaEnded, handlerEnded
RemoveHandler m.CurrentStateChanged, handlerChanged
RemoveHandler m.MediaFailed, handlerFailed
tcs.TrySetException(New Exception(e.ErrorMessage))
End Sub
AddHandler m.MediaEnded, handlerEnded
AddHandler m.CurrentStateChanged, handlerChanged
AddHandler m.MediaFailed, handlerFailed
If m.CurrentState = MediaElementState.Closed OrElse m.CurrentState = _
MediaElementState.Stopped Then tcs.TrySetResult(Nothing)
Return tcs.Task
End Function
Disclaimer: although I work at Microsoft on the VB/C# language team, this article is strictly an personal amateur effort based on public information
and experimentation - it's not in my professional area of expertise, is written in my own free time not as a representative of Microsoft,
and neither Microsoft nor I make any claims about its correctness.