diff mbox series

[FFmpeg-devel,GSoC,v3,5/7] ffplay: add an option to enable abr

Message ID 20200823122355.188611-5-sj.hc_Zhong@sjtu.edu.cn
State New
Headers show
Series [FFmpeg-devel,GSoC,v3,1/7] avformat/abr: Adaptive Bitrate support
Related show

Checks

Context Check Description
andriy/default pending
andriy/make success Make finished
andriy/make_fate success Make fate finished

Commit Message

Hongcheng Zhong Aug. 23, 2020, 12:23 p.m. UTC
From: spartazhc <spartazhc@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@gmail.com>

v1 fixed:
None.

v2 fixed:
1. check malloc result and return error message

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

Comments

Steven Liu Sept. 3, 2020, 3:05 p.m. UTC | #1
> 在 2020年8月23日,20:23,Hongcheng Zhong <sj.hc_zhong@sjtu.edu.cn> 写道:
> 
> From: spartazhc <spartazhc@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@gmail.com>
> 
> v1 fixed:
> None.
> 
> v2 fixed:
> 1. check malloc result and return error message
> 
> Signed-off-by: spartazhc <spartazhc@gmail.com>
> ---
> doc/ffplay.texi  |   2 +
> fftools/ffplay.c | 151 ++++++++++++++++++++++++++++++++++++++++++++---
> 2 files changed, 144 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.
> +@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 6c9c041e9a..30944ba4be 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 AVERROR(ENOMEM);
> +        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,21 @@ 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));
> +        if (!is->abr_list) {
> +            ret = AVERROR(ENOMEM);
> +            goto fail;
> +        }
> +        ret = abr_init_list(is);
> +        if (ret < 0) {
> +            av_log(NULL, AV_LOG_FATAL, "Failed to initiate abr_list\n");
> +            ret = -1;
> +            goto fail;
> +        }
> +    }
> +
>     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 +3158,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 +3190,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 +3720,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.28.0
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe”.

I have no more comments for this, waiting enough time for more reviewers’ comments,

Thanks
Steven
diff mbox series

Patch

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.
+@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 6c9c041e9a..30944ba4be 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 AVERROR(ENOMEM);
+        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,21 @@  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));
+        if (!is->abr_list) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+        ret = abr_init_list(is);
+        if (ret < 0) {
+            av_log(NULL, AV_LOG_FATAL, "Failed to initiate abr_list\n");
+            ret = -1;
+            goto fail;
+        }
+    }
+
     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 +3158,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 +3190,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 +3720,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" },