/// <summary> /// Creates the reader (overridable by ) /// </summary> protected virtual IMFSourceReader CreateReader2(byte[] buffer, MediaFoundationReaderSettings settings) { IMFSourceReader reader = null; //MediaFoundationInterop.MFCreateSourceReaderFromURL(file, null, out reader); reader = MediaFoundationApi.CreateSourceReaderFromByteStream(MediaFoundationApi.CreateByteStream(new ComStream(new MemoryStream(buffer)))); reader.SetStreamSelection(MediaFoundationInterop.MF_SOURCE_READER_ALL_STREAMS, false); reader.SetStreamSelection(MediaFoundationInterop.MF_SOURCE_READER_FIRST_AUDIO_STREAM, true); // Create a partial media type indicating that we want uncompressed PCM audio var partialMediaType = new MediaType(); partialMediaType.MajorType = MediaTypes.MFMediaType_Audio; partialMediaType.SubType = settings.RequestFloatOutput ? AudioSubtypes.MFAudioFormat_Float : AudioSubtypes.MFAudioFormat_PCM; var currentMediaType = GetCurrentMediaType(reader); // mono, low sample rate files can go wrong on Windows 10 unless we specify here partialMediaType.ChannelCount = currentMediaType.ChannelCount; partialMediaType.SampleRate = currentMediaType.SampleRate; try { // set the media type // can return MF_E_INVALIDMEDIATYPE if not supported reader.SetCurrentMediaType(MediaFoundationInterop.MF_SOURCE_READER_FIRST_AUDIO_STREAM, IntPtr.Zero, partialMediaType.MediaFoundationObject); } catch (COMException ex) when(ex.GetHResult() == MediaFoundationErrors.MF_E_INVALIDMEDIATYPE) { // HE-AAC (and v2) seems to halve the samplerate if (currentMediaType.SubType == AudioSubtypes.MFAudioFormat_AAC && currentMediaType.ChannelCount == 1) { partialMediaType.SampleRate = currentMediaType.SampleRate *= 2; partialMediaType.ChannelCount = currentMediaType.ChannelCount *= 2; reader.SetCurrentMediaType(MediaFoundationInterop.MF_SOURCE_READER_FIRST_AUDIO_STREAM, IntPtr.Zero, partialMediaType.MediaFoundationObject); } else { throw; } } Marshal.ReleaseComObject(currentMediaType.MediaFoundationObject); return(reader); }
/// <summary> /// Sets the media type for a stream. /// </summary> /// <param name="sourceReader">A valid IMFSourceReader instance.</param></param></param> /// <param name="streamIndex">The stream to configure.</param> /// <param name="mediaType">An instance of the IMFMediaType interface of the media type.</param> /// <returns>If this function succeeds, it returns the S_OK member. Otherwise, it returns another HResult's member that describe the error.</returns> public static HResult SetCurrentMediaType(this IMFSourceReader sourceReader, SourceReaderFirstStream streamIndex, IMFMediaType mediaType) { if (sourceReader == null) { throw new ArgumentNullException("sourceReader"); } return(sourceReader.SetCurrentMediaType((int)streamIndex, null, mediaType)); }
protected override IMFSourceReader CreateReader(MediaFoundationReader.MediaFoundationReaderSettings settings) { IMFSourceReader expr_15 = MediaFoundationApi.CreateSourceReaderFromByteStream(MediaFoundationApi.CreateByteStream(new ComStream(this.stream))); expr_15.SetStreamSelection(-2, false); expr_15.SetStreamSelection(-3, true); expr_15.SetCurrentMediaType(-3, IntPtr.Zero, new MediaType { MajorType = MediaTypes.MFMediaType_Audio, SubType = settings.RequestFloatOutput ? AudioSubtypes.MFAudioFormat_Float : AudioSubtypes.MFAudioFormat_PCM }.MediaFoundationObject); return(expr_15); }
public static WaveFormat GetCurrentWaveFormat(byte[] buffer) { MediaFoundationApi.Startup(); var settings = new MediaFoundationReaderSettings(); IMFSourceReader reader = null; //MediaFoundationInterop.MFCreateSourceReaderFromURL(file, null, out reader); reader = MediaFoundationApi.CreateSourceReaderFromByteStream(MediaFoundationApi.CreateByteStream(new ComStream(new MemoryStream(buffer)))); reader.SetStreamSelection(MediaFoundationInterop.MF_SOURCE_READER_ALL_STREAMS, false); reader.SetStreamSelection(MediaFoundationInterop.MF_SOURCE_READER_FIRST_AUDIO_STREAM, true); // Create a partial media type indicating that we want uncompressed PCM audio var partialMediaType = new MediaType(); partialMediaType.MajorType = MediaTypes.MFMediaType_Audio; partialMediaType.SubType = settings.RequestFloatOutput ? AudioSubtypes.MFAudioFormat_Float : AudioSubtypes.MFAudioFormat_PCM; var currentMediaType = GetCurrentMediaType(reader); // mono, low sample rate files can go wrong on Windows 10 unless we specify here partialMediaType.ChannelCount = currentMediaType.ChannelCount; partialMediaType.SampleRate = currentMediaType.SampleRate; try { // set the media type // can return MF_E_INVALIDMEDIATYPE if not supported reader.SetCurrentMediaType(MediaFoundationInterop.MF_SOURCE_READER_FIRST_AUDIO_STREAM, IntPtr.Zero, partialMediaType.MediaFoundationObject); } catch (COMException ex) when(ex.GetHResult() == MediaFoundationErrors.MF_E_INVALIDMEDIATYPE) { // HE-AAC (and v2) seems to halve the samplerate if (currentMediaType.SubType == AudioSubtypes.MFAudioFormat_AAC && currentMediaType.ChannelCount == 1) { partialMediaType.SampleRate = currentMediaType.SampleRate *= 2; partialMediaType.ChannelCount = currentMediaType.ChannelCount *= 2; reader.SetCurrentMediaType(MediaFoundationInterop.MF_SOURCE_READER_FIRST_AUDIO_STREAM, IntPtr.Zero, partialMediaType.MediaFoundationObject); } else { throw; } } IMFMediaType uncompressedMediaType; reader.GetCurrentMediaType(MediaFoundationInterop.MF_SOURCE_READER_FIRST_AUDIO_STREAM, out uncompressedMediaType); // Two ways to query it, first is to ask for properties (second is to convert into WaveFormatEx using MFCreateWaveFormatExFromMFMediaType) var outputMediaType = new MediaType(uncompressedMediaType); Guid actualMajorType = outputMediaType.MajorType; Debug.Assert(actualMajorType == MediaTypes.MFMediaType_Audio); Guid audioSubType = outputMediaType.SubType; int channels = outputMediaType.ChannelCount; int bits = outputMediaType.BitsPerSample; int sampleRate = outputMediaType.SampleRate; Marshal.ReleaseComObject(currentMediaType.MediaFoundationObject); //Marshal.ReleaseComObject(reader); if (audioSubType == AudioSubtypes.MFAudioFormat_PCM) { return(new WaveFormat(sampleRate, bits, channels)); } if (audioSubType == AudioSubtypes.MFAudioFormat_Float) { return(WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, channels)); } var subTypeDescription = FieldDescriptionHelper.Describe(typeof(AudioSubtypes), audioSubType); throw new InvalidDataException(String.Format("Unsupported audio sub Type {0}", subTypeDescription)); }
private static void InitializeSourceReader(string inputUrl, out IMFSourceReader reader, out int videoStreamIndex, out int audioStreamIndex) { Marshal.ThrowExceptionForHR((int)MFExtern.MFCreateAttributes(out IMFAttributes attributes, 0)); try { Marshal.ThrowExceptionForHR((int)attributes.SetUINT32(MFAttributesClsid.MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, 1)); Marshal.ThrowExceptionForHR((int)attributes.SetUINT32(MFAttributesClsid.MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, 1)); //Marshal.ThrowExceptionForHR((int)attributes.SetUINT32(MFAttributesClsid.MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, 1)); Marshal.ThrowExceptionForHR((int)MFExtern.MFCreateSourceReaderFromURL(inputUrl, attributes, out reader)); } finally { Marshal.ReleaseComObject(attributes); } try { GetSourceReaderStreamIndex(reader, out videoStreamIndex, out audioStreamIndex); if (videoStreamIndex != -1) { // Set the video input media type. Marshal.ThrowExceptionForHR((int)MFExtern.MFCreateMediaType(out IMFMediaType videoMediaTypeIn)); try { Marshal.ThrowExceptionForHR((int)videoMediaTypeIn.SetGUID(MFAttributesClsid.MF_MT_MAJOR_TYPE, MFMediaType.Video)); Marshal.ThrowExceptionForHR((int)videoMediaTypeIn.SetGUID(MFAttributesClsid.MF_MT_SUBTYPE, MFMediaType.RGB32)); Marshal.ThrowExceptionForHR((int)reader.SetCurrentMediaType(videoStreamIndex, null, videoMediaTypeIn)); } finally { Marshal.ReleaseComObject(videoMediaTypeIn); } } if (audioStreamIndex != -1) { // Set the audio input media type. Marshal.ThrowExceptionForHR((int)MFExtern.MFCreateMediaType(out IMFMediaType audioMediaTypeIn)); try { Marshal.ThrowExceptionForHR((int)audioMediaTypeIn.SetGUID(MFAttributesClsid.MF_MT_MAJOR_TYPE, MFMediaType.Audio)); Marshal.ThrowExceptionForHR((int)audioMediaTypeIn.SetGUID(MFAttributesClsid.MF_MT_SUBTYPE, MFMediaType.PCM)); Marshal.ThrowExceptionForHR((int)audioMediaTypeIn.SetUINT32(MFAttributesClsid.MF_MT_AUDIO_BITS_PER_SAMPLE, 16)); Marshal.ThrowExceptionForHR((int)audioMediaTypeIn.SetUINT32(MFAttributesClsid.MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050)); Marshal.ThrowExceptionForHR((int)audioMediaTypeIn.SetUINT32(MFAttributesClsid.MF_MT_AUDIO_NUM_CHANNELS, 2)); Marshal.ThrowExceptionForHR((int)audioMediaTypeIn.SetUINT32(MFAttributesClsid.MF_MT_AUDIO_BLOCK_ALIGNMENT, 4)); Marshal.ThrowExceptionForHR((int)audioMediaTypeIn.SetUINT32(MFAttributesClsid.MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 4 * 22050)); Marshal.ThrowExceptionForHR((int)reader.SetCurrentMediaType(audioStreamIndex, audioMediaTypeIn)); } finally { Marshal.ReleaseComObject(audioMediaTypeIn); } } } catch { Marshal.ReleaseComObject(reader); throw; } }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Does everything to copy a file. Opens the Source Reader and Sink Writer /// configures the streams and then, because a Synchronous version /// of the Source Reader is used, we sit in a loop and perform the copy. /// /// Any errors here simply throw an exception and must be trapped elsewhere /// /// Note that because this code is intended for demo purposes, it has been /// kept very simple and linear. Most of the things that could have been /// refactored in a common procedure (audio and video streams) are simply /// written out in duplicate in order to make it obvious what is going on. /// </summary> /// <param name="sourceFileName">the source file name</param> /// <param name="outputFileName">the name of the output file</param> /// <history> /// 01 Nov 18 Cynic - Originally Written /// </history> public void CopyFile(string sourceFileName, string outputFileName) { HResult hr; int sinkWriterOutputVideoStreamId = -1; int videoSamplesProcessed = 0; bool videoStreamIsAtEOS = false; int sourceReaderVideoStreamId = -1; int sinkWriterOutputAudioStreamId = -1; int audioSamplesProcessed = 0; bool audioStreamIsAtEOS = false; int sourceReaderAudioStreamId = -1; // not keen on endless loops. This is the maximum number // of streams we will check in the source reader. const int MAX_SOURCEREADER_STREAMS = 100; // create the SourceReader sourceReader = TantaWMFUtils.CreateSourceReaderSyncFromFile(sourceFileName, DEFAULT_ALLOW_HARDWARE_TRANSFORMS); if (sourceReader == null) { // we failed throw new Exception("CopyFile: Failed to create SourceReader, Nothing will work."); } // create the SinkWriter sinkWriter = TantaWMFUtils.CreateSinkWriterFromFile(outputFileName, DEFAULT_ALLOW_HARDWARE_TRANSFORMS); if (sinkWriter == null) { // we failed throw new Exception("CopyFile: Failed to create Sink Writer, Nothing will work."); } // find the first audio and video stream and identify the default Media Type // they are using. We could look into the streams and enumerate all of the // types on offer and choose one from the list - but for a copy operation // the default will be quite suitable. sourceReaderNativeVideoMediaType = null; sourceReaderNativeAudioMediaType = null; for (int streamIndex = 0; streamIndex < MAX_SOURCEREADER_STREAMS; streamIndex++) { IMFMediaType workingType = null; Guid guidMajorType = Guid.Empty; // if we have found both the video and audio types (and their stream ids) leave now if ((sourceReaderNativeVideoMediaType != null) && (sourceReaderNativeAudioMediaType != null)) { break; } hr = sourceReader.GetNativeMediaType(streamIndex, 0, out workingType); if (hr == HResult.MF_E_NO_MORE_TYPES) { break; } if (hr == HResult.MF_E_INVALIDSTREAMNUMBER) { break; } if (hr != HResult.S_OK) { // we failed throw new Exception("CopyFile: failed on call to GetNativeMediaType, retVal=" + hr.ToString()); } if (workingType == null) { // we failed throw new Exception("CopyFile: failed on call to GetNativeMediaType, workingType == null"); } // what major type does this stream have? hr = workingType.GetMajorType(out guidMajorType); if (hr != HResult.S_OK) { throw new Exception("CopyFile: call to workingType.GetMajorType failed. Err=" + hr.ToString()); } if (guidMajorType == null) { throw new Exception("CopyFile: call to workingType.GetMajorType failed. guidMajorType == null"); } // test for video or audio (there can be others) if ((guidMajorType == MFMediaType.Video) && (sourceReaderNativeVideoMediaType == null)) { // this stream represents a video type sourceReaderNativeVideoMediaType = workingType; sourceReaderVideoStreamId = streamIndex; // the sourceReaderNativeVideoMediaType will be released elsewhere continue; } else if ((guidMajorType == MFMediaType.Audio) && (sourceReaderNativeAudioMediaType == null)) { // this stream represents a audio type sourceReaderNativeAudioMediaType = workingType; sourceReaderAudioStreamId = streamIndex; // the sourceReaderNativeAudioMediaType will be released elsewhere continue; } // if we get here release the type - we do not use it if (workingType != null) { Marshal.ReleaseComObject(workingType); workingType = null; } } // at this point we expect we can have a native video or a native audio media type // or both, but not neither. if we don't we cannot carry on if ((sourceReaderNativeVideoMediaType == null) && (sourceReaderNativeAudioMediaType == null)) { // we failed throw new Exception("CopyFile: failed on call to GetNativeMediaType, sourceReaderNativeVideoMediaType == null"); } // if we have a video stream in the source file we now set it up if (sourceReaderNativeVideoMediaType != null) { // set the media type on the reader - this is the media type it will output hr = sourceReader.SetCurrentMediaType(sourceReaderVideoStreamId, null, sourceReaderNativeVideoMediaType); if (hr != HResult.S_OK) { // we failed throw new Exception("CopyFile: failed on call to SetCurrentMediaType(v), retVal=" + hr.ToString()); } // add a stream to the sink writer. The mediaType specifies the format of the samples that will be written // to the file. Note that it does not necessarily need to match the input format. To set the input format // use SetInputMediaType. In this case it does match because we copied the video encoder information directly // out of the video source type hr = sinkWriter.AddStream(sourceReaderNativeVideoMediaType, out sinkWriterOutputVideoStreamId); if (hr != HResult.S_OK) { // we failed throw new Exception("CopyFile: Failed adding the output stream(v), retVal=" + hr.ToString()); } // Set the input format for a stream on the sink writer. Note the use of the stream index here // The input format does not have to match the target format that is written to the media sink // If the formats do not match, this call attempts to load an transform // that can convert from the input format to the target format. If it cannot find one, and this is not // a sure thing, it will throw an exception. hr = sinkWriter.SetInputMediaType(sinkWriterOutputVideoStreamId, sourceReaderNativeVideoMediaType, null); if (hr != HResult.S_OK) { // we failed throw new Exception("CopyFile: Failed on calling SetInputMediaType(v) on the writer, retVal=" + hr.ToString()); } } // if we have an audio stream in the source file we now set it up if (sourceReaderNativeAudioMediaType != null) { // set the media type on the reader - this is the media type it will output hr = sourceReader.SetCurrentMediaType(sourceReaderAudioStreamId, null, sourceReaderNativeAudioMediaType); if (hr != HResult.S_OK) { // we failed throw new Exception("CopyFile: failed on call to SetCurrentMediaType(a), retVal=" + hr.ToString()); } // add a stream to the sink writer. The mediaType specifies the format of the samples that will be written // to the file. Note that it does not necessarily need to match the input format. To set the input format // use SetInputMediaType. In this case it does match because we copied the video encoder information directly // out of the video source type hr = sinkWriter.AddStream(sourceReaderNativeAudioMediaType, out sinkWriterOutputAudioStreamId); if (hr != HResult.S_OK) { // we failed throw new Exception("CopyFile: Failed adding the output stream(a), retVal=" + hr.ToString()); } // Set the input format for a stream on the sink writer. Note the use of the stream index here // The input format does not have to match the target format that is written to the media sink // If the formats do not match, this call attempts to load an transform // that can convert from the input format to the target format. If it cannot find one, and this is not // a sure thing, it will throw an exception. hr = sinkWriter.SetInputMediaType(sinkWriterOutputAudioStreamId, sourceReaderNativeAudioMediaType, null); if (hr != HResult.S_OK) { // we failed throw new Exception("CopyFile: Failed on calling SetInputMediaType(a) on the writer, retVal=" + hr.ToString()); } } // begin writing on the sink writer hr = sinkWriter.BeginWriting(); if (hr != HResult.S_OK) { // we failed throw new Exception("CopyFile: failed on call to BeginWriting, retVal=" + hr.ToString()); } // we sit in a loop here and get the sample from the source reader and write it out // to the sink writer. An EOS (end of sample) value in the flags will signal the end. while (true) { int actualStreamIndex; MF_SOURCE_READER_FLAG actualStreamFlags; long timeStamp = 0; IMFSample workingMediaSample = null; // Request the next sample from the media source. Note that this could be // any type of media sample (video, audio, subtitles etc). We do not know // until we look at the stream ID. We saved the stream ID earlier when // we obtained the media types and so we can branch based on that. hr = sourceReader.ReadSample( TantaWMFUtils.MF_SOURCE_READER_ANY_STREAM, 0, out actualStreamIndex, out actualStreamFlags, out timeStamp, out workingMediaSample ); if (hr != HResult.S_OK) { // we failed throw new Exception("CopyFile: Failed on calling the ReadSample on the reader, retVal=" + hr.ToString()); } // the sample may be null if either end of stream or a stream tick is returned if (workingMediaSample == null) { // just ignore, the flags will have the information we need. } else { // the sample is not null if (actualStreamIndex == sourceReaderAudioStreamId) { // audio data // ensure discontinuity is set for the first sample in each stream if (audioSamplesProcessed == 0) { // audio data hr = workingMediaSample.SetUINT32(MFAttributesClsid.MFSampleExtension_Discontinuity, 1); if (hr != HResult.S_OK) { // we failed throw new Exception("CopyFile: Failed on calling SetUINT32 on the sample, retVal=" + hr.ToString()); } // remember this - we only do it once audioSamplesProcessed++; } hr = sinkWriter.WriteSample(sinkWriterOutputAudioStreamId, workingMediaSample); if (hr != HResult.S_OK) { // we failed throw new Exception("CopyFile: Failed on calling the WriteSample on the writer, retVal=" + hr.ToString()); } } else if (actualStreamIndex == sourceReaderVideoStreamId) { // video data // ensure discontinuity is set for the first sample in each stream if (videoSamplesProcessed == 0) { // video data hr = workingMediaSample.SetUINT32(MFAttributesClsid.MFSampleExtension_Discontinuity, 1); if (hr != HResult.S_OK) { // we failed throw new Exception("CopyFile: Failed on calling SetUINT32 on the sample, retVal=" + hr.ToString()); } // remember this - we only do it once videoSamplesProcessed++; } hr = sinkWriter.WriteSample(sinkWriterOutputVideoStreamId, workingMediaSample); if (hr != HResult.S_OK) { // we failed throw new Exception("CopyFile: Failed on calling the WriteSample on the writer, retVal=" + hr.ToString()); } } // release the sample if (workingMediaSample != null) { Marshal.ReleaseComObject(workingMediaSample); workingMediaSample = null; } } // do we have a stream tick event? if ((actualStreamFlags & MF_SOURCE_READER_FLAG.StreamTick) != 0) { if (actualStreamIndex == sourceReaderVideoStreamId) { // video stream hr = sinkWriter.SendStreamTick(sinkWriterOutputVideoStreamId, timeStamp); } else if (actualStreamIndex == sourceReaderAudioStreamId) { // audio stream hr = sinkWriter.SendStreamTick(sinkWriterOutputAudioStreamId, timeStamp); } else { } } // is this stream at an END of Segment if ((actualStreamFlags & MF_SOURCE_READER_FLAG.EndOfStream) != 0) { // We have an EOS - but is it on the video or audio channel? // we have to get it on both if (actualStreamIndex == sourceReaderVideoStreamId) { // video stream // have we seen this before? if (videoStreamIsAtEOS == false) { hr = sinkWriter.NotifyEndOfSegment(sinkWriterOutputVideoStreamId); if (hr != HResult.S_OK) { // we failed throw new Exception("CopyFile: Failed on calling the NotifyEndOfSegment on video stream, retVal=" + hr.ToString()); } videoStreamIsAtEOS = true; } } else if (actualStreamIndex == sourceReaderAudioStreamId) { // audio stream // have we seen this before? if (audioStreamIsAtEOS == false) { hr = sinkWriter.NotifyEndOfSegment(sinkWriterOutputAudioStreamId); if (hr != HResult.S_OK) { // we failed throw new Exception("CopyFile: Failed on calling the NotifyEndOfSegment on audio stream, retVal=" + hr.ToString()); } audioStreamIsAtEOS = true; } // audio stream } else { } // our exit condition depends on which streams are in use if ((sourceReaderNativeVideoMediaType != null) && (sourceReaderNativeAudioMediaType != null)) { // if both streams are at EOS we can leave if ((videoStreamIsAtEOS == true) && (audioStreamIsAtEOS == true)) { break; } } else if (sourceReaderNativeVideoMediaType != null) { // only video is active, if the video stream is EOS we can leave if (videoStreamIsAtEOS == true) { break; } } else if (sourceReaderNativeAudioMediaType != null) { // only audio is active, if the audio stream is EOS we can leave if (audioStreamIsAtEOS == true) { break; } } } } // bottom of endless for loop hr = sinkWriter.Finalize_(); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed on call tosinkWriter.Finalize(), retVal=" + hr.ToString()); } }