public static void LoadAndWaitForMPUrlSourceFilter(string url, OnlineVideos.MPUrlSourceFilter.IFilterStateEx filterStateEx) { //string url = ApplyMPUrlSourceFilterSiteUserSettings(_resourceAccessor.ResourcePathName); int result = filterStateEx.LoadAsync(url); if (result < 0) { throw new UPnPRendererExceptions("Loading URL async error: {0}", result); } WaitUntilReady(filterStateEx.IsStreamOpened, 1, "Check stream open error"); WaitUntilReady(filterStateEx.IsFilterReadyToConnectPins, 50, "IsFilterReadyToConnectPins error"); }
/// <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(Sites.SiteUtilBase siteUtil) { Thread renderPinsThread = null; VideoRendererStatistics.VideoState = VideoRendererStatistics.State.VideoPresent; // prevents the BlackRectangle on first time playback bool PlaybackReady = false; IBaseFilter sourceFilter = null; string sourceFilterName = null; try { sourceFilterName = GetSourceFilterName(m_strCurrentFile); int result = graphBuilder.FindFilterByName(sourceFilterName, out sourceFilter); if (result != 0) { string errorText = DirectShowLib.DsError.GetErrorText(result); if (errorText != null) { errorText = errorText.Trim(); } Log.Instance.Warn("BufferFile : FindFilterByName returned '{0}'{1}", "0x" + result.ToString("X8"), !string.IsNullOrEmpty(errorText) ? " : (" + errorText + ")" : ""); return(false); } OnlineVideos.MPUrlSourceFilter.IFilterStateEx filterStateEx = sourceFilter as OnlineVideos.MPUrlSourceFilter.IFilterStateEx; if (filterStateEx != null) { // MediaPortal IPTV filter and url source splitter Log.Instance.Info("BufferFile : using 'MediaPortal IPTV filter and url source splitter' as source filter"); String url = OnlineVideos.MPUrlSourceFilter.UrlBuilder.GetFilterUrl(siteUtil, m_strCurrentFile); Log.Instance.Info("BufferFile : loading url: '{0}'", url); result = filterStateEx.LoadAsync(url); if (result < 0) { throw new OnlineVideosException(FilterError.ErrorDescription(filterStateEx, result)); } while (!this.BufferingStopped) { Boolean opened = false; result = filterStateEx.IsStreamOpened(out opened); if (result < 0) { throw new OnlineVideosException(FilterError.ErrorDescription(filterStateEx, result)); } if (opened) { break; } Thread.Sleep(1); } // buffer before starting playback bool filterConnected = false; bool filterIsReadyToConnect = false; percentageBuffered = 0.0f; long total = 0, current = 0, last = 0; while (!PlaybackReady && graphBuilder != null && !BufferingStopped) { result = ((IAMOpenProgress)sourceFilter).QueryProgress(out total, out current); if ((result != 0) && (result != 0x00040260)) { // 0x00040260 - VFW_S_ESTIMATED - correct state, but value is estimated throw new OnlineVideosException(FilterError.ErrorDescription(filterStateEx, result)); } result = filterStateEx.IsFilterReadyToConnectPins(out filterIsReadyToConnect); if (result != 0) { throw new OnlineVideosException(FilterError.ErrorDescription(filterStateEx, result)); } percentageBuffered = (float)current / (float)total * 100.0f; // after configured percentage has been buffered, connect the graph if (!filterConnected && (percentageBuffered >= PluginConfiguration.Instance.playbuffer || skipBuffering)) { if (filterIsReadyToConnect) { result = filterStateEx.GetCacheFileName(out cacheFile); if (result != 0) { throw new OnlineVideosException(FilterError.ErrorDescription(filterStateEx, result)); } if (skipBuffering) { Log.Instance.Debug("Buffering skipped at {0}%", percentageBuffered); } filterConnected = true; renderPinsThread = new Thread(delegate() { try { Log.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); Log.Instance.Debug("BufferFile : Playback Ready."); PlaybackReady = true; } catch (ThreadAbortException) { Thread.ResetAbort(); Log.Instance.Info("RenderUnconnectedOutputPins foribly aborted."); } catch (Exception ex) { Log.Instance.Warn(ex.Message); StopBuffering(); } }) { IsBackground = true, Name = "OVGraph" }; renderPinsThread.Start(); } } // log every percent if (current > last && current - last >= (double)total * 0.01) { Log.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("#OnlineVideos.buffered", percentageBuffered.ToString(formatString, System.Globalization.CultureInfo.InvariantCulture)); Thread.Sleep(50); // no need to do this more often than 20 times per second } } else { Marshal.ThrowExceptionForHR(((IFileSourceFilter)sourceFilter).Load(m_strCurrentFile, null)); Log.Instance.Info("BufferFile : using unknown filter as source filter"); if (sourceFilter is IAMOpenProgress && !m_strCurrentFile.Contains("live=true") && !m_strCurrentFile.Contains("RtmpLive=1")) { // 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 >= PluginConfiguration.Instance.playbuffer || skipBuffering)) { //cacheFile = filterState.GetCacheFileName(); if (skipBuffering) { Log.Instance.Debug("Buffering skipped at {0}%", percentageBuffered); } filterConnected = true; renderPinsThread = new Thread(delegate() { try { Log.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); Log.Instance.Debug("BufferFile : Playback Ready."); PlaybackReady = true; } catch (ThreadAbortException) { Thread.ResetAbort(); Log.Instance.Info("RenderUnconnectedOutputPins foribly aborted."); } catch (Exception ex) { Log.Instance.Warn(ex.Message); StopBuffering(); } }) { IsBackground = true, Name = "OVGraph" }; renderPinsThread.Start(); } // log every percent if (current > last && current - last >= (double)total * 0.01) { Log.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("#OnlineVideos.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 { // 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 (OnlineVideosException) { throw; } catch (COMException comEx) { Log.Instance.Warn(comEx.ToString()); string errorText = DirectShowLib.DsError.GetErrorText(comEx.ErrorCode); if (errorText != null) { errorText = errorText.Trim(); } if (!string.IsNullOrEmpty(errorText)) { throw new OnlineVideosException(errorText); } } catch (Exception ex) { Log.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) { Log.Instance.Info("Buffering was aborted."); if (sourceFilter is IAMOpenProgress) { ((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); }