FrameSize frameSizeForStreamRequest(WTVStreamingVideoRequest strq) { switch (strq.Quality) { case WTVProfileQuality.Low: return(new FrameSize(160, 120)); case WTVProfileQuality.Normal: return(new FrameSize(160, 120)); case WTVProfileQuality.Med: return(new FrameSize(320, 240)); case WTVProfileQuality.High: return(new FrameSize(320, 240)); case WTVProfileQuality.UltraHigh: return(new FrameSize(512, 384)); case WTVProfileQuality.Test: return(new FrameSize(320, 240)); case WTVProfileQuality.Custom: return(new FrameSize(strq.CustomFrameWidth, strq.CustomFrameHeight)); default: return(new FrameSize(320, 240)); } }
/// <summary> /// Use XMLDocument to go through a PRX string and change the video encode settings /// </summary> /// <param name="txtWMPrf"></param> /// <param name="strq"></param> private void SetProfileCustomSettings(ref string txtWMPrf, ref WTVStreamingVideoRequest strq) { if (strq.Quality != WTVProfileQuality.Custom) { return; } SendDebugMessage("Setting WMProfile Custom settings: Video Bitrate:" + strq.CustomVideoBitrate.ToString() + "bps and Smoothness: " + strq.CustomEncoderSmoothness.ToString()); XmlDocument xDoc = new XmlDocument(); xDoc.LoadXml(txtWMPrf); XmlNode root = xDoc.DocumentElement; // Configure first located video stream foreach (XmlNode rootchild in root.ChildNodes) { if (rootchild.Name == "streamconfig") { if (AttributeEqualsValue(rootchild.Attributes["majortype"], PRX_GuidVideoStream)) { XmlNode passChild = rootchild; AddCustomValuesToVideoStreamConfig(ref passChild, ref strq); break; } } } // SAVE txtWMPrf = xDoc.InnerXml; }
// Constructor public DSStreamer() : base() { StreamingRequest = null; StreamPort = 9081; RemoveReferenceClock = false; PendingCommands = new Queue <StreamCommand>(); }
// Constructor public DSStreamer() : base() { StreamingRequest = null; StreamPort = 9081; RemoveReferenceClock = false; PendingCommands = new Queue<StreamCommand>(); }
public WTVStreamingVideoResult StartStreamer(WTVStreamingVideoRequest strq) { int newStreamerID = -1; // Too many streamers? if (mediaStreamers.Count > MAXIMUM_STREAMERS) { Functions.WriteLineToLogFile("DSStreamingManager: too many streamers (" + mediaStreamers.Count.ToString() + " streamers are running, which is above the maximum of " + MAXIMUM_STREAMERS.ToString() + ")" ); return new WTVStreamingVideoResult(DSStreamResultCodes.ErrorTooManyStreamers); } // For now, some overrides and assumptions if (!File.Exists(strq.FileName)) { Functions.WriteLineToLogFile("WebSvc Start Streaming FAIL: File not found: " + strq.FileName); return new WTVStreamingVideoResult(DSStreamResultCodes.ErrorFileNotFound); } try { DSStreamer mediaStreamer = new DSStreamer(); mediaStreamer = new FatAttitude.WTVTranscoder.DSStreamer(); mediaStreamer.Finished += new EventHandler<DSTranscoderBase.ConversionEndedEventArgs>(mediaStreamer_Finished); mediaStreamer.ConversionCompleted += new EventHandler(mediaStreamer_ConversionCompleted); mediaStreamer.DebugMessageGenerated += new EventHandler<DSTranscoderBase.DebugMessageEventArgs>(mediaStreamer_DebugMessageGenerated); Functions.WriteLineToLogFile("DSStreamingManager: DSStreamer object created."); // Which port should we use? int portToTry = GetNextFreePort(); // Try streaming (Async) Functions.WriteLineToLogFileIfSetting(Settings.Default.DebugStreaming, "DSStreamingManager: Attempting to stream using port " + portToTry.ToString()); WTVStreamingVideoResult streamResult = mediaStreamer.StreamWithFileAndPort(strq, portToTry, false, true); if (streamResult.ResultCode == DSStreamResultCodes.OK) { // Add to local streamers newStreamerID = AddNewStreamer(mediaStreamer); mediaStreamer.ID = newStreamerID; // Add streamer ID to result code too streamResult.StreamerID = newStreamerID.ToString(); } // Return return streamResult; } catch (Exception e) { Functions.WriteLineToLogFile("Exception setting up mediaStreaming object:"); Functions.WriteExceptionToLogFile(e); return new WTVStreamingVideoResult(DSStreamResultCodes.ErrorExceptionOccurred, "Error setting up mediastreaming object: " + e.Message + " (see server log for more details)"); } }
public static WTVStreamingVideoRequest FromXML(string theXML) { WTVStreamingVideoRequest newRR = new WTVStreamingVideoRequest(); XmlSerializer serializer = new XmlSerializer(newRR.GetType()); StringReader sr = new StringReader(theXML); try { return (WTVStreamingVideoRequest)serializer.Deserialize(sr); } catch { return newRR; } }
public static WTVStreamingVideoRequest FromXML(string theXML) { WTVStreamingVideoRequest newRR = new WTVStreamingVideoRequest(); XmlSerializer serializer = new XmlSerializer(newRR.GetType()); StringReader sr = new StringReader(theXML); try { return((WTVStreamingVideoRequest)serializer.Deserialize(sr)); } catch { return(newRR); } }
public DSStreamResultCodes TranscodeFileAsync(string fileName, WTVProfileQuality quality) { FileName = fileName; Quality = quality; WTVStreamingVideoRequest strq = new WTVStreamingVideoRequest(FileName, quality, TimeSpan.FromSeconds(0)); DSStreamResultCodes result = InitWithFile(strq); if (result != DSStreamResultCodes.OK) return result; // ...and stop Thread th = new Thread(new ThreadStart(DoTranscodeFileAsync)); th.Name = "TranscodeFile1"; th.Start(); return DSStreamResultCodes.OK; }
void AddCustomValuesToVideoStreamConfig(ref XmlNode streamconfig, ref WTVStreamingVideoRequest strq) { // Sanity Checks if (strq.CustomVideoBitrate > 1500000) { strq.CustomVideoBitrate = 1500000; } if (strq.CustomEncoderSmoothness > 100) { strq.CustomEncoderSmoothness = 100; } if (strq.CustomVideoBitrate < 10000) { strq.CustomVideoBitrate = 10000; } if (strq.CustomEncoderSmoothness < 5) { strq.CustomEncoderSmoothness = 5; } // TODO: More sanity checks // BITRATE (1 of 2) XmlNode xnode; bool foo = SetAttributeIfFound(streamconfig, "bitrate", strq.CustomVideoBitrate); // QUALITY if (FindChildByName(streamconfig, "videomediaprops", out xnode)) { foo = SetAttributeIfFound(xnode, "quality", strq.CustomEncoderSmoothness); } XmlNode xnodewmmediatype; if (FindChildByName(streamconfig, "wmmediatype", out xnodewmmediatype)) { if (FindChildByName(xnodewmmediatype, "videoinfoheader", out xnode)) { // BITRATE (2 of 2) foo = SetAttributeIfFound(xnode, "dwbitrate", strq.CustomVideoBitrate); // FPS double avgtime = 10000000 / strq.CustomEncoderFPS; avgtime = Math.Round(avgtime, 0); foo = SetAttributeIfFound(xnode, "avgtimeperframe", avgtime); // TODO: String format } } }
/// <summary> /// Main method - builds a DirectShow graph to transcode a Stream Buffer source file (.wtv or .dvr-ms) /// The graph looks like this: /// /// /--> AUDIO DECODER \ /// SBE INPUT FILTER => TAG/DECRYPT => ==> OUTPUT FILTER (ASFWRITER) /// \--> VIDEO DECODER / /// /// </summary> /// <param name="strq"></param> /// <returns></returns> public DSStreamResultCodes InitWithFile(WTVStreamingVideoRequest strq) { FileInfo fiInputFile = new FileInfo(strq.FileName); string txtOutputFNPath = fiInputFile.FullName + ".wmv"; if ( (fiInputFile.Extension.ToLowerInvariant().Equals(".wtv")) || (fiInputFile.Extension.ToLowerInvariant().Equals(".dvr-ms")) ) { return(InitWithStreamBufferFile(strq)); } else { return(InitWithVideoFile(strq)); } }
public DSStreamResultCodes TranscodeFileAsync(string fileName, WTVProfileQuality quality) { FileName = fileName; Quality = quality; WTVStreamingVideoRequest strq = new WTVStreamingVideoRequest(FileName, quality, TimeSpan.FromSeconds(0)); DSStreamResultCodes result = InitWithFile(strq); if (result != DSStreamResultCodes.OK) { return(result); // ...and stop } Thread th = new Thread(new ThreadStart(DoTranscodeFileAsync)); th.Name = "TranscodeFile1"; th.Start(); return(DSStreamResultCodes.OK); }
DSStreamResultCodes InitWithVideoFile(WTVStreamingVideoRequest strq) { UsingSBEFilter = false; // Not using stream buffer // Init variables IPin[] pin = new IPin[1]; string dPin = string.Empty; string sName = string.Empty; string dName = string.Empty; string sPin = string.Empty; FileInfo fiInputFile = new FileInfo(strq.FileName); string txtOutputFNPath = fiInputFile.FullName + ".wmv"; if ( (fiInputFile.Extension.ToLowerInvariant().Equals(".wtv")) || (fiInputFile.Extension.ToLowerInvariant().Equals(".dvr-ms")) ) { return(DSStreamResultCodes.ErrorInvalidFileType); } int hr = 0; try { // Get the graphbuilder interface SendDebugMessage("Creating Graph Object", 0); IGraphBuilder graphbuilder = (IGraphBuilder)currentFilterGraph; // Create an ASF writer filter SendDebugMessage("Creating ASF Writer", 0); WMAsfWriter asf_filter = new WMAsfWriter(); dc.Add(asf_filter); // CHECK FOR ERRORS currentOutputFilter = (IBaseFilter)asf_filter; // class variable // Add the ASF filter to the graph hr = graphbuilder.AddFilter((IBaseFilter)asf_filter, "WM Asf Writer"); DsError.ThrowExceptionForHR(hr); // Set the filename SendDebugMessage("Setting filename", 0); IFileSinkFilter sinkFilter = (IFileSinkFilter)asf_filter; string destPathFN = fiInputFile.FullName + ".wmv"; hr = sinkFilter.SetFileName(destPathFN, null); DsError.ThrowExceptionForHR(hr); // Handy to have an ACM Wrapper filter hanging around for AVI files with MP3 audio SendDebugMessage("Adding ACM Wrapper", 0); IBaseFilter ACMFilter = FilterDefinition.AddToFilterGraph(FilterDefinitions.Other.ACMWrapperFilter, ref graphbuilder); dc.Add(ACMFilter); // Render file - then build graph SendDebugMessage("Rendering file", 0); graphbuilder.RenderFile(fiInputFile.FullName, null); SendDebugMessage("Saving graph", 0); FilterGraphTools.SaveGraphFile(graphbuilder, "C:\\ProgramData\\RemotePotato\\lastfiltergraph.grf"); // Are both our ASF pins connected? IPin ASFVidInputPin = FilterGraphTools.FindPinByMediaType((IBaseFilter)asf_filter, PinDirection.Input, MediaType.Video, MediaSubType.Null); IPin ASFAudInputPin = FilterGraphTools.FindPinByMediaType((IBaseFilter)asf_filter, PinDirection.Input, MediaType.Audio, MediaSubType.Null); // Run graph [can use this also to get media type => see, e.g. dvrmstowmvhd by Babgvant] SendDebugMessage("Run graph for testing purposes", 0); IMediaControl tempControl = (IMediaControl)graphbuilder; IMediaEvent tempEvent = (IMediaEvent)graphbuilder; DsError.ThrowExceptionForHR(tempControl.Pause()); EventCode pEventCode; hr = tempEvent.WaitForCompletion(1000, out pEventCode); // Get media type from vid input pin for ASF writer AMMediaType pmt = new AMMediaType(); hr = ASFVidInputPin.ConnectionMediaType(pmt); FrameSize SourceFrameSize = null; if (pmt.formatType == FormatType.VideoInfo2) { // Now graph has been run and stopped we can get the video width and height from the output pin of the main video decoder VideoInfoHeader2 pvih2 = new VideoInfoHeader2(); Marshal.PtrToStructure(pmt.formatPtr, pvih2); SourceFrameSize = new FrameSize(pvih2.BmiHeader.Width, pvih2.BmiHeader.Height); } else if (pmt.formatType == FormatType.VideoInfo) //{05589f80-c356-11ce-bf01-00aa0055595a} { VideoInfoHeader pvih = new VideoInfoHeader(); Marshal.PtrToStructure(pmt.formatPtr, pvih); SourceFrameSize = new FrameSize(pvih.BmiHeader.Width, pvih.BmiHeader.Height); } else { SourceFrameSize = new FrameSize(200, 200); // SQUARE } // Stop graph if necessary FilterState pFS; hr = tempControl.GetState(1000, out pFS); if (pFS != FilterState.Stopped) { DsError.ThrowExceptionForHR(tempControl.Stop()); } // Free up media type DsUtils.FreeAMMediaType(pmt); pmt = null; // (re)Configure the ASF writer with the selected WM Profile ConfigureASFWriter(asf_filter, strq, SourceFrameSize); // Release pins SendDebugMessage("Releasing COM objects (pins)", 0); // source Marshal.ReleaseComObject(ASFVidInputPin); ASFVidInputPin = null; Marshal.ReleaseComObject(ASFAudInputPin); ASFAudInputPin = null; } catch (Exception ex) { SendDebugMessageWithException(ex.Message, ex); return(DSStreamResultCodes.ErrorExceptionOccurred); } return(DSStreamResultCodes.OK); }
// Main public method - Begin Streaming public WTVStreamingVideoResult StreamWithFileAndPort(WTVStreamingVideoRequest svrq, int streamPort, bool removeReferenceClock, bool autodetectPal) { WTVStreamingVideoResult result = new WTVStreamingVideoResult(); // Attempting this port result.Port = streamPort.ToString(); // Store values locally for use by other class methods StreamingRequest = svrq; StreamPort = streamPort; RemoveReferenceClock = removeReferenceClock; // Build the graph using the base class DSStreamResultCodes resultCode = base.InitWithFile( StreamingRequest); if (resultCode != DSStreamResultCodes.OK) return new WTVStreamingVideoResult(resultCode); // Add streaming try { AddStreamSinkToCurrentOutputFilter(StreamPort); } catch (Exception ex) { result.ResultCode = DSStreamResultCodes.ErrorAlreadyStreaming; result.ResultString = ex.Message; return result; } // Remove clock? try { if (RemoveReferenceClock) RemoveRefClockFromGraph(); } catch (Exception ex) { result.ResultCode = DSStreamResultCodes.Error; result.ResultString = "Error removing reference clock: " + ex.Message; return result; } // Seek? try { if (StreamingRequest.StartAt.TotalSeconds > 0) SeekGraphToTime(StreamingRequest.StartAt); else SeekGraphToTime(TimeSpan.FromSeconds(5)); // EXPERIMENTAL } catch { // Ignore non-seeking errors for now } // Begin streaming the graph in a separate thread graphStartedEvent.Reset(); thStreamThread = new Thread(new ThreadStart(DoStreamWithFileAndPort)); thStreamThread.SetApartmentState(ApartmentState.MTA); // NetworkSink fails in STA threads - avoid! thStreamThread.Name = "StreamFile1"; thStreamThread.Start(); // Wait for graph to attempt to start so we can report back bool gotSignal = ( graphStartedEvent.WaitOne(6000)); if (gotSignal) { if (graphIsRunning) { result.ResultCode = DSStreamResultCodes.OK; return result; } else { result.ResultCode = DSStreamResultCodes.Error; result.ResultString = "DSStreamer: Graph could not run - see server log for more information."; return result; } } else { result.ResultCode = DSStreamResultCodes.Error; result.ResultString = "DSStreamer: Timed out waiting for graph to run - there may be more information in the server log."; return result; } }
/// <summary> /// Main method - builds a DirectShow graph to transcode a Stream Buffer source file (.wtv or .dvr-ms) /// The graph looks like this: /// /// /--> AUDIO DECODER \ /// SBE INPUT FILTER => TAG/DECRYPT => ==> OUTPUT FILTER (ASFWRITER) /// \--> VIDEO DECODER / /// /// </summary> /// <param name="strq"></param> /// <returns></returns> public DSStreamResultCodes InitWithFile(WTVStreamingVideoRequest strq) { FileInfo fiInputFile = new FileInfo(strq.FileName); string txtOutputFNPath = fiInputFile.FullName + ".wmv"; if ( (fiInputFile.Extension.ToLowerInvariant().Equals(".wtv")) || (fiInputFile.Extension.ToLowerInvariant().Equals(".dvr-ms")) ) return InitWithStreamBufferFile(strq); else return InitWithVideoFile(strq); }
void ConfigureASFWriter(WMAsfWriter asf_filter, WTVStreamingVideoRequest strq, FrameSize SourceFrameSize) { int hr; // Now it's added to the graph, configure it with the selected WM Profile SendDebugMessage("Getting WM profile with quality of " + strq.Quality.ToString(), 0); WindowsMediaLib.IWMProfileManager profileManager; WMUtils.WMCreateProfileManager(out profileManager); IWMProfile wmProfile; string txtPrxProfile = getPRXProfileForQuality(strq.Quality); if (!(string.IsNullOrEmpty(txtPrxProfile))) { SendDebugMessage("Adjusting WM profile to fit video within designated frame size", 0); // SET VIDEO SIZE TO FIT WITHIN THE RIGHT FRAME SendDebugMessage("Source video size is " + SourceFrameSize.ToString(), 0); FrameSize containerSize = frameSizeForStreamRequest(strq); SendDebugMessage("Container size is " + containerSize.ToString() , 0); FrameSize newVideoSize = new FrameSize(SourceFrameSize, containerSize); SendDebugMessage("Output size is " + newVideoSize.ToString(), 0); SetProfileFrameSize(ref txtPrxProfile, newVideoSize); SetProfileCustomSettings(ref txtPrxProfile, ref strq); // returns immediately if not custom quality SendDebugMessage("Configuring ASF Writer with profile", 0); profileManager.LoadProfileByData(txtPrxProfile, out wmProfile); WindowsMediaLib.IConfigAsfWriter configWriter = (WindowsMediaLib.IConfigAsfWriter)asf_filter; configWriter.ConfigureFilterUsingProfile(wmProfile); configWriter.SetIndexMode(true); // yes index - DEFAULT /* Additional config - TEST //DirectShowLib.IConfigAsfWriter2 configAsfWriter2 = (DirectShowLib.IConfigAsfWriter2)asf_filter; //configAsfWriter2.SetParam(ASFWriterConfig.AutoIndex, 0, 0); // IT IS DEFAULT */ // (NOT WORKING) // SET ANAMORPHIC VIDEO MARKERS WITHIN STREAM (ASPECT RATIO) ******************************* UInt32 uiAspectX = (UInt32)SourceFrameSize.Width; byte[] bAspectX = BitConverter.GetBytes(uiAspectX); UInt32 uiAspectY = (UInt32)SourceFrameSize.Height; byte[] bAspectY = BitConverter.GetBytes(uiAspectY); DirectShowLib.IServiceProvider pServiceProvider; // http://msdn.microsoft.com/en-us/library/dd390985%28VS.85%29.aspx pServiceProvider = (DirectShowLib.IServiceProvider)asf_filter; DsGuid dsgIWMHeaderinfo = DsGuid.FromGuid(new Guid(GUIDs.IWMWriterAdvanced2)); object o3 = null; hr = pServiceProvider.QueryService(dsgIWMHeaderinfo, dsgIWMHeaderinfo, out o3); // FAILS IN A STA THREAD DsError.ThrowExceptionForHR(hr); IWMHeaderInfo headerinfo = (IWMHeaderInfo)o3; // Get access to WMwriterAdvanced2 object using pServiceProvider (poss not futureproof) (see http://groups.google.com/group/microsoft.public.win32.programmer.directx.video/browse_thread/thread/36b154d41cb76ffd/c571d6ef56de11af?#c571d6ef56de11af ) DsGuid dsgWMwriterAdvanced2 = DsGuid.FromGuid(new Guid(GUIDs.IWMWriterAdvanced2)); object o = null; hr = pServiceProvider.QueryService(dsgWMwriterAdvanced2, dsgWMwriterAdvanced2, out o); // FAILS IN A STA THREAD DsError.ThrowExceptionForHR(hr); IWMWriterAdvanced2 WMWriterAdvanced2 = null; WMWriterAdvanced2 = (IWMWriterAdvanced2)o; // Get Access to IWMHeaderInfo3 through WMWriterAdvanced2 object o2 = null; //pServiceProvider = (DirectShowLib.IServiceProvider)WMWriterAdvanced2; DsGuid dsgIWMHeaderInfo3 = DsGuid.FromGuid(new Guid(GUIDs.IWMHeaderInfo3)); hr = pServiceProvider.QueryService(dsgWMwriterAdvanced2, dsgIWMHeaderInfo3, out o2); // LET'S SEE DsError.ThrowExceptionForHR(hr); IWMHeaderInfo3 WMHeaderInfo3 = null; WMHeaderInfo3 = (IWMHeaderInfo3)o2; short pwIndex; // Add Aspect Ratio information WMHeaderInfo3.AddAttribute(2, "AspectRatioX", out pwIndex, AttrDataType.DWORD, 0, bAspectX, bAspectX.Length); WMHeaderInfo3.AddAttribute(2, "AspectRatioY", out pwIndex, AttrDataType.DWORD, 0, bAspectY, bAspectY.Length); // Try with other interface too headerinfo.SetAttribute(2, "AspectRatioX", AttrDataType.DWORD, bAspectX, Convert.ToInt16(bAspectX.Length)); headerinfo.SetAttribute(2, "AspectRatioY", AttrDataType.DWORD, bAspectY, Convert.ToInt16(bAspectY.Length)); // ************ DEINTERLACE (experimental) if (strq.DeInterlaceMode > 0) { DeInterlaceModes dimode = DeInterlaceModes.WM_DM_NOTINTERLACED; // Deinterlace Mode if (strq.DeInterlaceMode == 1) dimode = DeInterlaceModes.WM_DM_DEINTERLACE_NORMAL; else if (strq.DeInterlaceMode == 2) dimode = DeInterlaceModes.WM_DM_DEINTERLACE_HALFSIZE; // Index of video pin int pinIndex = FilterGraphTools.FindPinIndexByMediaType(currentOutputFilter, PinDirection.Input, MediaType.Video, MediaSubType.Null); byte[] bDiMode = BitConverter.GetBytes((int)dimode); short szOf = (short)bDiMode.Length; // Set to use deinterlace mode try { WMWriterAdvanced2.SetInputSetting(pinIndex, g_wszDeinterlaceMode, AttrDataType.DWORD, bDiMode, szOf); } catch (Exception ex) { SendDebugMessageWithException("Could not set interlace mode:", ex); } } } else { SendDebugMessage("Warning - PRX Profile string was empty; using default WM config."); } }
DSStreamResultCodes InitWithStreamBufferFile(WTVStreamingVideoRequest strq) { // Init variables //IPin[] pin = new IPin[1]; IBaseFilter DecFilterAudio = null; IBaseFilter DecFilterVideo = null; IBaseFilter MainAudioDecoder = null; IBaseFilter MainVideoDecoder = null; string dPin = string.Empty; string sName = string.Empty; string dName = string.Empty; string sPin = string.Empty; FileInfo fiInputFile = new FileInfo(strq.FileName); string txtOutputFNPath = fiInputFile.FullName + ".wmv"; if ( (!fiInputFile.Extension.ToLowerInvariant().Equals(".wtv")) && (!fiInputFile.Extension.ToLowerInvariant().Equals(".dvr-ms")) ) { return(DSStreamResultCodes.ErrorInvalidFileType); } int hr = 0; try { // Get the graphbuilder interface SendDebugMessage("Creating Graph Object", 0); IGraphBuilder graphbuilder = (IGraphBuilder)currentFilterGraph; // Add the DVRMS/WTV file / filter to the graph SendDebugMessage("Add SBE Source Filter", 0); hr = graphbuilder.AddSourceFilter(fiInputFile.FullName, "SBE Filter", out currentSBEfilter); // class variable DsError.ThrowExceptionForHR(hr); dc.Add(currentSBEfilter); // Get the SBE audio and video out pins IPin SBEVidOutPin, SBEAudOutPin; SBEAudOutPin = FilterGraphTools.FindPinByMediaType(currentSBEfilter, PinDirection.Output, MediaType.Audio, MediaSubType.Null); SBEVidOutPin = FilterGraphTools.FindPinByMediaType(currentSBEfilter, PinDirection.Output, MediaType.Video, MediaSubType.Null); // Set up two decrypt filters according to file extension (assume audio and video both present ) if (fiInputFile.Extension.ToLowerInvariant().Equals(".dvr-ms")) { // Add DVR-MS decrypt filters SendDebugMessage("Add DVRMS (bda) decryption", 0); DecFilterAudio = (IBaseFilter) new DTFilter(); // THESE ARE FOR DVR-MS (BDA DTFilters) DecFilterVideo = (IBaseFilter) new DTFilter(); graphbuilder.AddFilter(DecFilterAudio, "Decrypt / Tag"); graphbuilder.AddFilter(DecFilterVideo, "Decrypt / Tag 0001"); } else // Add WTV decrypt filters { SendDebugMessage("Add WTV (pbda) decryption", 0); DecFilterAudio = FilterDefinition.AddToFilterGraph(FilterDefinitions.Decrypt.DTFilterPBDA, ref graphbuilder); DecFilterVideo = FilterDefinition.AddToFilterGraph(FilterDefinitions.Decrypt.DTFilterPBDA, ref graphbuilder, "PBDA DTFilter 0001"); } dc.Add(DecFilterAudio); dc.Add(DecFilterVideo); // Make the first link in the graph: SBE => Decrypts SendDebugMessage("Connect SBE => Decrypt filters", 0); IPin DecVideoInPin = DsFindPin.ByDirection(DecFilterVideo, PinDirection.Input, 0); FilterGraphTools.ConnectFilters(graphbuilder, SBEVidOutPin, DecVideoInPin, false); IPin DecAudioInPin = DsFindPin.ByDirection(DecFilterAudio, PinDirection.Input, 0); if (DecAudioInPin == null) { SendDebugMessage("WARNING: No Audio Input to decrypt filter."); } else { FilterGraphTools.ConnectFilters(graphbuilder, SBEAudOutPin, DecAudioInPin, false); } // Get Dec Audio Out pin IPin DecAudioOutPin = DsFindPin.ByDirection(DecFilterAudio, PinDirection.Output, 0); // Examine Dec Audio out for audio format SendDebugMessage("Examining source audio", 0); AMMediaType AudioMediaType = null; getPinMediaType(DecAudioOutPin, MediaType.Audio, Guid.Empty, Guid.Empty, ref AudioMediaType); SendDebugMessage("Audio media subtype: " + AudioMediaType.subType.ToString()); SendDebugMessage("Examining Audio StreamInfo"); StreamInfo si = FileInformation.GetStreamInfo(AudioMediaType); bool AudioIsAC3 = (si.SimpleType == "AC-3"); if (AudioIsAC3) { SendDebugMessage("Audio type is AC3"); } else { SendDebugMessage("Audio type is not AC3"); } si = null; DsUtils.FreeAMMediaType(AudioMediaType); // Add an appropriate audio decoder if (AudioIsAC3) { if (!FilterGraphTools.IsThisComObjectInstalled(FilterDefinitions.Audio.AudioDecoderMPCHC.CLSID)) { SendDebugMessage("Missing AC3 Audio Decoder, and AC3 audio detected."); return(DSStreamResultCodes.ErrorAC3CodecNotFound); } else { MainAudioDecoder = FilterDefinition.AddToFilterGraph(FilterDefinitions.Audio.AudioDecoderMPCHC, ref graphbuilder); //MainAudioDecoder = FatAttitude.WTVTranscoder.FilterDefinitions.Audio.AudioDecoderFFDShow.AddToFilterGraph(ref graph); Guid tmpGuid; MainAudioDecoder.GetClassID(out tmpGuid); SendDebugMessage("Main Audio decoder CLSID is " + tmpGuid.ToString()); } } else { MainAudioDecoder = FilterDefinition.AddToFilterGraph(FilterDefinitions.Audio.AudioDecoderMSDTV, ref graphbuilder); } // Add a video decoder SendDebugMessage("Add DTV decoder", 0); MainVideoDecoder = FilterDefinition.AddToFilterGraph(FilterDefinitions.Video.VideoDecoderMSDTV, ref graphbuilder); dc.Add(MainAudioDecoder); dc.Add(MainVideoDecoder); //SetAudioDecoderOutputToPCMStereo(MainAudioDecoder); // Add a null renderer SendDebugMessage("Add null renderer", 0); NullRenderer MyNullRenderer = new NullRenderer(); dc.Add(MyNullRenderer); hr = graphbuilder.AddFilter((IBaseFilter)MyNullRenderer, @"Null Renderer"); DsError.ThrowExceptionForHR(hr); // Link up video through to null renderer SendDebugMessage("Connect video to null renderer", 0); // Make the second link: Decrypts => DTV IPin DecVideoOutPin = DsFindPin.ByDirection(DecFilterVideo, PinDirection.Output, 0); IPin DTVVideoInPin = DsFindPin.ByName(MainVideoDecoder, @"Video Input"); // IPin DTVVideoInPin = DsFindPin.ByDirection(DTVVideoDecoder, PinDirection.Input, 0); // first one should be video input? // FilterGraphTools.ConnectFilters(graphbuilder, DecVideoOutPin, DTVVideoInPin, false); // 3. DTV => Null renderer IPin NullRInPin = DsFindPin.ByDirection((IBaseFilter)MyNullRenderer, PinDirection.Input, 0); IPin DTVVideoOutPin = FilterGraphTools.FindPinByMediaType(MainVideoDecoder, PinDirection.Output, MediaType.Video, MediaSubType.Null); FilterGraphTools.ConnectFilters(graphbuilder, DTVVideoOutPin, NullRInPin, false); Marshal.ReleaseComObject(NullRInPin); NullRInPin = null; // Run graph [can use this also to get media type => see, e.g. dvrmstowmvhd by Babgvant] SendDebugMessage("Run graph for testing purposes", 0); IMediaControl tempControl = (IMediaControl)graphbuilder; IMediaEvent tempEvent = (IMediaEvent)graphbuilder; DsError.ThrowExceptionForHR(tempControl.Pause()); DsError.ThrowExceptionForHR(tempControl.Run()); EventCode pEventCode; hr = tempEvent.WaitForCompletion(1000, out pEventCode); //DsError.ThrowExceptionForHR(hr); // DO *NOT* DO THIS HERE! THERE MAY WELL BE AN ERROR DUE TO EVENTS RAISED BY THE STREAM BUFFER ENGINE, THIS IS A DELIBERATE TEST RUN OF THE GRAPH // Stop graph if necessary FilterState pFS; hr = tempControl.GetState(1000, out pFS); if (pFS == FilterState.Running) { DsError.ThrowExceptionForHR(tempControl.Stop()); } // Remove null renderer hr = graphbuilder.RemoveFilter((IBaseFilter)MyNullRenderer); // Now graph has been run and stopped we can get the video width and height from the output pin of the main video decoder AMMediaType pmt = null; getPinMediaType(DTVVideoOutPin, MediaType.Video, MediaSubType.YUY2, Guid.Empty, ref pmt); FrameSize SourceFrameSize; if (pmt.formatType == FormatType.VideoInfo2) { VideoInfoHeader2 pvih2 = new VideoInfoHeader2(); Marshal.PtrToStructure(pmt.formatPtr, pvih2); int VideoWidth = pvih2.BmiHeader.Width; int VideoHeight = pvih2.BmiHeader.Height; SourceFrameSize = new FrameSize(VideoWidth, VideoHeight); } else { SourceFrameSize = new FrameSize(320, 240); } // Free up DsUtils.FreeAMMediaType(pmt); pmt = null; // Link up audio // 2. Audio Decrypt -> Audio decoder IPin MainAudioInPin = DsFindPin.ByDirection(MainAudioDecoder, PinDirection.Input, 0); FilterGraphTools.ConnectFilters(graphbuilder, DecAudioOutPin, MainAudioInPin, false); // Add ASF Writer // Create an ASF writer filter SendDebugMessage("Creating ASF Writer", 0); WMAsfWriter asf_filter = new WMAsfWriter(); dc.Add(asf_filter); // CHECK FOR ERRORS currentOutputFilter = (IBaseFilter)asf_filter; // class variable // Add the ASF filter to the graph hr = graphbuilder.AddFilter((IBaseFilter)asf_filter, "WM Asf Writer"); DsError.ThrowExceptionForHR(hr); // Set the filename IFileSinkFilter sinkFilter = (IFileSinkFilter)asf_filter; string destPathFN = fiInputFile.FullName + ".wmv"; hr = sinkFilter.SetFileName(destPathFN, null); DsError.ThrowExceptionForHR(hr); // Make the final links: DTV => writer SendDebugMessage("Linking audio/video through to decoder and writer", 0); IPin DTVAudioOutPin = DsFindPin.ByDirection(MainAudioDecoder, PinDirection.Output, 0); IPin ASFAudioInputPin = FilterGraphTools.FindPinByMediaType((IBaseFilter)asf_filter, PinDirection.Input, MediaType.Audio, MediaSubType.Null); IPin ASFVideoInputPin = FilterGraphTools.FindPinByMediaType((IBaseFilter)asf_filter, PinDirection.Input, MediaType.Video, MediaSubType.Null); FilterGraphTools.ConnectFilters(graphbuilder, DTVAudioOutPin, ASFAudioInputPin, false); if (ASFVideoInputPin != null) { FilterGraphTools.ConnectFilters(graphbuilder, DTVVideoOutPin, ASFVideoInputPin, false); } // Configure ASFWriter ConfigureASFWriter(asf_filter, strq, SourceFrameSize); // Release pins SendDebugMessage("Releasing COM objects (pins)", 0); // dec Marshal.ReleaseComObject(DecAudioInPin); DecAudioInPin = null; Marshal.ReleaseComObject(DecVideoInPin); DecVideoInPin = null; Marshal.ReleaseComObject(DecVideoOutPin); DecVideoOutPin = null; Marshal.ReleaseComObject(DecAudioOutPin); DecAudioOutPin = null; // dtv Marshal.ReleaseComObject(MainAudioInPin); MainAudioInPin = null; Marshal.ReleaseComObject(DTVVideoInPin); DTVVideoInPin = null; Marshal.ReleaseComObject(DTVVideoOutPin); DTVVideoOutPin = null; Marshal.ReleaseComObject(DTVAudioOutPin); DTVAudioOutPin = null; // asf Marshal.ReleaseComObject(ASFAudioInputPin); ASFAudioInputPin = null; Marshal.ReleaseComObject(ASFVideoInputPin); ASFVideoInputPin = null; } catch (Exception ex) { SendDebugMessageWithException(ex.Message, ex); return(DSStreamResultCodes.ErrorExceptionOccurred); } return(DSStreamResultCodes.OK); }
// Main public method - Begin Streaming public WTVStreamingVideoResult StreamWithFileAndPort(WTVStreamingVideoRequest svrq, int streamPort, bool removeReferenceClock, bool autodetectPal) { WTVStreamingVideoResult result = new WTVStreamingVideoResult(); // Attempting this port result.Port = streamPort.ToString(); // Store values locally for use by other class methods StreamingRequest = svrq; StreamPort = streamPort; RemoveReferenceClock = removeReferenceClock; // Build the graph using the base class DSStreamResultCodes resultCode = base.InitWithFile(StreamingRequest); if (resultCode != DSStreamResultCodes.OK) { return(new WTVStreamingVideoResult(resultCode)); } // Add streaming try { AddStreamSinkToCurrentOutputFilter(StreamPort); } catch (Exception ex) { result.ResultCode = DSStreamResultCodes.ErrorAlreadyStreaming; result.ResultString = ex.Message; return(result); } // Remove clock? try { if (RemoveReferenceClock) { RemoveRefClockFromGraph(); } } catch (Exception ex) { result.ResultCode = DSStreamResultCodes.Error; result.ResultString = "Error removing reference clock: " + ex.Message; return(result); } // Seek? try { if (StreamingRequest.StartAt.TotalSeconds > 0) { SeekGraphToTime(StreamingRequest.StartAt); } else { SeekGraphToTime(TimeSpan.FromSeconds(5)); // EXPERIMENTAL } } catch { // Ignore non-seeking errors for now } // Begin streaming the graph in a separate thread graphStartedEvent.Reset(); thStreamThread = new Thread(new ThreadStart(DoStreamWithFileAndPort)); thStreamThread.SetApartmentState(ApartmentState.MTA); // NetworkSink fails in STA threads - avoid! thStreamThread.Name = "StreamFile1"; thStreamThread.Start(); // Wait for graph to attempt to start so we can report back bool gotSignal = (graphStartedEvent.WaitOne(6000)); if (gotSignal) { if (graphIsRunning) { result.ResultCode = DSStreamResultCodes.OK; return(result); } else { result.ResultCode = DSStreamResultCodes.Error; result.ResultString = "DSStreamer: Graph could not run - see server log for more information."; return(result); } } else { result.ResultCode = DSStreamResultCodes.Error; result.ResultString = "DSStreamer: Timed out waiting for graph to run - there may be more information in the server log."; return(result); } }
/// <summary> /// Use XMLDocument to go through a PRX string and change the video encode settings /// </summary> /// <param name="txtWMPrf"></param> /// <param name="strq"></param> private void SetProfileCustomSettings(ref string txtWMPrf, ref WTVStreamingVideoRequest strq) { if (strq.Quality != WTVProfileQuality.Custom) return; SendDebugMessage("Setting WMProfile Custom settings: Video Bitrate:" + strq.CustomVideoBitrate.ToString() + "bps and Smoothness: " + strq.CustomEncoderSmoothness.ToString()); XmlDocument xDoc = new XmlDocument(); xDoc.LoadXml(txtWMPrf); XmlNode root = xDoc.DocumentElement; // Configure first located video stream foreach (XmlNode rootchild in root.ChildNodes) { if (rootchild.Name == "streamconfig") { if (AttributeEqualsValue(rootchild.Attributes["majortype"], PRX_GuidVideoStream)) { XmlNode passChild = rootchild; AddCustomValuesToVideoStreamConfig(ref passChild, ref strq); break; } } } // SAVE txtWMPrf = xDoc.InnerXml; }
DSStreamResultCodes InitWithVideoFile(WTVStreamingVideoRequest strq) { UsingSBEFilter = false; // Not using stream buffer // Init variables IPin[] pin = new IPin[1]; string dPin = string.Empty; string sName = string.Empty; string dName = string.Empty; string sPin = string.Empty; FileInfo fiInputFile = new FileInfo(strq.FileName); string txtOutputFNPath = fiInputFile.FullName + ".wmv"; if ( (fiInputFile.Extension.ToLowerInvariant().Equals(".wtv")) || (fiInputFile.Extension.ToLowerInvariant().Equals(".dvr-ms")) ) return DSStreamResultCodes.ErrorInvalidFileType; int hr = 0; try { // Get the graphbuilder interface SendDebugMessage("Creating Graph Object", 0); IGraphBuilder graphbuilder = (IGraphBuilder)currentFilterGraph; // Create an ASF writer filter SendDebugMessage("Creating ASF Writer", 0); WMAsfWriter asf_filter = new WMAsfWriter(); dc.Add(asf_filter); // CHECK FOR ERRORS currentOutputFilter = (IBaseFilter)asf_filter; // class variable // Add the ASF filter to the graph hr = graphbuilder.AddFilter((IBaseFilter)asf_filter, "WM Asf Writer"); DsError.ThrowExceptionForHR(hr); // Set the filename SendDebugMessage("Setting filename", 0); IFileSinkFilter sinkFilter = (IFileSinkFilter)asf_filter; string destPathFN = fiInputFile.FullName + ".wmv"; hr = sinkFilter.SetFileName(destPathFN, null); DsError.ThrowExceptionForHR(hr); // Handy to have an ACM Wrapper filter hanging around for AVI files with MP3 audio SendDebugMessage("Adding ACM Wrapper", 0); IBaseFilter ACMFilter = FilterDefinition.AddToFilterGraph(FilterDefinitions.Other.ACMWrapperFilter, ref graphbuilder); dc.Add(ACMFilter); // Render file - then build graph SendDebugMessage("Rendering file", 0); graphbuilder.RenderFile(fiInputFile.FullName, null); SendDebugMessage("Saving graph", 0); FilterGraphTools.SaveGraphFile(graphbuilder, "C:\\ProgramData\\RemotePotato\\lastfiltergraph.grf"); // Are both our ASF pins connected? IPin ASFVidInputPin = FilterGraphTools.FindPinByMediaType((IBaseFilter)asf_filter, PinDirection.Input, MediaType.Video, MediaSubType.Null); IPin ASFAudInputPin = FilterGraphTools.FindPinByMediaType((IBaseFilter)asf_filter, PinDirection.Input, MediaType.Audio, MediaSubType.Null); // Run graph [can use this also to get media type => see, e.g. dvrmstowmvhd by Babgvant] SendDebugMessage("Run graph for testing purposes", 0); IMediaControl tempControl = (IMediaControl)graphbuilder; IMediaEvent tempEvent = (IMediaEvent)graphbuilder; DsError.ThrowExceptionForHR(tempControl.Pause()); EventCode pEventCode; hr = tempEvent.WaitForCompletion(1000, out pEventCode); // Get media type from vid input pin for ASF writer AMMediaType pmt = new AMMediaType(); hr = ASFVidInputPin.ConnectionMediaType(pmt); FrameSize SourceFrameSize = null; if (pmt.formatType == FormatType.VideoInfo2) { // Now graph has been run and stopped we can get the video width and height from the output pin of the main video decoder VideoInfoHeader2 pvih2 = new VideoInfoHeader2(); Marshal.PtrToStructure(pmt.formatPtr, pvih2); SourceFrameSize = new FrameSize(pvih2.BmiHeader.Width, pvih2.BmiHeader.Height); } else if (pmt.formatType == FormatType.VideoInfo) //{05589f80-c356-11ce-bf01-00aa0055595a} { VideoInfoHeader pvih = new VideoInfoHeader(); Marshal.PtrToStructure(pmt.formatPtr, pvih); SourceFrameSize = new FrameSize(pvih.BmiHeader.Width, pvih.BmiHeader.Height); } else SourceFrameSize = new FrameSize(200, 200); // SQUARE // Stop graph if necessary FilterState pFS; hr = tempControl.GetState(1000, out pFS); if (pFS != FilterState.Stopped) DsError.ThrowExceptionForHR(tempControl.Stop()); // Free up media type DsUtils.FreeAMMediaType(pmt); pmt = null; // (re)Configure the ASF writer with the selected WM Profile ConfigureASFWriter(asf_filter, strq, SourceFrameSize); // Release pins SendDebugMessage("Releasing COM objects (pins)", 0); // source Marshal.ReleaseComObject(ASFVidInputPin); ASFVidInputPin = null; Marshal.ReleaseComObject(ASFAudInputPin); ASFAudInputPin = null; } catch (Exception ex) { SendDebugMessageWithException(ex.Message, ex); return DSStreamResultCodes.ErrorExceptionOccurred; } return DSStreamResultCodes.OK; }
DSStreamResultCodes InitWithStreamBufferFile(WTVStreamingVideoRequest strq) { // Init variables //IPin[] pin = new IPin[1]; IBaseFilter DecFilterAudio = null; IBaseFilter DecFilterVideo = null; IBaseFilter MainAudioDecoder = null; IBaseFilter MainVideoDecoder = null; string dPin = string.Empty; string sName = string.Empty; string dName = string.Empty; string sPin = string.Empty; FileInfo fiInputFile = new FileInfo(strq.FileName); string txtOutputFNPath = fiInputFile.FullName + ".wmv"; if ( (!fiInputFile.Extension.ToLowerInvariant().Equals(".wtv")) && (!fiInputFile.Extension.ToLowerInvariant().Equals(".dvr-ms")) ) return DSStreamResultCodes.ErrorInvalidFileType; int hr = 0; try { // Get the graphbuilder interface SendDebugMessage("Creating Graph Object",0); IGraphBuilder graphbuilder = (IGraphBuilder)currentFilterGraph; // Add the DVRMS/WTV file / filter to the graph SendDebugMessage("Add SBE Source Filter", 0); hr = graphbuilder.AddSourceFilter(fiInputFile.FullName, "SBE Filter", out currentSBEfilter); // class variable DsError.ThrowExceptionForHR(hr); dc.Add(currentSBEfilter); // Get the SBE audio and video out pins IPin SBEVidOutPin, SBEAudOutPin; SBEAudOutPin = FilterGraphTools.FindPinByMediaType(currentSBEfilter, PinDirection.Output, MediaType.Audio, MediaSubType.Null); SBEVidOutPin = FilterGraphTools.FindPinByMediaType(currentSBEfilter, PinDirection.Output, MediaType.Video, MediaSubType.Null); // Set up two decrypt filters according to file extension (assume audio and video both present ) if (fiInputFile.Extension.ToLowerInvariant().Equals(".dvr-ms")) { // Add DVR-MS decrypt filters SendDebugMessage("Add DVRMS (bda) decryption", 0); DecFilterAudio = (IBaseFilter)new DTFilter(); // THESE ARE FOR DVR-MS (BDA DTFilters) DecFilterVideo = (IBaseFilter)new DTFilter(); graphbuilder.AddFilter(DecFilterAudio, "Decrypt / Tag"); graphbuilder.AddFilter(DecFilterVideo, "Decrypt / Tag 0001"); } else // Add WTV decrypt filters { SendDebugMessage("Add WTV (pbda) decryption", 0); DecFilterAudio = FilterDefinition.AddToFilterGraph(FilterDefinitions.Decrypt.DTFilterPBDA, ref graphbuilder); DecFilterVideo = FilterDefinition.AddToFilterGraph(FilterDefinitions.Decrypt.DTFilterPBDA, ref graphbuilder, "PBDA DTFilter 0001"); } dc.Add(DecFilterAudio); dc.Add(DecFilterVideo); // Make the first link in the graph: SBE => Decrypts SendDebugMessage("Connect SBE => Decrypt filters", 0); IPin DecVideoInPin = DsFindPin.ByDirection(DecFilterVideo, PinDirection.Input, 0); FilterGraphTools.ConnectFilters(graphbuilder, SBEVidOutPin, DecVideoInPin, false); IPin DecAudioInPin = DsFindPin.ByDirection(DecFilterAudio, PinDirection.Input, 0); if (DecAudioInPin == null) SendDebugMessage("WARNING: No Audio Input to decrypt filter."); else FilterGraphTools.ConnectFilters(graphbuilder, SBEAudOutPin, DecAudioInPin, false); // Get Dec Audio Out pin IPin DecAudioOutPin = DsFindPin.ByDirection(DecFilterAudio, PinDirection.Output, 0); // Examine Dec Audio out for audio format SendDebugMessage("Examining source audio", 0); AMMediaType AudioMediaType = null; getPinMediaType(DecAudioOutPin, MediaType.Audio, Guid.Empty, Guid.Empty, ref AudioMediaType); SendDebugMessage("Audio media subtype: " + AudioMediaType.subType.ToString()); SendDebugMessage("Examining Audio StreamInfo"); StreamInfo si = FileInformation.GetStreamInfo(AudioMediaType); bool AudioIsAC3 = (si.SimpleType == "AC-3"); if (AudioIsAC3) SendDebugMessage("Audio type is AC3"); else SendDebugMessage("Audio type is not AC3"); si = null; DsUtils.FreeAMMediaType(AudioMediaType); // Add an appropriate audio decoder if (AudioIsAC3) { if (!FilterGraphTools.IsThisComObjectInstalled(FilterDefinitions.Audio.AudioDecoderMPCHC.CLSID)) { SendDebugMessage("Missing AC3 Audio Decoder, and AC3 audio detected."); return DSStreamResultCodes.ErrorAC3CodecNotFound; } else { MainAudioDecoder = FilterDefinition.AddToFilterGraph(FilterDefinitions.Audio.AudioDecoderMPCHC, ref graphbuilder); //MainAudioDecoder = FatAttitude.WTVTranscoder.FilterDefinitions.Audio.AudioDecoderFFDShow.AddToFilterGraph(ref graph); Guid tmpGuid; MainAudioDecoder.GetClassID(out tmpGuid); SendDebugMessage("Main Audio decoder CLSID is " + tmpGuid.ToString()); } } else MainAudioDecoder = FilterDefinition.AddToFilterGraph(FilterDefinitions.Audio.AudioDecoderMSDTV, ref graphbuilder); // Add a video decoder SendDebugMessage("Add DTV decoder", 0); MainVideoDecoder = FilterDefinition.AddToFilterGraph(FilterDefinitions.Video.VideoDecoderMSDTV, ref graphbuilder); dc.Add(MainAudioDecoder); dc.Add(MainVideoDecoder); //SetAudioDecoderOutputToPCMStereo(MainAudioDecoder); // Add a null renderer SendDebugMessage("Add null renderer", 0); NullRenderer MyNullRenderer = new NullRenderer(); dc.Add(MyNullRenderer); hr = graphbuilder.AddFilter((IBaseFilter)MyNullRenderer, @"Null Renderer"); DsError.ThrowExceptionForHR(hr); // Link up video through to null renderer SendDebugMessage("Connect video to null renderer", 0); // Make the second link: Decrypts => DTV IPin DecVideoOutPin = DsFindPin.ByDirection(DecFilterVideo, PinDirection.Output, 0); IPin DTVVideoInPin = DsFindPin.ByName(MainVideoDecoder, @"Video Input"); // IPin DTVVideoInPin = DsFindPin.ByDirection(DTVVideoDecoder, PinDirection.Input, 0); // first one should be video input? // FilterGraphTools.ConnectFilters(graphbuilder, DecVideoOutPin, DTVVideoInPin, false); // 3. DTV => Null renderer IPin NullRInPin = DsFindPin.ByDirection((IBaseFilter)MyNullRenderer, PinDirection.Input, 0); IPin DTVVideoOutPin = FilterGraphTools.FindPinByMediaType(MainVideoDecoder, PinDirection.Output, MediaType.Video, MediaSubType.Null); FilterGraphTools.ConnectFilters(graphbuilder, DTVVideoOutPin, NullRInPin, false); Marshal.ReleaseComObject(NullRInPin); NullRInPin = null; // Run graph [can use this also to get media type => see, e.g. dvrmstowmvhd by Babgvant] SendDebugMessage("Run graph for testing purposes", 0); IMediaControl tempControl = (IMediaControl)graphbuilder; IMediaEvent tempEvent = (IMediaEvent)graphbuilder; DsError.ThrowExceptionForHR(tempControl.Pause()); DsError.ThrowExceptionForHR(tempControl.Run()); EventCode pEventCode; hr = tempEvent.WaitForCompletion(1000, out pEventCode); //DsError.ThrowExceptionForHR(hr); // DO *NOT* DO THIS HERE! THERE MAY WELL BE AN ERROR DUE TO EVENTS RAISED BY THE STREAM BUFFER ENGINE, THIS IS A DELIBERATE TEST RUN OF THE GRAPH // Stop graph if necessary FilterState pFS; hr = tempControl.GetState(1000, out pFS); if (pFS == FilterState.Running) DsError.ThrowExceptionForHR(tempControl.Stop()); // Remove null renderer hr = graphbuilder.RemoveFilter((IBaseFilter)MyNullRenderer); // Now graph has been run and stopped we can get the video width and height from the output pin of the main video decoder AMMediaType pmt = null; getPinMediaType(DTVVideoOutPin, MediaType.Video, MediaSubType.YUY2, Guid.Empty, ref pmt); FrameSize SourceFrameSize; if (pmt.formatType == FormatType.VideoInfo2) { VideoInfoHeader2 pvih2 = new VideoInfoHeader2(); Marshal.PtrToStructure(pmt.formatPtr, pvih2); int VideoWidth = pvih2.BmiHeader.Width; int VideoHeight = pvih2.BmiHeader.Height; SourceFrameSize = new FrameSize(VideoWidth, VideoHeight); } else SourceFrameSize = new FrameSize(320, 240); // Free up DsUtils.FreeAMMediaType(pmt); pmt = null; // Link up audio // 2. Audio Decrypt -> Audio decoder IPin MainAudioInPin = DsFindPin.ByDirection(MainAudioDecoder, PinDirection.Input, 0); FilterGraphTools.ConnectFilters(graphbuilder, DecAudioOutPin, MainAudioInPin, false); // Add ASF Writer // Create an ASF writer filter SendDebugMessage("Creating ASF Writer", 0); WMAsfWriter asf_filter = new WMAsfWriter(); dc.Add(asf_filter); // CHECK FOR ERRORS currentOutputFilter = (IBaseFilter)asf_filter; // class variable // Add the ASF filter to the graph hr = graphbuilder.AddFilter((IBaseFilter)asf_filter, "WM Asf Writer"); DsError.ThrowExceptionForHR(hr); // Set the filename IFileSinkFilter sinkFilter = (IFileSinkFilter)asf_filter; string destPathFN = fiInputFile.FullName + ".wmv"; hr = sinkFilter.SetFileName(destPathFN, null); DsError.ThrowExceptionForHR(hr); // Make the final links: DTV => writer SendDebugMessage("Linking audio/video through to decoder and writer", 0); IPin DTVAudioOutPin = DsFindPin.ByDirection(MainAudioDecoder, PinDirection.Output, 0); IPin ASFAudioInputPin = FilterGraphTools.FindPinByMediaType((IBaseFilter)asf_filter, PinDirection.Input, MediaType.Audio, MediaSubType.Null); IPin ASFVideoInputPin = FilterGraphTools.FindPinByMediaType((IBaseFilter)asf_filter, PinDirection.Input, MediaType.Video, MediaSubType.Null); FilterGraphTools.ConnectFilters(graphbuilder, DTVAudioOutPin, ASFAudioInputPin, false); if (ASFVideoInputPin != null) FilterGraphTools.ConnectFilters(graphbuilder, DTVVideoOutPin, ASFVideoInputPin, false); // Configure ASFWriter ConfigureASFWriter(asf_filter, strq, SourceFrameSize); // Release pins SendDebugMessage("Releasing COM objects (pins)", 0); // dec Marshal.ReleaseComObject(DecAudioInPin); DecAudioInPin = null; Marshal.ReleaseComObject(DecVideoInPin); DecVideoInPin = null; Marshal.ReleaseComObject(DecVideoOutPin); DecVideoOutPin = null; Marshal.ReleaseComObject(DecAudioOutPin); DecAudioOutPin = null; // dtv Marshal.ReleaseComObject(MainAudioInPin); MainAudioInPin = null; Marshal.ReleaseComObject(DTVVideoInPin); DTVVideoInPin = null; Marshal.ReleaseComObject(DTVVideoOutPin); DTVVideoOutPin = null; Marshal.ReleaseComObject(DTVAudioOutPin); DTVAudioOutPin = null; // asf Marshal.ReleaseComObject(ASFAudioInputPin); ASFAudioInputPin = null; Marshal.ReleaseComObject(ASFVideoInputPin); ASFVideoInputPin = null; } catch (Exception ex) { SendDebugMessageWithException(ex.Message, ex); return DSStreamResultCodes.ErrorExceptionOccurred; } return DSStreamResultCodes.OK; }
FrameSize frameSizeForStreamRequest(WTVStreamingVideoRequest strq) { switch (strq.Quality) { case WTVProfileQuality.Low: return new FrameSize(160, 120); case WTVProfileQuality.Normal: return new FrameSize(160, 120); case WTVProfileQuality.Med: return new FrameSize(320, 240); case WTVProfileQuality.High: return new FrameSize(320, 240); case WTVProfileQuality.UltraHigh: return new FrameSize(512, 384); case WTVProfileQuality.Test: return new FrameSize(320, 240); case WTVProfileQuality.Custom: return new FrameSize(strq.CustomFrameWidth, strq.CustomFrameHeight); default: return new FrameSize(320, 240); } }
private bool StreamVideoUsingTVProgramme(TVProgramme tvp, int Quality, double StartAt, int iPlayerWidth, int iPlayerHeight) { // Get table for display string tblShow = FileCache.ReadSkinTextFile("stream_show.htm"); txtPageTitle = tvp.Title; tblShow = tblShow.Replace("**EPISODETITLE**", tvp.EpisodeTitle); tblShow = tblShow.Replace("**DESCRIPTION**", tvp.Description); // Set up streaming parameters WTVProfileQuality qual = WTVProfileQuality.Normal; switch (Quality) { case 0: qual = WTVProfileQuality.Low; break; case 1: qual = WTVProfileQuality.Normal; break; case 2: qual = WTVProfileQuality.Med; break; case 3: qual = WTVProfileQuality.High; break; case 4: qual = WTVProfileQuality.UltraHigh; break; default: qual = WTVProfileQuality.Low; break; } WTVStreamingVideoRequest svrq = new WTVStreamingVideoRequest(); svrq.FileName = tvp.Filename; svrq.Quality = qual; svrq.StartAt = TimeSpan.FromSeconds(StartAt); svrq.DeInterlaceMode = 1; WTVStreamingVideoResult rs = DSStreamingManager.Default.StartStreamer(svrq); if (rs.ResultCode == DSStreamResultCodes.OK) { // Wait for streaming to start Thread.Sleep(3500); // Compose stream URL StringBuilder sbStreamURL = new StringBuilder(30); sbStreamURL.Append(@"mms://"); sbStreamURL.Append(Request.Url.Host); sbStreamURL.Append(":" + rs.Port.ToString() ); sbStreamURL.Append(@"/tvshow.wmv"); tblShow = tblShow.Replace("**STREAMURL**", sbStreamURL.ToString()); tblShow = tblShow.Replace("**STREAMWIDTH**", iPlayerWidth.ToString()); tblShow = tblShow.Replace("**STREAMHEIGHT**", iPlayerHeight.ToString()); //tblShow = tblShow.Replace("**STOPSTREAMLINK**", "<a href=\"stopstreamprogramme?programmeid=" + tvp.Id.ToString() + "\">Stop streaming</a>"); tblShow = tblShow.Replace("**STOPSTREAMLINK**", ""); } else // NOT OK { string reasonCannotStream = "Cannot stream show - "; if (rs.ResultCode == DSStreamResultCodes.ErrorAlreadyStreaming) reasonCannotStream += "the streamer is already running; please wait a small while and then try again."; else if (rs.ResultCode == DSStreamResultCodes.ErrorAC3CodecNotFound) reasonCannotStream += "the required codecs are not installed; please download the Remote Potato streaming pack from www.fatattitude.com"; else if (rs.ResultCode == DSStreamResultCodes.ErrorFileNotFound) reasonCannotStream += "the file was not found"; else if (rs.ResultCode == DSStreamResultCodes.ErrorExceptionOccurred) reasonCannotStream += "an exception occurred: " + rs.ResultString; else reasonCannotStream += "an error occurred. " + rs.ResultString; tblShow = tblShow.Replace("**STREAMOBJECT**", reasonCannotStream); tblShow = tblShow.Replace("**STOPSTREAMLINK**", ""); } // Commit to displaying show info txtResponse += tblShow; return true; }
void ConfigureASFWriter(WMAsfWriter asf_filter, WTVStreamingVideoRequest strq, FrameSize SourceFrameSize) { int hr; // Now it's added to the graph, configure it with the selected WM Profile SendDebugMessage("Getting WM profile with quality of " + strq.Quality.ToString(), 0); WindowsMediaLib.IWMProfileManager profileManager; WMUtils.WMCreateProfileManager(out profileManager); IWMProfile wmProfile; string txtPrxProfile = getPRXProfileForQuality(strq.Quality); if (!(string.IsNullOrEmpty(txtPrxProfile))) { SendDebugMessage("Adjusting WM profile to fit video within designated frame size", 0); // SET VIDEO SIZE TO FIT WITHIN THE RIGHT FRAME SendDebugMessage("Source video size is " + SourceFrameSize.ToString(), 0); FrameSize containerSize = frameSizeForStreamRequest(strq); SendDebugMessage("Container size is " + containerSize.ToString(), 0); FrameSize newVideoSize = new FrameSize(SourceFrameSize, containerSize); SendDebugMessage("Output size is " + newVideoSize.ToString(), 0); SetProfileFrameSize(ref txtPrxProfile, newVideoSize); SetProfileCustomSettings(ref txtPrxProfile, ref strq); // returns immediately if not custom quality SendDebugMessage("Configuring ASF Writer with profile", 0); profileManager.LoadProfileByData(txtPrxProfile, out wmProfile); WindowsMediaLib.IConfigAsfWriter configWriter = (WindowsMediaLib.IConfigAsfWriter)asf_filter; configWriter.ConfigureFilterUsingProfile(wmProfile); configWriter.SetIndexMode(true); // yes index - DEFAULT /* Additional config - TEST * //DirectShowLib.IConfigAsfWriter2 configAsfWriter2 = (DirectShowLib.IConfigAsfWriter2)asf_filter; * //configAsfWriter2.SetParam(ASFWriterConfig.AutoIndex, 0, 0); // IT IS DEFAULT */ // (NOT WORKING) // SET ANAMORPHIC VIDEO MARKERS WITHIN STREAM (ASPECT RATIO) ******************************* UInt32 uiAspectX = (UInt32)SourceFrameSize.Width; byte[] bAspectX = BitConverter.GetBytes(uiAspectX); UInt32 uiAspectY = (UInt32)SourceFrameSize.Height; byte[] bAspectY = BitConverter.GetBytes(uiAspectY); DirectShowLib.IServiceProvider pServiceProvider; // http://msdn.microsoft.com/en-us/library/dd390985%28VS.85%29.aspx pServiceProvider = (DirectShowLib.IServiceProvider)asf_filter; DsGuid dsgIWMHeaderinfo = DsGuid.FromGuid(new Guid(GUIDs.IWMWriterAdvanced2)); object o3 = null; hr = pServiceProvider.QueryService(dsgIWMHeaderinfo, dsgIWMHeaderinfo, out o3); // FAILS IN A STA THREAD DsError.ThrowExceptionForHR(hr); IWMHeaderInfo headerinfo = (IWMHeaderInfo)o3; // Get access to WMwriterAdvanced2 object using pServiceProvider (poss not futureproof) (see http://groups.google.com/group/microsoft.public.win32.programmer.directx.video/browse_thread/thread/36b154d41cb76ffd/c571d6ef56de11af?#c571d6ef56de11af ) DsGuid dsgWMwriterAdvanced2 = DsGuid.FromGuid(new Guid(GUIDs.IWMWriterAdvanced2)); object o = null; hr = pServiceProvider.QueryService(dsgWMwriterAdvanced2, dsgWMwriterAdvanced2, out o); // FAILS IN A STA THREAD DsError.ThrowExceptionForHR(hr); IWMWriterAdvanced2 WMWriterAdvanced2 = null; WMWriterAdvanced2 = (IWMWriterAdvanced2)o; // Get Access to IWMHeaderInfo3 through WMWriterAdvanced2 object o2 = null; //pServiceProvider = (DirectShowLib.IServiceProvider)WMWriterAdvanced2; DsGuid dsgIWMHeaderInfo3 = DsGuid.FromGuid(new Guid(GUIDs.IWMHeaderInfo3)); hr = pServiceProvider.QueryService(dsgWMwriterAdvanced2, dsgIWMHeaderInfo3, out o2); // LET'S SEE DsError.ThrowExceptionForHR(hr); IWMHeaderInfo3 WMHeaderInfo3 = null; WMHeaderInfo3 = (IWMHeaderInfo3)o2; short pwIndex; // Add Aspect Ratio information WMHeaderInfo3.AddAttribute(2, "AspectRatioX", out pwIndex, AttrDataType.DWORD, 0, bAspectX, bAspectX.Length); WMHeaderInfo3.AddAttribute(2, "AspectRatioY", out pwIndex, AttrDataType.DWORD, 0, bAspectY, bAspectY.Length); // Try with other interface too headerinfo.SetAttribute(2, "AspectRatioX", AttrDataType.DWORD, bAspectX, Convert.ToInt16(bAspectX.Length)); headerinfo.SetAttribute(2, "AspectRatioY", AttrDataType.DWORD, bAspectY, Convert.ToInt16(bAspectY.Length)); // ************ DEINTERLACE (experimental) if (strq.DeInterlaceMode > 0) { DeInterlaceModes dimode = DeInterlaceModes.WM_DM_NOTINTERLACED; // Deinterlace Mode if (strq.DeInterlaceMode == 1) { dimode = DeInterlaceModes.WM_DM_DEINTERLACE_NORMAL; } else if (strq.DeInterlaceMode == 2) { dimode = DeInterlaceModes.WM_DM_DEINTERLACE_HALFSIZE; } // Index of video pin int pinIndex = FilterGraphTools.FindPinIndexByMediaType(currentOutputFilter, PinDirection.Input, MediaType.Video, MediaSubType.Null); byte[] bDiMode = BitConverter.GetBytes((int)dimode); short szOf = (short)bDiMode.Length; // Set to use deinterlace mode try { WMWriterAdvanced2.SetInputSetting(pinIndex, g_wszDeinterlaceMode, AttrDataType.DWORD, bDiMode, szOf); } catch (Exception ex) { SendDebugMessageWithException("Could not set interlace mode:", ex); } } } else { SendDebugMessage("Warning - PRX Profile string was empty; using default WM config."); } }
void AddCustomValuesToVideoStreamConfig(ref XmlNode streamconfig, ref WTVStreamingVideoRequest strq) { // Sanity Checks if (strq.CustomVideoBitrate > 1500000) strq.CustomVideoBitrate = 1500000; if (strq.CustomEncoderSmoothness > 100) strq.CustomEncoderSmoothness = 100; if (strq.CustomVideoBitrate < 10000) strq.CustomVideoBitrate = 10000; if (strq.CustomEncoderSmoothness < 5) strq.CustomEncoderSmoothness = 5; // TODO: More sanity checks // BITRATE (1 of 2) XmlNode xnode; bool foo = SetAttributeIfFound(streamconfig, "bitrate", strq.CustomVideoBitrate ); // QUALITY if (FindChildByName(streamconfig, "videomediaprops", out xnode)) foo = SetAttributeIfFound(xnode, "quality", strq.CustomEncoderSmoothness); XmlNode xnodewmmediatype; if (FindChildByName(streamconfig, "wmmediatype", out xnodewmmediatype)) { if (FindChildByName(xnodewmmediatype, "videoinfoheader", out xnode)) { // BITRATE (2 of 2) foo = SetAttributeIfFound(xnode, "dwbitrate", strq.CustomVideoBitrate); // FPS double avgtime = 10000000 / strq.CustomEncoderFPS; avgtime = Math.Round(avgtime, 0); foo = SetAttributeIfFound(xnode, "avgtimeperframe", avgtime); // TODO: String format } } }