/// <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); }
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); } }