[FFmpeg-devel] [RFC][GSoC][PATCH v2 4/6] ffplay: add an option to enable abr
Hongcheng Zhong
sj.hc_Zhong at sjtu.edu.cn
Thu Jul 16 15:51:14 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.
v1 fixed:
None.
v2 fixed:
1. check malloc result and return error message
Signed-off-by: spartazhc <spartazhc at 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.
+ 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..bcee507f40 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.27.0
More information about the ffmpeg-devel
mailing list