From patchwork Wed Jul 15 08:37:31 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hongcheng Zhong X-Patchwork-Id: 21036 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 820D144B9FC for ; Wed, 15 Jul 2020 11:37:49 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 7085368B510; Wed, 15 Jul 2020 11:37:49 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from smtp181.sjtu.edu.cn (smtp181.sjtu.edu.cn [202.120.2.181]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id ADEE368921A for ; Wed, 15 Jul 2020 11:37:40 +0300 (EEST) Received: from proxy02.sjtu.edu.cn (smtp188.sjtu.edu.cn [202.120.2.188]) by smtp181.sjtu.edu.cn (Postfix) with ESMTPS id 2DECB1008CBD3; Wed, 15 Jul 2020 16:37:38 +0800 (CST) Received: from localhost (localhost.localdomain [127.0.0.1]) by proxy02.sjtu.edu.cn (Postfix) with ESMTP id 2B061200B4496; Wed, 15 Jul 2020 16:37:38 +0800 (CST) X-Virus-Scanned: amavisd-new at Received: from proxy02.sjtu.edu.cn ([127.0.0.1]) by localhost (proxy02.sjtu.edu.cn [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id Antox6CY9-Nl; Wed, 15 Jul 2020 16:37:38 +0800 (CST) Received: from localhost.localdomain (unknown [10.162.157.92]) (Authenticated sender: sj.hc_Zhong@sjtu.edu.cn) by proxy02.sjtu.edu.cn (Postfix) with ESMTPSA id 5BF11200B448D; Wed, 15 Jul 2020 16:37:36 +0800 (CST) From: Hongcheng Zhong To: ffmpeg-devel@ffmpeg.org Date: Wed, 15 Jul 2020 16:37:31 +0800 Message-Id: <20200715083733.101880-4-sj.hc_Zhong@sjtu.edu.cn> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20200715083733.101880-1-sj.hc_Zhong@sjtu.edu.cn> References: <20200715083733.101880-1-sj.hc_Zhong@sjtu.edu.cn> MIME-Version: 1.0 Subject: [FFmpeg-devel] [RFC][GSoC][PATCH v1 4/6] ffplay: add an option to enable abr X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: spartazhc Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" From: spartazhc 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 --- 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; + 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" },