示例#1
0
        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));
            }
        }
示例#2
0
        /// <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;
        }
示例#3
0
 // Constructor
 public DSStreamer() : base()
 {
     StreamingRequest     = null;
     StreamPort           = 9081;
     RemoveReferenceClock = false;
     PendingCommands      = new Queue <StreamCommand>();
 }
示例#4
0
 // 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;
     }
 }
示例#7
0
        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);
            }
        }
示例#8
0
        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;
        }
示例#9
0
        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
                }
            }
        }
示例#10
0
        /// <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));
            }
        }
示例#11
0
        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);
        }
示例#12
0
        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);
        }
示例#13
0
        // 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;
            }
        }
示例#14
0
 /// <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);
 }
示例#15
0
        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.");
            }
        }
示例#16
0
        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);
        }
示例#17
0
        // 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);
            }
        }
示例#18
0
        /// <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;
        }
示例#19
0
        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;
        }
示例#20
0
        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;
        }
示例#21
0
        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);
            }
        }
示例#22
0
        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;
        }
示例#23
0
        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.");
            }
        }
示例#24
0
        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
                }
            }
        }