Пример #1
0
        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <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());
            }
        }