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

Steven Liu lingjiujianke at gmail.com
Wed Jul 15 12:28:41 EEST 2020


Hongcheng Zhong <sj.hc_Zhong at sjtu.edu.cn> 于2020年7月15日周三 下午4:38写道:
>
> 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;
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,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));
check NULL?
> +        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
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".


More information about the ffmpeg-devel mailing list