/// <summary> /// If necessary, disposes the existing filtergraph and creates a new one based on the frame arguments. /// </summary> /// <param name="frame">The frame.</param> /// <exception cref="MediaContainerException"> /// avfilter_graph_create_filter /// or /// avfilter_graph_create_filter /// or /// avfilter_link /// or /// avfilter_graph_parse /// or /// avfilter_graph_config /// </exception> private void InitializeFilterGraph(AVFrame *frame) { /* * References: * https://www.ffmpeg.org/doxygen/2.0/doc_2examples_2filtering_audio_8c-example.html */ var frameArguments = ComputeFilterArguments(frame); if (string.IsNullOrWhiteSpace(CurrentFilterArguments) || frameArguments.Equals(CurrentFilterArguments) == false) { DestroyFiltergraph(); } else { return; } FilterGraph = ffmpeg.avfilter_graph_alloc(); RC.Current.Add(FilterGraph, $"264: {nameof(AudioComponent)}.{nameof(InitializeFilterGraph)}()"); CurrentFilterArguments = frameArguments; try { var result = 0; fixed(AVFilterContext **source = &SourceFilter) fixed(AVFilterContext **sink = &SinkFilter) { result = ffmpeg.avfilter_graph_create_filter(source, ffmpeg.avfilter_get_by_name("abuffer"), "audio_buffer", CurrentFilterArguments, null, FilterGraph); if (result != 0) { throw new MediaContainerException( $"{nameof(ffmpeg.avfilter_graph_create_filter)} (audio_buffer) failed. Error {result}: {FFmpegEx.GetErrorMessage(result)}"); } result = ffmpeg.avfilter_graph_create_filter(sink, ffmpeg.avfilter_get_by_name("abuffersink"), "audio_buffersink", null, null, FilterGraph); if (result != 0) { throw new MediaContainerException( $"{nameof(ffmpeg.avfilter_graph_create_filter)} (audio_buffersink) failed. Error {result}: {FFmpegEx.GetErrorMessage(result)}"); } } if (string.IsNullOrWhiteSpace(FilterString)) { result = ffmpeg.avfilter_link(SourceFilter, 0, SinkFilter, 0); if (result != 0) { throw new MediaContainerException($"{nameof(ffmpeg.avfilter_link)} failed. Error {result}: {FFmpegEx.GetErrorMessage(result)}"); } } else { var initFilterCount = FilterGraph->nb_filters; SourceOutput = ffmpeg.avfilter_inout_alloc(); SourceOutput->name = ffmpeg.av_strdup("in"); SourceOutput->filter_ctx = SourceFilter; SourceOutput->pad_idx = 0; SourceOutput->next = null; SinkInput = ffmpeg.avfilter_inout_alloc(); SinkInput->name = ffmpeg.av_strdup("out"); SinkInput->filter_ctx = SinkFilter; SinkInput->pad_idx = 0; SinkInput->next = null; result = ffmpeg.avfilter_graph_parse(FilterGraph, FilterString, SinkInput, SourceOutput, null); if (result != 0) { throw new MediaContainerException($"{nameof(ffmpeg.avfilter_graph_parse)} failed. Error {result}: {FFmpegEx.GetErrorMessage(result)}"); } // Reorder the filters to ensure that inputs of the custom filters are merged first for (var i = 0; i < FilterGraph->nb_filters - initFilterCount; i++) { var sourceAddress = FilterGraph->filters[i]; var targetAddress = FilterGraph->filters[i + initFilterCount]; FilterGraph->filters[i] = targetAddress; FilterGraph->filters[i + initFilterCount] = sourceAddress; } } result = ffmpeg.avfilter_graph_config(FilterGraph, null); if (result != 0) { throw new MediaContainerException($"{nameof(ffmpeg.avfilter_graph_config)} failed. Error {result}: {FFmpegEx.GetErrorMessage(result)}"); } } catch (Exception ex) { Container.Logger?.Log(MediaLogMessageType.Error, $"Audio filter graph could not be built: {FilterString}.\r\n{ex.Message}"); DestroyFiltergraph(); } }
/// <summary> /// If necessary, disposes the existing filtergraph and creates a new one based on the frame arguments. /// </summary> /// <param name="frame">The frame.</param> /// <exception cref="MediaContainerException"> /// avfilter_graph_create_filter /// or /// avfilter_graph_create_filter /// or /// avfilter_link /// or /// avfilter_graph_parse /// or /// avfilter_graph_config /// </exception> private void InitializeFilterGraph(AVFrame *frame) { /* * References: * http://libav-users.943685.n4.nabble.com/Libav-user-yadif-deinterlace-how-td3606561.html * https://www.ffmpeg.org/doxygen/trunk/filtering_8c-source.html * https://raw.githubusercontent.com/FFmpeg/FFmpeg/release/3.2/ffplay.c */ var frameArguments = ComputeFilterArguments(frame); if (string.IsNullOrWhiteSpace(CurrentFilterArguments) || frameArguments.Equals(CurrentFilterArguments) == false) { DestroyFiltergraph(); } else { return; } FilterGraph = ffmpeg.avfilter_graph_alloc(); RC.Current.Add(FilterGraph, $"144: {nameof(VideoComponent)}.{nameof(InitializeFilterGraph)}()"); CurrentFilterArguments = frameArguments; try { var result = 0; fixed(AVFilterContext **source = &SourceFilter) fixed(AVFilterContext **sink = &SinkFilter) { result = ffmpeg.avfilter_graph_create_filter(source, ffmpeg.avfilter_get_by_name("buffer"), "video_buffer", CurrentFilterArguments, null, FilterGraph); if (result != 0) { throw new MediaContainerException($"{nameof(ffmpeg.avfilter_graph_create_filter)} (buffer) failed. Error {result}: {FFmpegEx.GetErrorMessage(result)}"); } result = ffmpeg.avfilter_graph_create_filter(sink, ffmpeg.avfilter_get_by_name("buffersink"), "video_buffersink", null, null, FilterGraph); if (result != 0) { throw new MediaContainerException($"{nameof(ffmpeg.avfilter_graph_create_filter)} (buffersink) failed. Error {result}: {FFmpegEx.GetErrorMessage(result)}"); } // TODO: from ffplay, ffmpeg.av_opt_set_int_list(sink, "pix_fmts", (byte*)&f0, 1, ffmpeg.AV_OPT_SEARCH_CHILDREN); } if (string.IsNullOrWhiteSpace(FilterString)) { result = ffmpeg.avfilter_link(SourceFilter, 0, SinkFilter, 0); if (result != 0) { throw new MediaContainerException($"{nameof(ffmpeg.avfilter_link)} failed. Error {result}: {FFmpegEx.GetErrorMessage(result)}"); } } else { var initFilterCount = FilterGraph->nb_filters; SourceOutput = ffmpeg.avfilter_inout_alloc(); SourceOutput->name = ffmpeg.av_strdup("in"); SourceOutput->filter_ctx = SourceFilter; SourceOutput->pad_idx = 0; SourceOutput->next = null; SinkInput = ffmpeg.avfilter_inout_alloc(); SinkInput->name = ffmpeg.av_strdup("out"); SinkInput->filter_ctx = SinkFilter; SinkInput->pad_idx = 0; SinkInput->next = null; result = ffmpeg.avfilter_graph_parse(FilterGraph, FilterString, SinkInput, SourceOutput, null); if (result != 0) { throw new MediaContainerException($"{nameof(ffmpeg.avfilter_graph_parse)} failed. Error {result}: {FFmpegEx.GetErrorMessage(result)}"); } // Reorder the filters to ensure that inputs of the custom filters are merged first for (var i = 0; i < FilterGraph->nb_filters - initFilterCount; i++) { var sourceAddress = FilterGraph->filters[i]; var targetAddress = FilterGraph->filters[i + initFilterCount]; FilterGraph->filters[i] = targetAddress; FilterGraph->filters[i + initFilterCount] = sourceAddress; } } result = ffmpeg.avfilter_graph_config(FilterGraph, null); if (result != 0) { throw new MediaContainerException($"{nameof(ffmpeg.avfilter_graph_config)} failed. Error {result}: {FFmpegEx.GetErrorMessage(result)}"); } } catch (Exception ex) { Container.Logger?.Log(MediaLogMessageType.Error, $"Video filter graph could not be built: {FilterString}.\r\n{ex.Message}"); DestroyFiltergraph(); } }