private void CreateH264Decoder() { HResult hr; // create H.264 decoder var comobject = new ResamplerMediaComObject(); decodertransform = (IMFTransform)comobject; // setup input media type for decoder MFExtern.MFCreateMediaType(out decinputmediatype); // setup media type manualy IMFMediaType testdecinputmediatype, testdecoutputmediatype; decinputmediatype.SetGUID(MFAttributesClsid.MF_MT_MAJOR_TYPE, MFMediaType.Video); decinputmediatype.SetGUID(MFAttributesClsid.MF_MT_SUBTYPE, MFMediaType.H264); decinputmediatype.SetUINT32(MFAttributesClsid.MF_MT_INTERLACE_MODE, (int)MFVideoInterlaceMode.Progressive); MFExtern.MFSetAttributeSize(decinputmediatype, MFAttributesClsid.MF_MT_FRAME_SIZE, VIDEO_SAMPLE_WIDTH, VIDEO_SAMPLE_HEIGHT); uint fixedSampleSize = VIDEO_SAMPLE_WIDTH * (16 * ((VIDEO_SAMPLE_HEIGHT + 15) / 16)) + VIDEO_SAMPLE_WIDTH * (VIDEO_SAMPLE_HEIGHT / 2);//for Y, U and V decinputmediatype.SetUINT32(MFAttributesClsid.MF_MT_SAMPLE_SIZE, fixedSampleSize); decinputmediatype.SetUINT32(MFAttributesClsid.MF_MT_DEFAULT_STRIDE, VIDEO_SAMPLE_WIDTH); decinputmediatype.SetUINT32(MFAttributesClsid.MF_MT_FIXED_SIZE_SAMPLES, 1); decinputmediatype.SetUINT32(MFAttributesClsid.MF_MT_ALL_SAMPLES_INDEPENDENT, 1); MFExtern.MFSetAttributeRatio(decinputmediatype, MFAttributesClsid.MF_MT_PIXEL_ASPECT_RATIO, 1, 1); hr = decodertransform.SetInputType(0, decinputmediatype, 0); decodertransform.GetInputAvailableType(0, 0, out testdecinputmediatype); // setup media type for output of decoder MFExtern.MFCreateMediaType(out decoutputmediatype); decoutputmediatype.SetGUID(MFAttributesClsid.MF_MT_MAJOR_TYPE, MFMediaType.Video); decoutputmediatype.SetGUID(MFAttributesClsid.MF_MT_SUBTYPE, MFMediaType.IYUV); MFExtern.MFSetAttributeSize(decoutputmediatype, MFAttributesClsid.MF_MT_FRAME_SIZE, VIDEO_SAMPLE_WIDTH, VIDEO_SAMPLE_HEIGHT); MFExtern.MFSetAttributeRatio(decoutputmediatype, MFAttributesClsid.MF_MT_FRAME_RATE, 30, 1); MFExtern.MFSetAttributeRatio(decoutputmediatype, MFAttributesClsid.MF_MT_PIXEL_ASPECT_RATIO, 1, 1); decoutputmediatype.SetUINT32(MFAttributesClsid.MF_MT_INTERLACE_MODE, 2); hr = decodertransform.SetOutputType(0, decoutputmediatype, 0); decodertransform.GetOutputAvailableType(0, 0, out testdecoutputmediatype); decodertransform.GetInputStatus(0, out mftStatus); if (mftStatus != MFTInputStatusFlags.AcceptData) { Debug.WriteLine("DECODER NOT ACCEPT INPUT DATA"); return; } else { Debug.WriteLine("PROCESS INPUT DONE>>>> " + mftStatus); } decodertransform.ProcessMessage(MFTMessageType.CommandFlush, (IntPtr)null); decodertransform.ProcessMessage(MFTMessageType.NotifyBeginStreaming, (IntPtr)null); decodertransform.ProcessMessage(MFTMessageType.NotifyStartOfStream, (IntPtr)null); }
/// <summary> /// Given an input media type, create the associated output type. /// </summary> /// <param name="inType">The input type from which to create the output type</param> /// <returns>An output type generated from the input type.</returns> protected override IMFMediaType CreateOutputFromInput() { MFError throwonhr; IMFMediaType pOutputType = CloneMediaType(InputType); throwonhr = pOutputType.SetGUID(MFAttributesClsid.MF_MT_SUBTYPE, m_MediaSubtypesOut[0]); throwonhr = pOutputType.SetUINT32(MFAttributesClsid.MF_MT_DEFAULT_STRIDE, m_StrideOut); throwonhr = pOutputType.SetUINT32(MFAttributesClsid.MF_MT_SAMPLE_SIZE, m_cbImageSizeOutput); return(pOutputType); }
public HRESULT SetUINT32(Guid guidKey, uint unValue) { var hr = _type.SetUINT32(guidKey, unValue); Trace("key: " + guidKey.ToName() + " value: " + unValue + " hr: " + hr); return(hr); }
private IMFMediaType CreateTargetVideoMediaType() { IMFMediaType mediaType = null; MFHelper.MFCreateMediaType(out mediaType); mediaType.SetGUID(new Guid(Consts.MF_MT_MAJOR_TYPE), new Guid(Consts.MFMediaType_Video)); mediaType.SetGUID(new Guid(Consts.MF_MT_SUBTYPE), this.videoOutput.Subtype); mediaType.SetUINT64(new Guid(Consts.MF_MT_FRAME_SIZE), this.videoOutput.FrameSize.Packed); mediaType.SetUINT64(new Guid(Consts.MF_MT_FRAME_RATE), this.videoOutput.FrameRate.Packed); mediaType.SetUINT64(new Guid(Consts.MF_MT_PIXEL_ASPECT_RATIO), this.videoOutput.PixelAspectRatio.Packed); mediaType.SetUINT32(new Guid(Consts.MF_MT_AVG_BITRATE), this.videoOutput.AvgBitRate); mediaType.SetUINT32(new Guid(Consts.MF_MT_INTERLACE_MODE), this.videoOutput.InterlaceMode); return(mediaType); }
HResult ConfigureEncoder( EncodingParameters eparams, IMFMediaType pType, IMFSinkWriter pWriter, out int pdwStreamIndex ) { HResult hr = HResult.S_OK; IMFMediaType pType2 = null; hr = MFExtern.MFCreateMediaType(out pType2); if (Succeeded(hr)) { hr = pType2.SetGUID(MFAttributesClsid.MF_MT_MAJOR_TYPE, MFMediaType.Video); } if (Succeeded(hr)) { hr = pType2.SetGUID(MFAttributesClsid.MF_MT_SUBTYPE, eparams.subtype); } if (Succeeded(hr)) { hr = pType2.SetUINT32(MFAttributesClsid.MF_MT_AVG_BITRATE, eparams.bitrate); } if (Succeeded(hr)) { hr = CopyAttribute(pType, pType2, MFAttributesClsid.MF_MT_FRAME_SIZE); } if (Succeeded(hr)) { hr = CopyAttribute(pType, pType2, MFAttributesClsid.MF_MT_FRAME_RATE); } if (Succeeded(hr)) { hr = CopyAttribute(pType, pType2, MFAttributesClsid.MF_MT_PIXEL_ASPECT_RATIO); } if (Succeeded(hr)) { hr = CopyAttribute(pType, pType2, MFAttributesClsid.MF_MT_INTERLACE_MODE); } pdwStreamIndex = 0; if (Succeeded(hr)) { hr = pWriter.AddStream(pType2, out pdwStreamIndex); } SafeRelease(pType2); return(hr); }
/// <summary> /// Converts the specified wave format into the appropriate Media Foundation audio media type. /// </summary> /// <param name="format">Input wave format to convert.</param> /// <returns>Media Foundation type resulting from conversion.</returns> internal static IMFMediaType CreateMediaType(WaveFormat format) { IMFMediaType mediaType = null; Guid guidSubType = GetMediaSubtype(format); if (guidSubType != Guid.Empty) { // Create the empty media type. mediaType = NativeMethods.MFCreateMediaType(); // Calculate derived values. uint blockAlign = (uint)(format.Channels * (format.BitsPerSample / 8)); uint bytesPerSecond = (uint)(blockAlign * format.SamplesPerSec); // Set attributes on the type. mediaType.SetGUID(Guids.MFMTMajorType, Guids.MFMediaTypeAudio); mediaType.SetGUID(Guids.MFMTSubType, guidSubType); mediaType.SetUINT32(Guids.MFMTAudioNumChannels, format.Channels); mediaType.SetUINT32(Guids.MFMTAudioSamplesPerSecond, format.SamplesPerSec); mediaType.SetUINT32(Guids.MFMTAudioBlockAlignment, blockAlign); mediaType.SetUINT32(Guids.MFMTAudioAvgBytesPerSecond, bytesPerSecond); mediaType.SetUINT32(Guids.MFMTAudioBitsPerSample, format.BitsPerSample); mediaType.SetUINT32(Guids.MFMTAllSamplesIndependent, 1); } return(mediaType); }
//----------------------------------------------------------------------------- // GetDefaultStride // // Gets the default stride for a video frame, assuming no extra padding bytes. // //----------------------------------------------------------------------------- private static int GetDefaultStride(IMFMediaType pType, out int plStride) { int lStride; plStride = 0; // Try to get the default stride from the media type. var hr = pType.GetUINT32(MFAttributesClsid.MF_MT_DEFAULT_STRIDE, out lStride); if (Failed(hr)) { // Attribute not set. Try to calculate the default stride. Guid subtype; var width = 0; // ReSharper disable once TooWideLocalVariableScope // ReSharper disable once RedundantAssignment var height = 0; // Get the subtype and the image size. hr = pType.GetGUID(MFAttributesClsid.MF_MT_SUBTYPE, out subtype); if (Succeeded(hr)) { hr = CProcess.MfGetAttributeSize(pType, out width, out height); } if (Succeeded(hr)) { var f = new FourCC(subtype); hr = MFExtern.MFGetStrideForBitmapInfoHeader(f.ToInt32(), width, out lStride); } // Set the attribute for later reference. if (Succeeded(hr)) { hr = pType.SetUINT32(MFAttributesClsid.MF_MT_DEFAULT_STRIDE, lStride); } } if (Succeeded(hr)) { plStride = lStride; } return(hr); }
//----------------------------------------------------------------------------- // GetDefaultStride // // Gets the default stride for a video frame, assuming no extra padding bytes. // //----------------------------------------------------------------------------- private static HResult GetDefaultStride(IMFMediaType pType, out int plStride) { int lStride = 0; plStride = 0; // Try to get the default stride from the media type. HResult hr = pType.GetUINT32(MFAttributesClsid.MF_MT_DEFAULT_STRIDE, out lStride); if (Failed(hr)) { // Attribute not set. Try to calculate the default stride. Guid subtype = Guid.Empty; int width = 0; int height = 0; // Get the subtype and the image size. hr = pType.GetGUID(MFAttributesClsid.MF_MT_SUBTYPE, out subtype); if (Succeeded(hr)) { hr = MFExtern.MFGetAttributeSize(pType, MFAttributesClsid.MF_MT_FRAME_SIZE, out width, out height); } if (Succeeded(hr)) { FourCC f = new FourCC(subtype); hr = MFExtern.MFGetStrideForBitmapInfoHeader(f.ToInt32(), width, out lStride); } // Set the attribute for later reference. if (Succeeded(hr)) { hr = pType.SetUINT32(MFAttributesClsid.MF_MT_DEFAULT_STRIDE, lStride); } } if (Succeeded(hr)) { plStride = lStride; } return(hr); }
protected override IMFMediaType CreateOutputFromInput() { // For some MFTs, the output type is the same as the input type. // However, since we are rotating, several attributes in the // media type (like frame size) must be different on our output. // This routine generates the appropriate output type for the // current input type, given the current state of m_WasOdd. IMFMediaType inType = InputType; IMFMediaType pOutputType = CloneMediaType(inType); if (m_WasOdd) { MFError throwonhr; int h, w; // Intentionally backward throwonhr = MFExtern.MFGetAttributeSize(inType, MFAttributesClsid.MF_MT_FRAME_SIZE, out h, out w); throwonhr = MFExtern.MFSetAttributeSize(pOutputType, MFAttributesClsid.MF_MT_FRAME_SIZE, w, h); MFVideoArea a = GetArea(inType, MFAttributesClsid.MF_MT_GEOMETRIC_APERTURE); if (a != null) { a.Area.Height = h; a.Area.Width = w; SetArea(pOutputType, MFAttributesClsid.MF_MT_GEOMETRIC_APERTURE, a); } a = GetArea(inType, MFAttributesClsid.MF_MT_MINIMUM_DISPLAY_APERTURE); if (a != null) { a.Area.Height = h; a.Area.Width = w; SetArea(pOutputType, MFAttributesClsid.MF_MT_MINIMUM_DISPLAY_APERTURE, a); } throwonhr = pOutputType.SetUINT32(MFAttributesClsid.MF_MT_DEFAULT_STRIDE, w * bpp); } return(pOutputType); }
//----------------------------------------------------------------------------- // GetDefaultStride // // Gets the default stride for a video frame, assuming no extra padding bytes. // //----------------------------------------------------------------------------- private static int GetDefaultStride(IMFMediaType pType, out int plStride) { int lStride = 0; plStride = 0; // Try to get the default stride from the media type. int hr = pType.GetUINT32(MFAttributesClsid.MF_MT_DEFAULT_STRIDE, out lStride); if (Failed(hr)) { // Attribute not set. Try to calculate the default stride. Guid subtype = Guid.Empty; int width = 0; int height = 0; // Get the subtype and the image size. hr = pType.GetGUID(MFAttributesClsid.MF_MT_SUBTYPE, out subtype); if (Succeeded(hr)) { hr = MFGetAttributeSize(pType, MFAttributesClsid.MF_MT_FRAME_SIZE, out width, out height); } if (Succeeded(hr)) { FourCC f = new FourCC(subtype); hr = MediaFoundation.MFExtern.MFGetStrideForBitmapInfoHeader(f.ToInt32(), width, out lStride); } // Set the attribute for later reference. if (Succeeded(hr)) { hr = pType.SetUINT32(MFAttributesClsid.MF_MT_DEFAULT_STRIDE, lStride); } } if (Succeeded(hr)) { plStride = lStride; } return hr; }
//----------------------------------------------------------------------------- // GetDefaultStride // // Gets the default stride for a video frame, assuming no extra padding bytes. // //----------------------------------------------------------------------------- private static int GetDefaultStride(IMFMediaType pType, out int plStride) { int lStride; plStride = 0; // Try to get the default stride from the media type. var hr = pType.GetUINT32(MFAttributesClsid.MF_MT_DEFAULT_STRIDE, out lStride); if (Failed(hr)) { // Attribute not set. Try to calculate the default stride. Guid subtype; var width = 0; // ReSharper disable once TooWideLocalVariableScope // ReSharper disable once RedundantAssignment var height = 0; // Get the subtype and the image size. hr = pType.GetGUID(MFAttributesClsid.MF_MT_SUBTYPE, out subtype); if (Succeeded(hr)) { hr = CProcess.MfGetAttributeSize(pType, out width, out height); } if (Succeeded(hr)) { var f = new FourCC(subtype); hr = MFExtern.MFGetStrideForBitmapInfoHeader(f.ToInt32(), width, out lStride); } // Set the attribute for later reference. if (Succeeded(hr)) { hr = pType.SetUINT32(MFAttributesClsid.MF_MT_DEFAULT_STRIDE, lStride); } } if (Succeeded(hr)) { plStride = lStride; } return hr; }
private IMFMediaType CreateTargetAudioMediaType() { IMFMediaType mediaType = null; if (audioOutput.Subtype.Equals(new Guid(Consts.MFAudioFormat_AAC))) { // Create the AAC media type MFHelper.MFCreateMediaType(out mediaType); mediaType.SetGUID(new Guid(Consts.MF_MT_MAJOR_TYPE), new Guid(Consts.MFMediaType_Audio)); mediaType.SetGUID(new Guid(Consts.MF_MT_SUBTYPE), this.audioOutput.Subtype); mediaType.SetUINT32(new Guid(Consts.MF_MT_AUDIO_AVG_BYTES_PER_SECOND), this.audioOutput.AvgBytePerSecond); mediaType.SetUINT32(new Guid(Consts.MF_MT_AUDIO_NUM_CHANNELS), this.audioOutput.NumOfChannels); mediaType.SetUINT32(new Guid(Consts.MF_MT_AUDIO_SAMPLES_PER_SECOND), this.audioOutput.SamplesPerSecond); mediaType.SetUINT32(new Guid(Consts.MF_MT_AUDIO_BLOCK_ALIGNMENT), this.audioOutput.BlockAlignment); mediaType.SetUINT32(new Guid(Consts.MF_MT_AUDIO_BITS_PER_SAMPLE), this.audioOutput.BitsPerSample); } else { // Create the WMA media type uint codecConfig = 0; uint elementsNumber = 0; uint selectedType = 0; int avgBitrateDiff = int.MaxValue; uint avgBytePerSecond = uint.MaxValue; object supportedAttributes = null; // Get the available audio ouput types for the required sub type IMFCollection availableTypes = null; MFHelper.MFTranscodeGetAudioOutputAvailableTypes(audioOutput.Subtype, (uint)Enums.MFT_ENUM_FLAG.MFT_ENUM_FLAG_ALL, codecConfig, out availableTypes); // Get the number of types availableTypes.GetElementCount(out elementsNumber); for (uint elementIndex = 0; elementIndex < elementsNumber; elementIndex++) { // Get the next element availableTypes.GetElement(elementIndex, out supportedAttributes); mediaType = (IMFMediaType)supportedAttributes; // Get the byte per second mediaType.GetUINT32(new Guid(Consts.MF_MT_AUDIO_AVG_BYTES_PER_SECOND), out avgBytePerSecond); // If this is better than the last one found remember the index if (Math.Abs((int)avgBytePerSecond - (int)audioOutput.AvgBytePerSecond) < avgBitrateDiff) { selectedType = elementIndex; avgBitrateDiff = Math.Abs((int)avgBytePerSecond - (int)audioOutput.AvgBytePerSecond); } mediaType = null; } // Get the best audio type found availableTypes.GetElement(selectedType, out supportedAttributes); mediaType = (IMFMediaType)supportedAttributes; } return(mediaType); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Starts capture of the data to a file. /// /// Because this code is intended for demo purposes and in the interests of /// reducing complexity it is extremely linear and step-by-step. Doubtless /// there is much refactoring that could be done. /// /// </summary> /// <history> /// 01 Nov 18 Cynic - Started /// </history> private void CaptureToFile() { HResult hr; IMFMediaType videoType = null; IMFMediaType encoderType = null; TantaMFDevice currentDevice = null; try { // get the current video device. currentDevice = ctlTantaVideoPicker1.CurrentDevice; if (currentDevice == null) { MessageBox.Show("No current video device. Are there any video devices on this system?"); return; } // check our output filename is correct and usable if ((textBoxCaptureFileNameAndPath == null) || (textBoxCaptureFileNameAndPath.Text.Length == 0)) { MessageBox.Show("No Capture Filename and path. Cannot continue."); return; } string pwszFileName = textBoxCaptureFileNameAndPath.Text; // check the path is rooted if (Path.IsPathRooted(pwszFileName) == false) { MessageBox.Show("No Capture Filename and path is not rooted. A full directory and path is required. Cannot continue."); return; } // create a new call back handler. This, once we get it all wired up, will act // as a pump to move the data from the source to the sink workingSourceReaderCallBackHandler = new TantaSourceReaderCallbackHandler(); // the following code will create a SourceReader which is tied to a camera on the system, // a SinkWriter which is tied to a file output and will hook up the two. Because we are using // a SourceReader and SourceWriter we do not have the usual Topology or Pipeline. The SourceReader // and SourceWriter are connected directly (input to output) in the code below and transfer their // data via the callback handler. The callback handler also requests the next sample from // the SourceReader when it has written the data to the sink. Note however it is possible // that the SourceReader can automatically bring in a Transform for format conversion. This // is done internally and you never deal with it - other than perhaps making it available // to the process if it is not globally available. // create the source reader workingSourceReader = TantaWMFUtils.CreateSourceReaderAsyncFromDevice(currentDevice, workingSourceReaderCallBackHandler); if (workingSourceReader == null) { MessageBox.Show("CreateSourceReaderAsyncFromDevice did not return a media source. Cannot continue."); return; } // open up the sink Writer workingSinkWriter = OpenSinkWriter(pwszFileName); if (workingSinkWriter == null) { MessageBox.Show("OpenSinkWriter workingSinkWriter == null. Cannot continue."); return; } // now set the source and the sink in the callback handler. It needs to know these // in order to operate workingSourceReaderCallBackHandler.SourceReader = workingSourceReader; workingSourceReaderCallBackHandler.SinkWriter = workingSinkWriter; workingSourceReaderCallBackHandler.InitForFirstSample(); workingSourceReaderCallBackHandler.SourceReaderAsyncCallBackError = HandleSourceReaderAsyncCallBackErrors; // now we configure the video source. It will probably offer a lot of different types // this example offers two modes: one mode where you choose the format and mode and // effectively just say "Use this one". The other uses the general case where we // present a list of reasonable types we can accept and then let it auto // configure itself from one of those. Of course if it autoconfigures itself we // don't know which one it has chosen. This is why, you will later see the video // source being interrogated after the configuration so we know which one we hit. if (radioButtonUseSpecified.Checked == true) { // we saved the video format container here - this is just the last one that came in if ((radioButtonUseSpecified.Tag == null) || ((radioButtonUseSpecified.Tag is TantaMFVideoFormatContainer) == false)) { MessageBox.Show("No source video device and format selected. Cannot continue."); return; } // get the container TantaMFVideoFormatContainer videoFormatCont = (radioButtonUseSpecified.Tag as TantaMFVideoFormatContainer); // configure the Source Reader to use this format hr = TantaWMFUtils.ConfigureSourceReaderWithVideoFormat(workingSourceReader, videoFormatCont); if (hr != HResult.S_OK) { // we failed MessageBox.Show("Failed on call to ConfigureSourceAsyncReaderWithVideoFormat (a), retVal=" + hr.ToString()); return; } } else { // prepare a list of subtypes we are prepared to accept from the video source // device. These will be tested in order - the first match will be used. List <Guid> subTypes = new List <Guid>(); subTypes.Add(MFMediaType.NV12); subTypes.Add(MFMediaType.YUY2); subTypes.Add(MFMediaType.UYVY); subTypes.Add(MFMediaType.RGB32); subTypes.Add(MFMediaType.RGB24); subTypes.Add(MFMediaType.IYUV); // make sure the default Media Type is one of the above video formats hr = TantaWMFUtils.ConfigureSourceReaderWithVideoFormat(workingSourceReader, subTypes, false); if (hr != HResult.S_OK) { // we failed MessageBox.Show("Failed on call to ConfigureSourceAsyncReaderWithVideoFormat (b), retVal=" + hr.ToString()); return; } } // if we get here we know the source reader now has a configured format but we might not // know which one it is. So we ask it. It will return a video type // we will use this later on to configure our sink writer. Note, we have to properly dispose // of the videoType object after we use it. hr = workingSourceReader.GetCurrentMediaType(TantaWMFUtils.MF_SOURCE_READER_FIRST_VIDEO_STREAM, out videoType); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed on call to GetCurrentMediaType, retVal=" + hr.ToString()); } // now we configure the encoder. This sets up the sink writer so that it knows what format // the output data should be written in. The format we give the writer does not // need to be the same as the format it outputs to disk - however to make life easier for ourselves // we will copy a lot of the settings from the videoType retrieved above // create a new empty media type for us to populate hr = MFExtern.MFCreateMediaType(out encoderType); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed on call to MFCreateMediaType, retVal=" + hr.ToString()); } // The major type defines the overall category of the media data. Major types include video, audio, script & etc. hr = encoderType.SetGUID(MFAttributesClsid.MF_MT_MAJOR_TYPE, MFMediaType.Video); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed setting the MF_MT_MAJOR_TYPE, retVal=" + hr.ToString()); } // The subtype GUID defines a specific media format type within a major type. For example, within video, // the subtypes include MFMediaType.H264 (MP4), MFMediaType.WMV3 (WMV), MJPEG & etc. Within audio, the // subtypes include PCM audio, Windows Media Audio 9, & etc. hr = encoderType.SetGUID(MFAttributesClsid.MF_MT_SUBTYPE, MEDIA_TYPETO_WRITE); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed setting the MF_MT_SUBTYPE, retVal=" + hr.ToString()); } // this is the approximate data rate of the video stream, in bits per second, for a video media type // in the MF.Net sample code this is 240000 but I found 2000000 to be much better. I am not sure, // at this time, how this value is derived or what the tradeoffs are. hr = encoderType.SetUINT32(MFAttributesClsid.MF_MT_AVG_BITRATE, TARGET_BIT_RATE); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed setting the MF_MT_AVG_BITRATE, retVal=" + hr.ToString()); } // populate our new encoding type with the frame size of the videoType selected earlier hr = TantaWMFUtils.CopyAttributeData(videoType, encoderType, MFAttributesClsid.MF_MT_FRAME_SIZE); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed copying the MF_MT_FRAME_SIZE, retVal=" + hr.ToString()); } // populate our new encoding type with the frame rate of the video type selected earlier hr = TantaWMFUtils.CopyAttributeData(videoType, encoderType, MFAttributesClsid.MF_MT_FRAME_RATE); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed copying the MF_MT_FRAME_RATE, retVal=" + hr.ToString()); } // populate our new encoding type with the pixel aspect ratio of the video type selected earlier hr = TantaWMFUtils.CopyAttributeData(videoType, encoderType, MFAttributesClsid.MF_MT_PIXEL_ASPECT_RATIO); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed copying the MF_MT_PIXEL_ASPECT_RATIO, retVal=" + hr.ToString()); } // populate our new encoding type with the interlace mode of the video type selected earlier hr = TantaWMFUtils.CopyAttributeData(videoType, encoderType, MFAttributesClsid.MF_MT_INTERLACE_MODE); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed copying the MF_MT_INTERLACE_MODE, retVal=" + hr.ToString()); } // add a stream to the sink writer. The encoderType 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. int sink_stream = 0; hr = workingSinkWriter.AddStream(encoderType, out sink_stream); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed adding the output stream, retVal=" + hr.ToString()); } // Windows 10, by default, provides an adequate set of codecs which the Sink Writer can // find to write out the MP4 file. This is not true on Windows 7. // If we are not on Windows 10 we register (locally) a codec // the Sink Writer can find and use. The ColorConvertDMO is supplied by // microsoft it is just not available to enumerate on Win7 etc. // Making it available locally does not require administrator privs // but only this process can see it and it disappears when the process // closes OperatingSystem os = Environment.OSVersion; int versionID = ((os.Version.Major * 10) + os.Version.Minor); if (versionID < 62) { Guid ColorConvertDMOGUID = new Guid("98230571-0087-4204-b020-3282538e57d3"); // Register the color converter DSP for this process, in the video // processor category. This will enable the sink writer to enumerate // the color converter when the sink writer attempts to match the // media types. hr = MFExtern.MFTRegisterLocalByCLSID( ColorConvertDMOGUID, MFTransformCategory.MFT_CATEGORY_VIDEO_PROCESSOR, "", MFT_EnumFlag.SyncMFT, 0, null, 0, null ); } // 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 = workingSinkWriter.SetInputMediaType(sink_stream, videoType, null); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed on calling SetInputMediaType on the writer, retVal=" + hr.ToString()); } // now we initialize the sink writer for writing. We call this method after configuring the // input streams but before we send any data to the sink writer. The underlying media sink must // have at least one input stream and we know it does because we set it up earlier hr = workingSinkWriter.BeginWriting(); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed on calling BeginWriting on the writer, retVal=" + hr.ToString()); } // Request the first video frame from the media source. The TantaSourceReaderCallbackHandler // set up earlier will be invoked and it will continue requesting and processing video // frames after that. hr = workingSourceReader.ReadSample( TantaWMFUtils.MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero ); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed on calling the first ReadSample on the reader, retVal=" + hr.ToString()); } // we are ready to start, flag this buttonStartStopCapture.Text = STOP_CAPTURE; // disable our screen controls SetEnableStateOnScreenControls(false); } finally { // setting this to null will cause it to be cleaned up currentDevice = null; // close and release if (videoType != null) { Marshal.ReleaseComObject(videoType); videoType = null; } if (encoderType != null) { Marshal.ReleaseComObject(encoderType); encoderType = null; } } }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Starts the process of recording. creates the sink writer. We do not /// check to see if the filename is viable or already exists. This is /// assumed to have been done before this call. /// </summary> /// <param name="outputFileName">the output file name</param> /// <param name="incomingVideoMediaType">the incoming media type</param> /// <param name="wantTimebaseRebaseIn">if true we rebase all incoming sample /// times to zero from the point we started recording and send a copy of the /// sample to the sink writer instead of the input sample</param> /// <returns>z success, nz fail</returns> /// <history> /// 01 Nov 18 Cynic - Started /// </history> public int StartRecording(string outputFileName, IMFMediaType incomingVideoMediaType, bool wantTimebaseRebaseIn) { HResult hr; IMFMediaType encoderType = null; LogMessage("MFTTantaSampleGrabber_Sync, StartRecording called"); // first stop any recordings now StopRecording(); // check the output file name for sanity if ((outputFileName == null) || (outputFileName.Length == 0)) { LogMessage("StartRecording (outputFileName==null)|| (outputFileName.Length==0)"); return(100); } // check the media type for sanity if (incomingVideoMediaType == null) { LogMessage("StartRecording videoMediaType == null"); return(150); } lock (sinkWriterLockObject) { // create the sink writer workingSinkWriter = OpenSinkWriter(outputFileName, true); if (workingSinkWriter == null) { LogMessage("StartRecording failed to create sink writer"); return(200); } // now configure the SinkWriter. This sets up the sink writer so that it knows what format // the output data should be written in. The format we give the writer does not // need to be the same as the format receives as input - however to make life easier for ourselves // we will copy a lot of the settings from the videoType retrieved above // create a new empty media type for us to populate hr = MFExtern.MFCreateMediaType(out encoderType); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed on call to MFCreateMediaType, retVal=" + hr.ToString()); } // The major type defines the overall category of the media data. Major types include video, audio, script & etc. hr = encoderType.SetGUID(MFAttributesClsid.MF_MT_MAJOR_TYPE, MFMediaType.Video); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed setting the MF_MT_MAJOR_TYPE, retVal=" + hr.ToString()); } // The subtype GUID defines a specific media format type within a major type. For example, within video, // the subtypes include MFMediaType.H264 (MP4), MFMediaType.WMV3 (WMV), MJPEG & etc. Within audio, the // subtypes include PCM audio, Windows Media Audio 9, & etc. hr = encoderType.SetGUID(MFAttributesClsid.MF_MT_SUBTYPE, MEDIA_TYPETO_WRITE); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed setting the MF_MT_SUBTYPE, retVal=" + hr.ToString()); } // this is the approximate data rate of the video stream, in bits per second, for a // video media type. The choice here is somewhat arbitrary but seems to work well. hr = encoderType.SetUINT32(MFAttributesClsid.MF_MT_AVG_BITRATE, TARGET_BIT_RATE); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed setting the MF_MT_AVG_BITRATE, retVal=" + hr.ToString()); } // populate our new encoding type with the frame size of the videoType selected earlier hr = TantaWMFUtils.CopyAttributeData(incomingVideoMediaType, encoderType, MFAttributesClsid.MF_MT_FRAME_SIZE); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed copying the MF_MT_FRAME_SIZE, retVal=" + hr.ToString()); } // populate our new encoding type with the frame rate of the video type selected earlier hr = TantaWMFUtils.CopyAttributeData(incomingVideoMediaType, encoderType, MFAttributesClsid.MF_MT_FRAME_RATE); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed copying the MF_MT_FRAME_RATE, retVal=" + hr.ToString()); } // populate our new encoding type with the pixel aspect ratio of the video type selected earlier hr = TantaWMFUtils.CopyAttributeData(incomingVideoMediaType, encoderType, MFAttributesClsid.MF_MT_PIXEL_ASPECT_RATIO); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed copying the MF_MT_PIXEL_ASPECT_RATIO, retVal=" + hr.ToString()); } // populate our new encoding type with the interlace mode of the video type selected earlier hr = TantaWMFUtils.CopyAttributeData(incomingVideoMediaType, encoderType, MFAttributesClsid.MF_MT_INTERLACE_MODE); if (hr != HResult.S_OK) { // we failed throw new Exception("Failed copying the MF_MT_INTERLACE_MODE, retVal=" + hr.ToString()); } // add a stream to the sink writer for the output Media type. The // incomingVideoMediaType 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. hr = workingSinkWriter.AddStream(encoderType, out sinkWriterVideoStreamId); if (hr != HResult.S_OK) { // we failed throw new Exception("StartRecording Failed adding the output stream(v), retVal=" + hr.ToString()); } // Windows 10, by default, provides an adequate set of codecs which the Sink Writer can // find to write out the MP4 file. This is not true on Windows 7. // If we are not on Windows 10 we register (locally) a codec // the Sink Writer can find and use. The ColorConvertDMO is supplied by // microsoft it is just not available to enumerate on Win7 etc. // Making it available locally does not require administrator privs // but only this process can see it and it disappears when the process // closes OperatingSystem os = Environment.OSVersion; int versionID = ((os.Version.Major * 10) + os.Version.Minor); if (versionID < 62) { Guid ColorConverterDMOGUID = new Guid("98230571-0087-4204-b020-3282538e57d3"); // Register the color converter DSP for this process, in the video // processor category. This will enable the sink writer to enumerate // the color converter when the sink writer attempts to match the // media types. hr = MFExtern.MFTRegisterLocalByCLSID( ColorConverterDMOGUID, MFTransformCategory.MFT_CATEGORY_VIDEO_PROCESSOR, "", MFT_EnumFlag.SyncMFT, 0, null, 0, null ); } // 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 = workingSinkWriter.SetInputMediaType(sinkWriterVideoStreamId, incomingVideoMediaType, null); if (hr != HResult.S_OK) { // we failed throw new Exception("StartRecording Failed on calling SetInputMediaType(v) on the writer, retVal=" + hr.ToString()); } // set this flag now wantTimebaseRebase = wantTimebaseRebaseIn; // now we initialize the sink writer for writing. We call this method after configuring the // input streams but before we send any data to the sink writer. The underlying media sink must // have at least one input stream and we know it does because we set it up above hr = workingSinkWriter.BeginWriting(); if (hr != HResult.S_OK) { // we failed throw new Exception("StartRecording Failed on calling BeginWriting on the writer, retVal=" + hr.ToString()); } } return(0); }