Hi,

The Audio/video decoded output pass through filter graph before proceeding to 
their respective encoders.

Since filters operates on raw input/output , filtering is computationally 
expensive.
And if we have multistep transcoding the filtering happens sequentially for all 
the steps , thereby decreasing the overall transcode performance.

So if we do filtering using threads for each step, thereby parallelizing the 
filtering for all the steps and increase the transcoder performance.
As decoder output goes to the filter, semaphores are posted for all the 
required filtering steps and
encoders waits on semaphore for the filtering job to complete to take it's 
input.

The changes are in 2 files:

1.       ffmpeg.c

2.       Configure: for configuring the build

For a sample ts file to file transcoding with and without threads shows the 
transcoder performance improvements for 8 steps. The input is 720p and output 
video is 480p for all the steps. So for all the steps required video scalar 
filter.

Command line:

time ./ffmpeg -threads 8 -loglevel fatal -y -i ./geiger720p.m2t -sws_flags 
bicubic -b 1500000 -s 720x480 -r 30.000  -bt 1500000 -vcodec mpeg2video 
-sc_threshold 50 -qmin 1 -qmax 51 -g 60 -keyint_min 6 -acodec mp2 -ab 128000 
-ar 44100  -f mpegts /dev/null -sws_flags bicubic -b 1500000 -s 720x480 -r 
30.000  -bt 1500000 -vcodec mpeg2video -sc_threshold 50 -qmin 1 -qmax 51 -g 60 
-keyint_min 6 -acodec mp2 -ab 128000 -ar 44100  -f mpegts /dev/null -sws_flags 
bicubic -b 1500000 -s 720x480 -r 30.000  -bt 1500000 -vcodec mpeg2video 
-sc_threshold 50 -qmin 1 -qmax 51 -g 60 -keyint_min 6 -acodec mp2 -ab 128000 
-ar 44100  -f mpegts /dev/null -sws_flags bicubic -b 1500000 -s 720x480 -r 
30.000  -bt 1500000 -vcodec mpeg2video -sc_threshold 50 -qmin 1 -qmax 51 -g 60 
-keyint_min 6 -acodec mp2 -ab 128000 -ar 44100  -f mpegts /dev/null -sws_flags 
bicubic -b 1500000 -s 720x480 -r 30.000  -bt 1500000 -vcodec mpeg2video 
-sc_threshold 50 -qmin 1 -qmax 51 -g 60 -keyint_min 6 -acodec mp2 -ab 128000 
-ar 44100  -f mpegts /dev/null -sws_flags bicubic -b 1500000 -s 720x480 -r 
30.000  -bt 1500000 -vcodec mpeg2video -sc_threshold 50 -qmin 1 -qmax 51 -g 60 
-keyint_min 6 -acodec mp2 -ab 128000 -ar 44100  -f mpegts /dev/null -sws_flags 
bicubic -b 1500000 -s 720x480 -r 30.000  -bt 1500000 -vcodec mpeg2video 
-sc_threshold 50 -qmin 1 -qmax 51 -g 60 -keyint_min 6 -acodec mp2 -ab 128000 
-ar 44100  -f mpegts /dev/null -sws_flags bicubic -b 1500000 -s 720x480 -r 
30.000  -bt 1500000 -vcodec mpeg2video -sc_threshold 50 -qmin 1 -qmax 51 -g 60 
-keyint_min 6 -acodec mp2 -ab 128000 -ar 44100  -f mpegts /dev/null

without threads:
real time : 5m28.182s
fps            : 28

with threads:
real time : 2m52.623s
fps            : 53

Tested enough to see that, there are no problems using threading for filtering 
job.

Manjunath








diff --git a/ffmpeg.c b/ffmpeg.c
old mode 100644
new mode 100755
index 17fe6e5..b7648a9
--- a/ffmpeg.c
+++ b/ffmpeg.c
@@ -61,6 +61,10 @@
 # include "libavfilter/buffersrc.h"
 # include "libavfilter/buffersink.h"
 
+#if CONFIG_AV_FILTER_THREADS
+#include <semaphore.h>
+#endif
+
 #if HAVE_SYS_RESOURCE_H
 #include <sys/types.h>
 #include <sys/time.h>
@@ -327,6 +331,14 @@ typedef struct OutputStream {
     int copy_initial_nonkeyframes;
 
     int keep_pix_fmt;
+#if CONFIG_AV_FILTER_THREADS
+    int avfilter_thread_alive;
+    pthread_t avfilter_thread;
+    sem_t sem_avfilter;
+    sem_t sem_encoder;
+    int graph_id;
+    int avfilter_ret;
+#endif
 } OutputStream;
 
 
@@ -1894,6 +1906,131 @@ static void do_video_stats(AVFormatContext *os, 
OutputStream *ost,
     }
 }
 
+#if CONFIG_AV_FILTER_THREADS
+static int avfilter_frame(void *p)
+{
+    OutputStream *ost = (OutputStream *)p;
+    while (ost->avfilter_thread_alive) {
+        sem_wait(&ost->sem_avfilter);
+        ost->avfilter_ret = 
avfilter_graph_request_oldest(filtergraphs[ost->graph_id]->graph);
+        sem_post(&ost->sem_encoder);
+    }
+    return 0;
+}
+/* check for new output on any of the filtergraphs */
+static int poll_filters(InputStream *ist)
+{
+    AVFilterBufferRef *picref;
+    AVFrame *filtered_frame = NULL;
+    int i, ret, ret_all;
+    unsigned nb_success, nb_eof;
+    int64_t frame_pts;
+
+    if (ist->st->codec->codec_type != AVMEDIA_TYPE_AUDIO && 
ist->st->codec->codec_type != AVMEDIA_TYPE_VIDEO)
+        return 0;
+
+    ret_all = nb_success = nb_eof = 0;
+    for (i = 0; i < nb_output_streams; i++) {
+        OutputStream *ost = output_streams[i];
+        if (ist->st->codec->codec_type == ost->st->codec->codec_type) {
+            sem_wait(&ost->sem_encoder);
+            if (!ost->avfilter_ret) {
+                nb_success++;
+            } else if (ost->avfilter_ret == AVERROR_EOF) {
+                nb_eof++;
+            } else if (ost->avfilter_ret != AVERROR(EAGAIN)) {
+                char buf[256];
+                av_strerror(ost->avfilter_ret, buf, sizeof(buf));
+                av_log(NULL, AV_LOG_WARNING,
+                       "Error in request_frame(): %s\n", buf);
+                ret_all = ost->avfilter_ret;
+            }
+        }
+    }
+    if (!nb_success)
+        return nb_eof == ist->nb_filters ? AVERROR_EOF : ret_all;
+
+    /* Reap all buffers present in the buffer sinks */
+    for (i = 0; i < nb_output_streams; i++) {
+        OutputStream *ost = output_streams[i];
+        OutputFile    *of = output_files[ost->file_index];
+        int ret = 0;
+
+        if (!ost->filter)
+            continue;
+
+        if (!ost->filtered_frame && !(ost->filtered_frame = 
avcodec_alloc_frame())) {
+            return AVERROR(ENOMEM);
+        } else
+            avcodec_get_frame_defaults(ost->filtered_frame);
+        filtered_frame = ost->filtered_frame;
+
+        while (!ost->is_past_recording_time) {
+            if (ost->enc->type == AVMEDIA_TYPE_AUDIO &&
+                !(ost->enc->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE))
+                ret = av_buffersink_read_samples(ost->filter->filter, &picref,
+                                                ost->st->codec->frame_size);
+            else
+#ifdef SINKA
+                ret = av_buffersink_read(ost->filter->filter, &picref);
+#else
+                ret = av_buffersink_get_buffer_ref(ost->filter->filter, 
&picref,
+                                                   
AV_BUFFERSINK_FLAG_NO_REQUEST);
+#endif
+            if (ret < 0) {
+                if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
+                    char buf[256];
+                    av_strerror(ret, buf, sizeof(buf));
+                    av_log(NULL, AV_LOG_WARNING,
+                           "Error in av_buffersink_get_buffer_ref(): %s\n", 
buf);
+                }
+                break;
+            }
+            frame_pts = AV_NOPTS_VALUE;
+            if (picref->pts != AV_NOPTS_VALUE) {
+                filtered_frame->pts = frame_pts = av_rescale_q(picref->pts,
+                                                
ost->filter->filter->inputs[0]->time_base,
+                                                ost->st->codec->time_base) -
+                                    av_rescale_q(of->start_time,
+                                                AV_TIME_BASE_Q,
+                                                ost->st->codec->time_base);
+
+                if (of->start_time && filtered_frame->pts < 0) {
+                    avfilter_unref_buffer(picref);
+                    continue;
+                }
+            }
+            //if (ost->source_index >= 0)
+            //    *filtered_frame= 
*input_streams[ost->source_index]->decoded_frame; //for me_threshold
+
+
+            switch (ost->filter->filter->inputs[0]->type) {
+            case AVMEDIA_TYPE_VIDEO:
+                avfilter_fill_frame_from_video_buffer_ref(filtered_frame, 
picref);
+                filtered_frame->pts = frame_pts;
+                if (!ost->frame_aspect_ratio)
+                    ost->st->codec->sample_aspect_ratio = 
picref->video->sample_aspect_ratio;
+
+                do_video_out(of->ctx, ost, filtered_frame,
+                             same_quant ? ost->last_quality :
+                                          ost->st->codec->global_quality);
+                break;
+            case AVMEDIA_TYPE_AUDIO:
+                avfilter_copy_buf_props(filtered_frame, picref);
+                filtered_frame->pts = frame_pts;
+                do_audio_out(of->ctx, ost, filtered_frame);
+                break;
+            default:
+                // TODO support subtitle filters
+                av_assert0(0);
+            }
+
+            avfilter_unref_buffer(picref);
+        }
+    }
+    return nb_eof == ist->nb_filters ? AVERROR_EOF : ret_all;
+}
+#else
 /* check for new output on any of the filtergraphs */
 static int poll_filters(void)
 {
@@ -2004,7 +2141,7 @@ static int poll_filters(void)
     }
     return nb_eof == nb_filtergraphs ? AVERROR_EOF : ret_all;
 }
-
+#endif
 static void print_report(int is_last_report, int64_t timer_start, int64_t 
cur_time)
 {
     char buf[1024];
@@ -2351,6 +2488,13 @@ static int decode_audio(InputStream *ist, AVPacket *pkt, 
int *got_output)
     }
 
     if (!*got_output) {
+#if CONFIG_AV_FILTER_THREADS
+    for (i = 0; i < nb_output_streams; i++) {
+        OutputStream *ost = output_streams[i];
+        if (ost->filter && ost->st->codec->codec_type == AVMEDIA_TYPE_AUDIO)
+            sem_post(&ost->sem_avfilter);
+    }
+#endif
         /* no audio frame */
         if (!pkt->size)
             for (i = 0; i < ist->nb_filters; i++)
@@ -2424,6 +2568,13 @@ static int decode_audio(InputStream *ist, AVPacket *pkt, 
int *got_output)
 
     for (i = 0; i < ist->nb_filters; i++)
         av_buffersrc_add_frame(ist->filters[i]->filter, decoded_frame, 0);
+#if CONFIG_AV_FILTER_THREADS
+    for (i = 0; i < nb_output_streams; i++) {
+        OutputStream *ost = output_streams[i];
+        if (ost->filter && ost->st->codec->codec_type == AVMEDIA_TYPE_AUDIO)
+            sem_post(&ost->sem_avfilter);
+    }
+#endif
 
     return ret;
 }
@@ -2454,6 +2605,13 @@ static int decode_video(InputStream *ist, AVPacket *pkt, 
int *got_output)
     quality = same_quant ? decoded_frame->quality : 0;
     if (!*got_output) {
         /* no picture yet */
+#if CONFIG_AV_FILTER_THREADS
+    for (i = 0; i < nb_output_streams; i++) {
+        OutputStream *ost = output_streams[i];
+        if (ost->filter && ost->st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
+            sem_post(&ost->sem_avfilter);
+    }
+#endif
         if (!pkt->size)
             for (i = 0; i < ist->nb_filters; i++)
                 av_buffersrc_add_ref(ist->filters[i]->filter, NULL, 
AV_BUFFERSRC_FLAG_NO_COPY);
@@ -2532,7 +2690,13 @@ static int decode_video(InputStream *ist, AVPacket *pkt, 
int *got_output)
         }
 
     }
-
+#if CONFIG_AV_FILTER_THREADS
+    for (i = 0; i < nb_output_streams; i++) {
+        OutputStream *ost = output_streams[i];
+        if (ost->filter && ost->st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
+            sem_post(&ost->sem_avfilter);
+    }
+#endif
     av_free(buffer_to_free);
     return ret;
 }
@@ -2791,7 +2955,9 @@ static int transcode_init(void)
     for (i = 0; i < nb_filtergraphs; i++)
         if ((ret = avfilter_graph_config(filtergraphs[i]->graph, NULL)) < 0)
             return ret;
-
+#if CONFIG_AV_FILTER_THREADS
+        int graph_id = 0;
+#endif
     /* for each output stream, we compute the right encoding parameters */
     for (i = 0; i < nb_output_streams; i++) {
         ost = output_streams[i];
@@ -2954,6 +3120,22 @@ static int transcode_init(void)
                         av_log(NULL, AV_LOG_FATAL, "Error opening filters!\n");
                         exit(1);
                     }
+#if CONFIG_AV_FILTER_THREADS
+                    if (sem_init(&ost->sem_avfilter, 0, 0) == -1) {
+                        av_log(NULL, AV_LOG_FATAL, "Could not initialize 
semaphores ost->sem_scalar\n");
+                        exit(1);
+                    }
+                    if (sem_init(&ost->sem_encoder, 0, 0) == -1) {
+                        av_log(NULL, AV_LOG_FATAL, "Could not initialize 
semaphores ost->sem_encoder\n");
+                        exit(1);
+                    }
+                    ost->avfilter_thread_alive = 1;
+                    if (pthread_create(&ost->avfilter_thread, NULL, 
avfilter_frame, (void *)ost)) {
+                        av_log(NULL, AV_LOG_FATAL, "Unsuccessful in creating 
thread ost->stream_thread\n");
+                        exit(1);
+                    }
+                    ost->graph_id = graph_id++;
+#endif
             }
 
             switch (codec->codec_type) {
@@ -3611,7 +3793,11 @@ static int transcode(void)
 
         // fprintf(stderr,"read #%d.%d size=%d\n", ist->file_index, 
ist->st->index, pkt.size);
         if ((ret = output_packet(ist, &pkt)) < 0 ||
+#if CONFIG_AV_FILTER_THREADS
+            ((ret = poll_filters(ist)) < 0 && ret != AVERROR_EOF)) {
+#else
             ((ret = poll_filters()) < 0 && ret != AVERROR_EOF)) {
+#endif
             char buf[128];
             av_strerror(ret, buf, sizeof(buf));
             av_log(NULL, AV_LOG_ERROR, "Error while decoding stream #%d:%d: 
%s\n",
@@ -3638,8 +3824,13 @@ static int transcode(void)
         if (!input_files[ist->file_index]->eof_reached && 
ist->decoding_needed) {
             output_packet(ist, NULL);
         }
+#if CONFIG_AV_FILTER_THREADS
+        poll_filters(ist);
+#endif
     }
+#if !(CONFIG_AV_FILTER_THREADS)
     poll_filters();
+#endif
     flush_encoders();
 
     term_exit();
@@ -3659,6 +3850,16 @@ static int transcode(void)
         if (ost->encoding_needed) {
             av_freep(&ost->st->codec->stats_in);
             avcodec_close(ost->st->codec);
+#if CONFIG_AV_FILTER_THREADS
+            if (ost->filter && (ost->st->codec->codec_type == 
AVMEDIA_TYPE_VIDEO || ost->st->codec->codec_type == AVMEDIA_TYPE_AUDIO)) {
+                int ret;
+                ost->avfilter_thread_alive = 0;
+                sem_post(&ost->sem_avfilter);
+                ret = pthread_join(ost->avfilter_thread, NULL);
+                if (ret)
+                    av_log(NULL, AV_LOG_FATAL, "Error %d in Joining thread of 
%d th stream\n", ret, i);
+            }
+#endif
         }
     }
 
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to