/// <summary>
        /// This function can be called by a background thread. It finishes building the graph and
        /// waits until the buffer is filled to the configured percentage.
        /// If a filter in the graph requires the full file to be downloaded, the function will return only afterwards.
        /// </summary>
        /// <returns>true, when playback can be started</returns>
        public bool BufferFile()
        {
            Thread renderPinsThread = null;

            VideoRendererStatistics.VideoState = VideoRendererStatistics.State.VideoPresent; // prevents the BlackRectangle on first time playback
            bool        PlaybackReady = false;
            IBaseFilter sourceFilter  = null;

            try
            {
                int result = graphBuilder.FindFilterByName(sourceFilterName, out sourceFilter);
                if (result != 0)
                {
                    string errorText = DirectShowLib.DsError.GetErrorText(result);
                    if (errorText != null)
                    {
                        errorText = errorText.Trim();
                    }
                    Logger.Instance.Warn("BufferFile : FindFilterByName returned '{0}'{1}", "0x" + result.ToString("X8"), !string.IsNullOrEmpty(errorText) ? " : (" + errorText + ")" : "");
                    return(false);
                }

                Marshal.ThrowExceptionForHR(((IFileSourceFilter)sourceFilter).Load(m_strCurrentFile, null));
                OnlineVideos.MPUrlSourceFilter.IFilterState filterState = sourceFilter as OnlineVideos.MPUrlSourceFilter.IFilterState;
                if (sourceFilter is IAMOpenProgress)
                {
                    // buffer before starting playback
                    bool filterConnected = false;
                    percentageBuffered = 0.0f;
                    long total = 0, current = 0, last = 0;
                    do
                    {
                        result = ((IAMOpenProgress)sourceFilter).QueryProgress(out total, out current);
                        Marshal.ThrowExceptionForHR(result);

                        percentageBuffered = (float)current / (float)total * 100.0f;
                        // after configured percentage has been buffered, connect the graph
                        if (!filterConnected && (percentageBuffered >= bufferPercent || skipBuffering))
                        {
                            if (((filterState != null) && (filterState.IsFilterReadyToConnectPins())) ||
                                (filterState == null))
                            {
                                //cacheFile = filterState.GetCacheFileName();
                                if (skipBuffering)
                                {
                                    Logger.Instance.Debug("Buffering skipped at {0}%", percentageBuffered);
                                }
                                filterConnected  = true;
                                renderPinsThread = new Thread(delegate()
                                {
                                    try
                                    {
                                        Logger.Instance.Debug("BufferFile : Rendering unconnected output pins of source filter ...");
                                        // add audio and video filter from MP Movie Codec setting section
                                        AddPreferredFilters(graphBuilder, sourceFilter);
                                        // connect the pin automatically -> will buffer the full file in cases of bad metadata in the file or request of the audio or video filter
                                        DirectShowUtil.RenderUnconnectedOutputPins(graphBuilder, sourceFilter);
                                        Logger.Instance.Debug("BufferFile : Playback Ready.");
                                        PlaybackReady = true;
                                    }
                                    catch (ThreadAbortException)
                                    {
                                        Thread.ResetAbort();
                                        Logger.Instance.Info("RenderUnconnectedOutputPins foribly aborted.");
                                    }
                                    catch (Exception ex)
                                    {
                                        Logger.Instance.Warn(ex.Message);
                                        StopBuffering();
                                    }
                                })
                                {
                                    IsBackground = true, Name = "AirPlayerGraph"
                                };
                                renderPinsThread.Start();
                            }
                        }
                        // log every percent
                        if (current > last && current - last >= (double)total * 0.01)
                        {
                            Logger.Instance.Debug("Buffering: {0}/{1} KB ({2}%)", current / 1024, total / 1024, (int)percentageBuffered);
                            last = current;
                        }
                        // set the percentage to a gui property, formatted according to percentage, so the user knows very early if anything is buffering
                        string formatString = "###";
                        if (percentageBuffered == 0f)
                        {
                            formatString = "0.0";
                        }
                        else if (percentageBuffered < 1f)
                        {
                            formatString = ".00";
                        }
                        else if (percentageBuffered < 10f)
                        {
                            formatString = "0.0";
                        }
                        else if (percentageBuffered < 100f)
                        {
                            formatString = "##";
                        }
                        GUIPropertyManager.SetProperty("#AirPlayerVideo.buffered", percentageBuffered.ToString(formatString, System.Globalization.CultureInfo.InvariantCulture));
                        Thread.Sleep(50); // no need to do this more often than 20 times per second
                    }while (!PlaybackReady && graphBuilder != null && !BufferingStopped);
                }
                else
                {
                    if (filterState != null)
                    {
                        while (!filterState.IsFilterReadyToConnectPins())
                        {
                            Thread.Sleep(50);
                        }
                        //cacheFile = filterState.GetCacheFileName();
                    }
                    // add audio and video filter from MP Movie Codec setting section
                    AddPreferredFilters(graphBuilder, sourceFilter);
                    // connect the pin automatically -> will buffer the full file in cases of bad metadata in the file or request of the audio or video filter
                    DirectShowUtil.RenderUnconnectedOutputPins(graphBuilder, sourceFilter);
                    percentageBuffered = 100.0f; // no progress reporting possible
                    GUIPropertyManager.SetProperty("#TV.Record.percent3", percentageBuffered.ToString());
                    PlaybackReady = true;
                }
            }
            catch (ThreadAbortException)
            {
                Thread.ResetAbort();
            }
            catch (COMException comEx)
            {
                Logger.Instance.Warn(comEx.ToString());

                if (sourceFilterName == MPURL_SOURCE_FILTER &&
                    Enum.IsDefined(typeof(OnlineVideos.MPUrlSourceFilter.MPUrlSourceSplitterError), comEx.ErrorCode))
                {
                    throw new Exception(((OnlineVideos.MPUrlSourceFilter.MPUrlSourceSplitterError)comEx.ErrorCode).ToString());
                }

                string errorText = DirectShowLib.DsError.GetErrorText(comEx.ErrorCode);
                if (errorText != null)
                {
                    errorText = errorText.Trim();
                }
                if (!string.IsNullOrEmpty(errorText))
                {
                    throw new Exception(errorText);
                }
            }
            catch (Exception ex)
            {
                Logger.Instance.Warn(ex.ToString());
            }
            finally
            {
                if (sourceFilter != null)
                {
                    // the render pin thread was already started and is still runnning
                    if (renderPinsThread != null && (renderPinsThread.ThreadState & ThreadState.Stopped) == 0)
                    {
                        // buffering was stopped by the user -> abort the thread
                        if (BufferingStopped)
                        {
                            renderPinsThread.Abort();
                        }
                    }

                    // playback is not ready but the source filter is already downloading -> abort the operation
                    if (!PlaybackReady)
                    {
                        Logger.Instance.Info("Buffering was aborted.");
                        if (sourceFilter != null)
                        {
                            ((IAMOpenProgress)sourceFilter).AbortOperation();
                        }
                        Thread.Sleep(100);                                    // give it some time
                        int result = graphBuilder.RemoveFilter(sourceFilter); // remove the filter from the graph to prevent lockup later in Dispose
                    }

                    // release the COM pointer that we created
                    DirectShowUtil.ReleaseComObject(sourceFilter);
                }
            }

            return(PlaybackReady);
        }
Beispiel #2
0
        public bool Play(string fileName, Control parent, out string ErrorOrSplitter)
        {
            ErrorOrSplitter = "";
            int hr;

            _parentControl = parent;

            _graphBuilder = (IFilterGraph2) new FilterGraph();
            _rotEntry     = new DsROTEntry(_graphBuilder);

            // add the video renderer (evr does not seem to work here)
            IBaseFilter vmr9Renderer = DirectShowUtil.AddFilterToGraph(_graphBuilder, "Video Mixing Renderer 9");

            ((IVMRAspectRatioControl9)vmr9Renderer).SetAspectRatioMode(VMRAspectRatioMode.LetterBox);
            DirectShowUtil.ReleaseComObject(vmr9Renderer, 2000);

            // add the audio renderer
            IBaseFilter audioRenderer = DirectShowUtil.AddAudioRendererToGraph(_graphBuilder, MPSettings.Instance.GetValueAsString("movieplayer", "audiorenderer", "Default DirectSound Device"), false);

            DirectShowUtil.ReleaseComObject(audioRenderer, 2000);

            // add the source filter
            string sourceFilterName = OnlineVideos.MediaPortal1.Player.OnlineVideosPlayer.GetSourceFilterName(fileName);

            if (string.IsNullOrEmpty(sourceFilterName))
            {
                return(false);
            }
            IBaseFilter sourceFilter = null;

            try
            {
                sourceFilter = DirectShowUtil.AddFilterToGraph(_graphBuilder, sourceFilterName);
            }
            catch (Exception ex)
            {
                ErrorOrSplitter = ex.Message;
                return(false);
            }

            hr = ((IFileSourceFilter)sourceFilter).Load(fileName, null);

            if (hr != 0)
            {
                ErrorOrSplitter = DirectShowLib.DsError.GetErrorText(hr);
                DirectShowUtil.ReleaseComObject(sourceFilter, 2000);
                return(false);
            }

            // wait for our filter to buffer before rendering the pins
            OnlineVideos.MPUrlSourceFilter.IFilterState filterState = sourceFilter as OnlineVideos.MPUrlSourceFilter.IFilterState;

            if (filterState != null)
            {
                bool ready = false;

                while ((!ready) && (hr == 0))
                {
                    hr = filterState.IsFilterReadyToConnectPins(out ready);

                    System.Threading.Thread.Sleep(25);
                }
            }

            if (hr != 0)
            {
                ErrorOrSplitter = DirectShowLib.DsError.GetErrorText(hr);
                DirectShowUtil.ReleaseComObject(sourceFilter, 2000);
                return(false);
            }

            OnlineVideos.MediaPortal1.Player.OnlineVideosPlayer.AddPreferredFilters(_graphBuilder, sourceFilter);

            // try to connect the filters
            int       numConnected = 0;
            IEnumPins pinEnum;

            hr = sourceFilter.EnumPins(out pinEnum);
            if ((hr == 0) && (pinEnum != null))
            {
                pinEnum.Reset();
                IPin[] pins = new IPin[1];
                int    iFetched;
                int    iPinNo = 0;
                do
                {
                    iPinNo++;
                    hr = pinEnum.Next(1, pins, out iFetched);
                    if (hr == 0)
                    {
                        if (iFetched == 1 && pins[0] != null)
                        {
                            PinDirection pinDir;
                            pins[0].QueryDirection(out pinDir);
                            if (pinDir == PinDirection.Output)
                            {
                                hr = _graphBuilder.Render(pins[0]);
                                if (hr == 0)
                                {
                                    numConnected++;
                                    IPin connectedPin;
                                    if (pins[0].ConnectedTo(out connectedPin) == 0 && connectedPin != null)
                                    {
                                        PinInfo connectedPinInfo;
                                        connectedPin.QueryPinInfo(out connectedPinInfo);
                                        FilterInfo connectedFilterInfo;
                                        connectedPinInfo.filter.QueryFilterInfo(out connectedFilterInfo);
                                        DirectShowUtil.ReleaseComObject(connectedPin, 2000);
                                        IBaseFilter connectedFilter;
                                        if (connectedFilterInfo.pGraph.FindFilterByName(connectedFilterInfo.achName, out connectedFilter) == 0 && connectedFilter != null)
                                        {
                                            var codecInfo = GetCodecInfo(connectedFilter, connectedFilterInfo.achName);
                                            if (codecInfo != null)
                                            {
                                                if (string.IsNullOrEmpty(ErrorOrSplitter))
                                                {
                                                    ErrorOrSplitter = codecInfo.ToString();
                                                }
                                                else
                                                {
                                                    ErrorOrSplitter += ", " + codecInfo.ToString();
                                                }
                                            }
                                            DirectShowUtil.ReleaseComObject(connectedFilter);
                                        }
                                    }
                                }
                            }
                            DirectShowUtil.ReleaseComObject(pins[0], 2000);
                        }
                    }
                } while (iFetched == 1);
            }
            DirectShowUtil.ReleaseComObject(pinEnum, 2000);

            if (numConnected > 0)
            {
                _videoWin = _graphBuilder as IVideoWindow;
                if (_videoWin != null)
                {
                    _videoWin.put_Owner(_parentControl.Handle);
                    _videoWin.put_WindowStyle((WindowStyle)((int)WindowStyle.Child + (int)WindowStyle.ClipSiblings + (int)WindowStyle.ClipChildren));
                    _videoWin.SetWindowPosition(_parentControl.ClientRectangle.X, _parentControl.ClientRectangle.Y, _parentControl.ClientRectangle.Width, _parentControl.ClientRectangle.Height);
                    _videoWin.put_Visible(OABool.True);
                }

                _mediaCtrl = (IMediaControl)_graphBuilder;
                hr         = _mediaCtrl.Run();

                mediaEvents = (IMediaEventEx)_graphBuilder;
                // Have the graph signal event via window callbacks for performance
                mediaEvents.SetNotifyWindow(_parentControl.FindForm().Handle, WMGraphNotify, IntPtr.Zero);

                _parentControl.SizeChanged += _parentControl_SizeChanged;
                return(true);
            }
            else
            {
                ErrorOrSplitter = string.Format("Could not render output pins of {0}", sourceFilterName);
                DirectShowUtil.ReleaseComObject(sourceFilter, 2000);
                Stop();
                return(false);
            }
        }