[FFmpeg-devel] [RFC][GSoC][PATCH v1 4/6] ffplay: add an option to enable abr

Hongcheng Zhong sj.hc_Zhong at sjtu.edu.cn
Wed Jul 15 11:37:31 EEST 2020


From: spartazhc <spartazhc at gmail.com>

Add abr option, ffplay can play hls using abr by:
ffplay -i http://xxx/master.m3u8 -abr

Structure ABRList is added to save stream type and index, it is
used to allow packet_queue_put function to put pkt which from same
type(for example: video pkt) but different stream index to queue.

Signed-off-by: spartazhc <spartazhc at gmail.com>
---
 doc/ffplay.texi  |   2 +
 fftools/ffplay.c | 145 ++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 138 insertions(+), 9 deletions(-)

diff --git a/doc/ffplay.texi b/doc/ffplay.texi
index f3761bb12e..6a24542cda 100644
--- a/doc/ffplay.texi
+++ b/doc/ffplay.texi
@@ -46,6 +46,8 @@ Disable audio.
 Disable video.
 @item -sn
 Disable subtitles.
+ at item -abr
+Enable adaptive bitrate for hls/dash.
 @item -ss @var{pos}
 Seek to @var{pos}. Note that in most formats it is not possible to seek
 exactly, so @command{ffplay} will seek to the nearest seek point to
diff --git a/fftools/ffplay.c b/fftools/ffplay.c
index d673b8049a..b17b75fa8f 100644
--- a/fftools/ffplay.c
+++ b/fftools/ffplay.c
@@ -201,6 +201,15 @@ typedef struct Decoder {
     SDL_Thread *decoder_tid;
 } Decoder;
 
+typedef struct ABRList {
+    int **audio_list;
+    int audios;
+    int **video_list;
+    int videos;
+    int **sub_list;
+    int subs;
+} ABRList;
+
 typedef struct VideoState {
     SDL_Thread *read_tid;
     AVInputFormat *iformat;
@@ -305,6 +314,8 @@ typedef struct VideoState {
     int last_video_stream, last_audio_stream, last_subtitle_stream;
 
     SDL_cond *continue_read_thread;
+
+    ABRList *abr_list;
 } VideoState;
 
 /* options specified by the user */
@@ -356,6 +367,7 @@ static char *afilters = NULL;
 static int autorotate = 1;
 static int find_stream_info = 1;
 static int filter_nbthreads = 0;
+static int abr = 0;
 
 /* current context */
 static int is_full_screen;
@@ -1262,6 +1274,29 @@ static void stream_component_close(VideoState *is, int stream_index)
     }
 }
 
+static void free_abr_dynarray(int **list, int num)
+{
+    for (int i = 0; i < num; i++) {
+        av_free(list[i]);
+    }
+}
+
+static void free_abr_list(ABRList *abrlist)
+{
+    if (abrlist->audios) {
+        free_abr_dynarray(abrlist->audio_list, abrlist->audios);
+        av_freep(&abrlist->audio_list);
+    }
+    if (abrlist->videos) {
+        free_abr_dynarray(abrlist->video_list, abrlist->videos);
+        av_freep(&abrlist->video_list);
+    }
+    if (abrlist->subs) {
+        free_abr_dynarray(abrlist->sub_list, abrlist->subs);
+        av_freep(&abrlist->sub_list);
+    }
+}
+
 static void stream_close(VideoState *is)
 {
     /* XXX: use a special url_shutdown call to abort parse cleanly */
@@ -2753,6 +2788,67 @@ static int is_realtime(AVFormatContext *s)
     return 0;
 }
 
+static av_cold int abr_init_list(VideoState *is)
+{
+    int stream_index, *tmp;
+    AVStream *st;
+    int nb_streams = is->ic->nb_streams;
+    ABRList *abrlist = is->abr_list;
+
+    for (stream_index = 0; stream_index < nb_streams; stream_index++) {
+        st = is->ic->streams[stream_index];
+        tmp = av_memdup(&stream_index, sizeof(int));
+        if (!tmp)
+            return -1;
+        switch (st->codecpar->codec_type) {
+            case AVMEDIA_TYPE_AUDIO:
+                av_dynarray_add(&abrlist->audio_list, &abrlist->audios, tmp);
+                break;
+            case AVMEDIA_TYPE_VIDEO:
+                av_dynarray_add(&abrlist->video_list, &abrlist->videos, tmp);
+                break;
+            case AVMEDIA_TYPE_SUBTITLE:
+                av_dynarray_add(&abrlist->sub_list, &abrlist->subs, tmp);
+                break;
+            default:
+                av_free(tmp);
+                break;
+        }
+    }
+    return 0;
+}
+
+static int abr_check_list(ABRList *abr_list, enum AVMediaType type, int st)
+{
+    int **st_list;
+    int n_st;
+    switch (type) {
+        case AVMEDIA_TYPE_AUDIO:
+            st_list = abr_list->audio_list;
+            n_st = abr_list->audios;
+            break;
+        case AVMEDIA_TYPE_VIDEO:
+            st_list = abr_list->video_list;
+            n_st = abr_list->videos;
+            break;
+        case AVMEDIA_TYPE_SUBTITLE:
+            st_list = abr_list->sub_list;
+            n_st = abr_list->subs;
+            break;
+        default:
+            break;
+    }
+    if (!st_list)
+        return 0;
+    for (int i = 0; i < n_st; i++) {
+        if (*st_list[i] == st)
+            return 1;
+    }
+    return 0;
+}
+
+
+
 /* this thread gets the stream from the disk or the network */
 static int read_thread(void *arg)
 {
@@ -2789,6 +2885,8 @@ static int read_thread(void *arg)
         av_dict_set(&format_opts, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE);
         scan_all_pmts_set = 1;
     }
+    if (abr)
+        av_dict_set(&format_opts, "abr", "1", 0);
     err = avformat_open_input(&ic, is->filename, is->iformat, &format_opts);
     if (err < 0) {
         print_error(is->filename, err);
@@ -2918,6 +3016,15 @@ static int read_thread(void *arg)
         stream_component_open(is, st_index[AVMEDIA_TYPE_SUBTITLE]);
     }
 
+    /* clean packet list filled in hls_read_header if abr is enabled */
+    if (abr) {
+        is->abr_list = av_mallocz(sizeof(ABRList));
+        ret = abr_init_list(is);
+        if (ret < 0) {
+            av_log(NULL, AV_LOG_WARNING, "Failed to initiate abr_list\n");
+        }
+    }
+
     if (is->video_stream < 0 && is->audio_stream < 0) {
         av_log(NULL, AV_LOG_FATAL, "Failed to open file '%s' or configure filtergraph\n",
                is->filename);
@@ -3045,15 +3152,31 @@ static int read_thread(void *arg)
                 av_q2d(ic->streams[pkt->stream_index]->time_base) -
                 (double)(start_time != AV_NOPTS_VALUE ? start_time : 0) / 1000000
                 <= ((double)duration / 1000000);
-        if (pkt->stream_index == is->audio_stream && pkt_in_play_range) {
-            packet_queue_put(&is->audioq, pkt);
-        } else if (pkt->stream_index == is->video_stream && pkt_in_play_range
-                   && !(is->video_st->disposition & AV_DISPOSITION_ATTACHED_PIC)) {
-            packet_queue_put(&is->videoq, pkt);
-        } else if (pkt->stream_index == is->subtitle_stream && pkt_in_play_range) {
-            packet_queue_put(&is->subtitleq, pkt);
+        if (abr) {
+            if ((pkt->stream_index == is->audio_stream
+                    || abr_check_list(is->abr_list, AVMEDIA_TYPE_AUDIO, pkt->stream_index)) && pkt_in_play_range) {
+                packet_queue_put(&is->audioq, pkt);
+            } else if ((pkt->stream_index == is->video_stream
+                    || abr_check_list(is->abr_list, AVMEDIA_TYPE_VIDEO, pkt->stream_index)) && pkt_in_play_range
+                    && !(is->video_st->disposition & AV_DISPOSITION_ATTACHED_PIC)) {
+                packet_queue_put(&is->videoq, pkt);
+            } else if ((pkt->stream_index == is->subtitle_stream
+                    || abr_check_list(is->abr_list, AVMEDIA_TYPE_SUBTITLE, pkt->stream_index)) && pkt_in_play_range) {
+                packet_queue_put(&is->subtitleq, pkt);
+            } else {
+                av_packet_unref(pkt);
+            }
         } else {
-            av_packet_unref(pkt);
+            if (pkt->stream_index == is->audio_stream && pkt_in_play_range) {
+                packet_queue_put(&is->audioq, pkt);
+            } else if (pkt->stream_index == is->video_stream && pkt_in_play_range
+                    && !(is->video_st->disposition & AV_DISPOSITION_ATTACHED_PIC)) {
+                packet_queue_put(&is->videoq, pkt);
+            } else if (pkt->stream_index == is->subtitle_stream && pkt_in_play_range) {
+                packet_queue_put(&is->subtitleq, pkt);
+            } else {
+                av_packet_unref(pkt);
+            }
         }
     }
 
@@ -3061,7 +3184,10 @@ static int read_thread(void *arg)
  fail:
     if (ic && !is->ic)
         avformat_close_input(&ic);
-
+    if (abr && is->abr_list) {
+        free_abr_list(is->abr_list);
+        av_freep(&is->abr_list);
+    }
     if (ret != 0) {
         SDL_Event event;
 
@@ -3588,6 +3714,7 @@ static const OptionDef options[] = {
     { "an", OPT_BOOL, { &audio_disable }, "disable audio" },
     { "vn", OPT_BOOL, { &video_disable }, "disable video" },
     { "sn", OPT_BOOL, { &subtitle_disable }, "disable subtitling" },
+    { "abr", OPT_BOOL, { &abr }, "enable adaptive bitrate for hls/dash" },
     { "ast", OPT_STRING | HAS_ARG | OPT_EXPERT, { &wanted_stream_spec[AVMEDIA_TYPE_AUDIO] }, "select desired audio stream", "stream_specifier" },
     { "vst", OPT_STRING | HAS_ARG | OPT_EXPERT, { &wanted_stream_spec[AVMEDIA_TYPE_VIDEO] }, "select desired video stream", "stream_specifier" },
     { "sst", OPT_STRING | HAS_ARG | OPT_EXPERT, { &wanted_stream_spec[AVMEDIA_TYPE_SUBTITLE] }, "select desired subtitle stream", "stream_specifier" },
-- 
2.27.0



More information about the ffmpeg-devel mailing list