Message ID | 20200715083733.101880-4-sj.hc_Zhong@sjtu.edu.cn |
---|---|
State | Superseded |
Headers | show |
Series | [FFmpeg-devel,RFC,GSoC,v1,1/6] avformat/abr: Adaptive Bitrate support | expand |
Context | Check | Description |
---|---|---|
andriy/default | pending | |
andriy/make | fail | Make failed |
Hongcheng Zhong <sj.hc_Zhong@sjtu.edu.cn> 于2020年7月15日周三 下午4:38写道: > > 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> > --- > 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. > +@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@ffmpeg.org > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel > > To unsubscribe, visit link above, or email > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
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 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" },