/// <summary> /// Crée le filtre "File Splitter (demuxer)" et l'ajoute au graphe. /// </summary> /// <param name="graph">Le graphe.</param> /// <param name="filtersConfig">La configuration pour ce fichier.</param> /// <param name="sourceOutputPin">Le pin de sortie de la source.</param> /// <param name="parserOutputAudioPin">Le pin de sortie audio.</param> /// <param name="parserOutputVideoPin">Le pin de sortie vidéo</param> /// <returns>Le filtre File SPlitter.</returns> internal static IBaseFilter CreateSplitterFilter(IGraphBuilder graph, ExtensionFiltersSource filtersConfig, IPin sourceOutputPin, out IPin parserOutputAudioPin, out IPin parserOutputVideoPin) { Type filterType = null; IBaseFilter parserFilter = null; FilterSource splitterSource = filtersConfig.Splitter; if (splitterSource.SourceType != FilterSourceTypeEnum.External) { throw new InvalidOperationException("The Splitter Filter Source cannot be Automatic"); } CreateFilter(splitterSource.ExternalCLSID, splitterSource.Name, ref filterType, ref parserFilter); IPin parserInputPin = DsFindPin.ByDirection(parserFilter, PinDirection.Input, 0); int hr = graph.AddFilter(parserFilter, splitterSource.Name); DsError.ThrowExceptionForHR(hr); // Connectiong FileSource et Splitter hr = graph.Connect(sourceOutputPin, parserInputPin); DsError.ThrowExceptionForHR(hr); SafeRelease(sourceOutputPin); SafeRelease(parserInputPin); // Pins de sortie parserOutputAudioPin = DsFindPin.ByDirection(parserFilter, PinDirection.Output, 0); parserOutputVideoPin = DsFindPin.ByDirection(parserFilter, PinDirection.Output, 1); // Only video or audio if (parserOutputVideoPin == null) { parserOutputVideoPin = parserOutputAudioPin; parserOutputAudioPin = null; } parserOutputVideoPin.EnumMediaTypes(out IEnumMediaTypes enumMediaTypes); AMMediaType[] mts = { null }; while (enumMediaTypes.Next(1, mts, IntPtr.Zero) == 0) { if (mts[0].majorType != MediaType.Video) { // Invert in case it's not the appropriate media type IPin temp = parserOutputAudioPin; parserOutputAudioPin = parserOutputVideoPin; parserOutputVideoPin = temp; break; } } return(parserFilter); }
/// <summary> /// Obtient les paramètres pour un fichier. /// </summary> /// <param name="filtersConfiguration">Tous les paramètres.</param> /// <param name="fileSource">Le chemin vers le fichier vidéo.</param> /// <returns>Les paramètres de filtre pour le fichier.</returns> internal static ExtensionFiltersSource GetExtensionFiltersSource(FiltersConfiguration filtersConfiguration, string fileSource) { string fileExtension = Path.GetExtension(fileSource); ExtensionFiltersSource extensionFiltersSources = filtersConfiguration[fileExtension]; if (extensionFiltersSources == null) { throw new InvalidOperationException("There is no filter for this file type"); } return(extensionFiltersSources); }
/// <summary> /// Crée le graphe DirectShow /// </summary> void SetupGraph() { FiltersConfiguration filtersConfiguration = GraphHelper.GetFiltersConfiguration(); if (filtersConfiguration == null) { throw new InvalidOperationException("The filters are not configured"); } //System.IO.FileStream fs = null; try { ExtensionFiltersSource extensionFiltersSources = GraphHelper.GetExtensionFiltersSource(filtersConfiguration, _sourceFilePath); try { /* Creates the GraphBuilder COM object */ _graph = new FilterGraphNoThread() as IGraphBuilder; if (_graph == null) { throw new Exception("Could not create a graph"); } //if (_graphLogLocation != null) //{ // fs = System.IO.File.Create(_graphLogLocation); // int r = _graph.SetLogFile(fs.Handle); //} // // Creating FileSource filter // IBaseFilter sourceFilter = GraphHelper.CreateLAVSplitterSourceFilter(_graph, _sourceFilePath, out IPin parserAudioOutputPin, out IPin parserVideoOutputPin); // // Creating renderer // Type videoRendererFilterType = null; IBaseFilter videoRenderer = null; GraphHelper.CreateFilter(NULL_RENDERER, "Null Renderer", ref videoRendererFilterType, ref videoRenderer); int hr = _graph.AddFilter(videoRenderer, "Null Renderer"); DsError.ThrowExceptionForHR(hr); IPin videoRendererInputPin = DsFindPin.ByDirection(videoRenderer, PinDirection.Input, 0); // // Creating Video filter // // Connection du sample Grabber IBaseFilter sampleGrabberFilter = (IBaseFilter)_sampleGrabber; hr = _graph.AddFilter(sampleGrabberFilter, "Sample Grabber"); DsError.ThrowExceptionForHR(hr); IPin sampleGrabberInputPin = DsFindPin.ByDirection(sampleGrabberFilter, PinDirection.Input, 0); IPin sampleGrabberOuputPin = DsFindPin.ByDirection(sampleGrabberFilter, PinDirection.Output, 0); //Insertion du Color Space Converter Type filterType = null; IBaseFilter colorSpaceConverter = null; GraphHelper.CreateFilter(GraphHelper.CLSID_COLOR_SPACE_CONVERTER, GraphHelper.COLOR_SPACE_CONVERTER_FRIENDLYNAME, ref filterType, ref colorSpaceConverter); hr = _graph.AddFilter(colorSpaceConverter, GraphHelper.COLOR_SPACE_CONVERTER_FRIENDLYNAME); DsError.ThrowExceptionForHR(hr); IPin colorSpaceConverterInputPin = DsFindPin.ByDirection(colorSpaceConverter, PinDirection.Input, 0); GraphHelper.ConnectSplitterAndRendererWithDecoder(_graph, extensionFiltersSources, parserVideoOutputPin, colorSpaceConverterInputPin); IPin colorSpaceConverterOutputPin = DsFindPin.ByDirection(colorSpaceConverter, PinDirection.Output, 0); hr = _graph.ConnectDirect(colorSpaceConverterOutputPin, sampleGrabberInputPin, null); DsError.ThrowExceptionForHR(hr); hr = _graph.Connect(sampleGrabberOuputPin, videoRendererInputPin); DsError.ThrowExceptionForHR(hr); // Removes the clock to run the graph as fast as possible ((IMediaFilter)_graph).SetSyncSource(null); GraphHelper.SafeRelease(parserAudioOutputPin); GraphHelper.SafeRelease(parserVideoOutputPin); GraphHelper.SafeRelease(videoRendererInputPin); GraphHelper.SafeRelease(sampleGrabberInputPin); GraphHelper.SafeRelease(sampleGrabberOuputPin); GraphHelper.SafeRelease(colorSpaceConverterInputPin); GraphHelper.SafeRelease(colorSpaceConverterOutputPin); _mediaSeeking = _graph as IMediaSeeking; _mediaControl = _graph as IMediaControl; _mediaEvent = _graph as IMediaEventEx; /* Attempt to set the time format */ hr = _mediaSeeking.SetTimeFormat(TimeFormat.MediaTime); DsError.ThrowExceptionForHR(hr); } catch (Exception ex) { this.TraceError(ex, ex.Message); /* This exection will happen usually if the media does * not exist or could not open due to not having the * proper filters installed */ FreeResources(); } } finally { /*if (_graphLogLocation != null && fs != null) * fs.Close();*/ } }
/// <summary> /// Connecte le File Splitter et le renderer vidéo en créant le décodeur vidéo. /// </summary> /// <param name="graph">Le graphe.</param> /// <param name="filtersConfig">La configuration pour ce fichier.</param> /// <param name="parserOutputVideoPin">Le pin de sortie vidéo</param> /// <param name="videoRendererInputPin">Le pin d'entrée du Renderer.</param> internal static void ConnectSplitterAndRendererWithDecoder(IGraphBuilder graph, ExtensionFiltersSource filtersConfig, IPin parserOutputVideoPin, IPin videoRendererInputPin) { FilterSource videoFilterSource = filtersConfig.VideoDecoder; switch (videoFilterSource.SourceType) { case FilterSourceTypeEnum.Auto: int hr = graph.Connect(parserOutputVideoPin, videoRendererInputPin); DsError.ThrowExceptionForHR(hr); break; case FilterSourceTypeEnum.External: if (new Guid(videoFilterSource.ExternalCLSID) == new Guid(CLSID_VIDEO_DECODER_DMO)) { // The DMO filter is handled differently DMOWrapperFilter dmoFilter = new DMOWrapperFilter(); IDMOWrapperFilter wrapper = (IDMOWrapperFilter)dmoFilter; hr = wrapper.Init(new Guid(CLSID_VIDEO_DECODER_DMO), DirectShowLib.DMO.DMOCategory.VideoDecoder); DsError.ThrowExceptionForHR(hr); if (dmoFilter is IBaseFilter decoderFilter) { hr = graph.AddFilter(decoderFilter, "WMVideo Decoder DMO"); DsError.ThrowExceptionForHR(hr); IPin wmvDecoderInputPin = DsFindPin.ByDirection(decoderFilter, PinDirection.Input, 0); hr = graph.ConnectDirect(parserOutputVideoPin, wmvDecoderInputPin, null); DsError.ThrowExceptionForHR(hr); IPin wmvDecoderOutputPin = DsFindPin.ByDirection(decoderFilter, PinDirection.Output, 0); hr = graph.ConnectDirect(wmvDecoderOutputPin, videoRendererInputPin, null); DsError.ThrowExceptionForHR(hr); SafeRelease(wmvDecoderInputPin); SafeRelease(wmvDecoderOutputPin); } else { wrapper = null; SafeRelease(dmoFilter); dmoFilter = null; } } else { Type filterType = null; IBaseFilter externalFilter = null; CreateFilter(videoFilterSource.ExternalCLSID, videoFilterSource.Name, ref filterType, ref externalFilter); hr = graph.AddFilter(externalFilter, videoFilterSource.Name); DsError.ThrowExceptionForHR(hr); IPin externalDecoderInputPin = DsFindPin.ByDirection(externalFilter, PinDirection.Input, 0); hr = graph.ConnectDirect(parserOutputVideoPin, externalDecoderInputPin, null); DsError.ThrowExceptionForHR(hr); IPin externalDecoderOutputPin = DsFindPin.ByDirection(externalFilter, PinDirection.Output, 0); hr = graph.ConnectDirect(externalDecoderOutputPin, videoRendererInputPin, null); DsError.ThrowExceptionForHR(hr); SafeRelease(externalDecoderInputPin); SafeRelease(externalDecoderOutputPin); } break; default: throw new ArgumentOutOfRangeException($"{nameof(videoFilterSource)}.{nameof(FilterSource.SourceType)}"); } }
/// <summary> /// Parse la configuration DirectShow /// </summary> public static FiltersConfiguration Parse() { try { FiltersConfigurationSection configSection = FiltersConfigurationSection.GetConfiguration(); FiltersConfiguration conf = new FiltersConfiguration(); foreach (ExtensionElement extension in configSection.Extensions) { var ext = new ExtensionFiltersSource() { Extension = extension.Extension, MinSpeedRatio = extension.MinSpeedRatio, MaxSpeedRatio = extension.MaxSpeedRatio, Splitter = new FilterSource { Name = extension.Splitter.Name, SourceType = extension.Splitter.SourceType, ExternalCLSID = extension.Splitter.ExternalCLSID, }, VideoDecoder = new FilterSource { Name = extension.VideoDecoder.Name, SourceType = extension.VideoDecoder.SourceType, ExternalCLSID = extension.VideoDecoder.ExternalCLSID, }, AudioDecoder = new FilterSource { Name = extension.AudioDecoder.Name, SourceType = extension.AudioDecoder.SourceType, ExternalCLSID = extension.AudioDecoder.ExternalCLSID, } }; conf[ext.Extension] = ext; } // Filtres par défaut, minimum requis if (conf[".wmv"] == null) { conf[".wmv"] = new ExtensionFiltersSource { Extension = ".wmv", Splitter = new FilterSource { Name = "GDCL WMV Splitter", SourceType = FilterSourceTypeEnum.External, ExternalCLSID = CLSID_WMV_SPLITTER, }, VideoDecoder = new FilterSource { Name = "LAV Video Decoder", SourceType = FilterSourceTypeEnum.External, ExternalCLSID = CLSID_LAV_VIDEO_DECODER, }, AudioDecoder = new FilterSource { Name = "Auto Audio Decoder", SourceType = FilterSourceTypeEnum.Auto, } }; } // Vérification de l'exactitude des valeurs if (conf.Select(e => e.Extension).GroupBy(e => e).Any(g => g.Count() > 2)) { throw new ConfigurationErrorsException("Les extensions configurées doivent être uniques."); } foreach (var e in conf) { if (e.Splitter.SourceType == FilterSourceTypeEnum.Auto) { throw new ConfigurationErrorsException("Le splitter ne peut pas être Auto"); } if ((e.MinSpeedRatio.HasValue && !e.MaxSpeedRatio.HasValue) || (!e.MinSpeedRatio.HasValue && e.MaxSpeedRatio.HasValue)) { throw new ConfigurationErrorsException("Les vitesses de lecture doivent être toutes les deux définies"); } if (e.MinSpeedRatio.HasValue && e.MaxSpeedRatio.HasValue && e.MinSpeedRatio.Value > e.MaxSpeedRatio.Value) { throw new ConfigurationErrorsException("Les vitesses de lecture sont incorrectes"); } var sources = new FilterSource[] { e.Splitter, e.AudioDecoder, e.VideoDecoder }; foreach (var source in sources) { if (source.SourceType == FilterSourceTypeEnum.External && string.IsNullOrEmpty(source.ExternalCLSID)) { throw new ConfigurationErrorsException("Une source externe doit être accompagnée d'un CLSID externe"); } if (source.SourceType == FilterSourceTypeEnum.Auto && !string.IsNullOrEmpty(source.ExternalCLSID)) { throw new ConfigurationErrorsException("Une source auto ne doit pas être accompagnée d'un CLSID externe"); } } } return(conf); } catch (ConfigurationErrorsException ex) { TraceManager.TraceError(ex, "Erreur de configuration"); MessageBox.Show(Resources.LocalizationResources.Error_Configuration_Filters, Resources.LocalizationResources.Error, MessageBoxButton.OK, MessageBoxImage.Error); return(null); } catch (Exception e) { TraceManager.TraceError(e, "Erreur lors de l'initialisation de la configuration des filtres"); MessageBox.Show(Resources.LocalizationResources.Exception_GenericMessage, Resources.LocalizationResources.Error, MessageBoxButton.OK, MessageBoxImage.Error); return(null); } }