/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Close the Media sink /// </summary> /// <param name="writerObject">the media source object</param> /// <history> /// 01 Nov 18 Cynic - Started /// </history> private void CloseSinkWriter(IMFSinkWriter writerObject) { // End any active captures if (writerObject == null) { LogMessage("CloseSinkWriter rwriterObject == null"); return; } // close and release HResult hr = writerObject.Finalize_(); Marshal.ReleaseComObject(writerObject); }
public int StopCapture() { var hr = S_Ok; lock (LockSync) { if (PWriter != null) { hr = PWriter.Finalize_(); SafeRelease(PWriter); PWriter = null; } } return(hr); }
//------------------------------------------------------------------- // CloseDevice // // Releases all resources held by this object. //------------------------------------------------------------------- public HResult CloseDevice() { lock (this) { if (m_pWriter != null) { m_pWriter.Finalize_(); SafeRelease(m_pWriter); m_pWriter = null; } SafeRelease(m_pReader); m_pReader = null; m_pwszSymbolicLink = null; } return(HResult.S_OK); }
/// <summary> /// stops the process of recording. finalizes and then releases the sink writer /// </summary> /// <returns>z success, nz fail</returns> public int StopRecording() { LogMessage("StopRecording called"); // End any active captures if (workingSinkWriter != null) { lock (sinkWriterLockObject) { // close and release workingSinkWriter.Finalize_(); Marshal.ReleaseComObject(workingSinkWriter); workingSinkWriter = null; } } // reset these sinkWriterVideoStreamId = -1; isFirstSample = true; firstSampleBaseTime = 0; wantTimebaseRebase = false; return(0); }
public HResult EndCaptureSession() { HResult hr = HResult.S_OK; lock (this) { if (m_pWriter != null) { hr = m_pWriter.Finalize_(); SafeRelease(m_pWriter); m_pWriter = null; } if (m_pReader != null) { SafeRelease(m_pReader); m_pReader = null; } } return(hr); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <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()); } }