From patchwork Sun Jun 2 12:33:59 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Liu Steven X-Patchwork-Id: 13378 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 CE838449C49 for ; Sun, 2 Jun 2019 15:34:21 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id B2A8B689F72; Sun, 2 Jun 2019 15:34:21 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from smtpbgau1.qq.com (smtpbgau1.qq.com [54.206.16.166]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 81D41689E14 for ; Sun, 2 Jun 2019 15:34:12 +0300 (EEST) X-QQ-mid: bizesmtp27t1559478842twsf1bow Received: from localhost (unknown [47.90.47.25]) by esmtp10.qq.com (ESMTP) with id ; Sun, 02 Jun 2019 20:34:01 +0800 (CST) X-QQ-SSF: 01100000008000K0ZOF1B00A0000000 X-QQ-FEAT: j5Y3lWpKjFZB9JmFy3on5HBiulgUmpEFEvlKE/emp+Zcf80zf627ToKMG0Yew QDINqjfSIl8Wtx6ecxNwjEyDXalATohzz7cPfhff5LNt7DKBcGvs4wnGBqDygEXS1HaEhPR 5RSyJhQvbhKjqknTn+ytYo8Xz44ElGxuInry4pkt8L7nX7dExSvcOIyD5d8JeroXAh/dF1j knd12HsXj05Ki9FETdfvKfczmwb8wKiEapsvpHxErFG1OHmMvNnAhg6b41d3PNy4Y6SKI5p ax3yvoJBBWhnf/fCYIr/G7GodwirrPopI3IpcvaaxsmFjLL9vNjiWYF+A= X-QQ-GoodBg: 0 From: Steven Liu To: ffmpeg-devel@ffmpeg.org Date: Sun, 2 Jun 2019 20:33:59 +0800 Message-Id: <20190602123359.6776-1-lq@chinaffmpeg.org> X-Mailer: git-send-email 2.10.1.382.ga23ca1b.dirty X-QQ-SENDSIZE: 520 Feedback-ID: bizesmtp:chinaffmpeg.org:qybgforeign:qybgforeign4 X-QQ-Bgrelay: 1 Subject: [FFmpeg-devel] [PATCH] avformat/hlsenc: add EXT-X-I-FRAMES-ONLY tag support 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: Steven Liu MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Steven Liu --- doc/muxers.texi | 4 ++++ libavformat/dashenc.c | 4 ++-- libavformat/hlsenc.c | 27 ++++++++++++++++++++++----- libavformat/hlsplaylist.c | 11 ++++++++--- libavformat/hlsplaylist.h | 5 +++-- 5 files changed, 39 insertions(+), 12 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 83ae017d6c..7f3758b117 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -852,6 +852,10 @@ including the file containing the AES encryption key. Add the @code{#EXT-X-INDEPENDENT-SEGMENTS} to playlists that has video segments and when all the segments of that playlist are guaranteed to start with a Key frame. +@item iframes_only +Add the @code{#EXT-X-I-FRAMES-ONLY} to playlists that has video segments +and only can play i frames in the @code{#EXT-X-BYTERANGE} mode. + @item split_by_time Allow segments to start on frames other than keyframes. This improves behavior on some players when the time between keyframes is inconsistent, diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 94b198ceb8..3fd7e78166 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -471,7 +471,7 @@ static void write_hls_media_playlist(OutputStream *os, AVFormatContext *s, } ff_hls_write_playlist_header(c->m3u8_out, 6, -1, target_duration, - start_number, PLAYLIST_TYPE_NONE); + start_number, PLAYLIST_TYPE_NONE, 0); ff_hls_write_init_file(c->m3u8_out, os->initfile, c->single_file, os->init_range_length, os->init_start_pos); @@ -491,7 +491,7 @@ static void write_hls_media_playlist(OutputStream *os, AVFormatContext *s, (double) seg->duration / timescale, 0, seg->range_length, seg->start_pos, NULL, c->single_file ? os->initfile : seg->file, - &prog_date_time); + &prog_date_time, 0, 0, 0); if (ret < 0) { av_log(os->ctx, AV_LOG_WARNING, "ff_hls_write_file_entry get error\n"); } diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 6b913be31c..4ffc522803 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -75,6 +75,8 @@ typedef struct HLSSegment { int discont; int64_t pos; int64_t size; + int64_t keyframe_pos; + int64_t keyframe_size; unsigned var_stream_idx; char key_uri[LINE_BUFFER_SIZE + 1]; @@ -99,6 +101,7 @@ typedef enum HLSFlags { HLS_TEMP_FILE = (1 << 11), HLS_PERIODIC_REKEY = (1 << 12), HLS_INDEPENDENT_SEGMENTS = (1 << 13), + HLS_I_FRAMES_ONLY = (1 << 14), } HLSFlags; typedef enum { @@ -125,6 +128,9 @@ typedef struct VariantStream { double dpp; // duration per packet int64_t start_pts; int64_t end_pts; + int64_t video_lastpos; + int64_t video_keyframe_pos; + int64_t video_keyframe_size; double duration; // last segment duration computed so far, in seconds int64_t start_pos; // last segment starting position int64_t size; // last segment size @@ -994,6 +1000,8 @@ static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, en->duration = duration; en->pos = pos; en->size = size; + en->keyframe_pos = vs->video_keyframe_pos; + en->keyframe_size = vs->video_keyframe_size; en->next = NULL; en->discont = 0; @@ -1411,7 +1419,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) vs->discontinuity_set = 0; ff_hls_write_playlist_header(hls->m3u8_out, hls->version, hls->allowcache, - target_duration, sequence, hls->pl_type); + target_duration, sequence, hls->pl_type, hls->flags & HLS_I_FRAMES_ONLY); if((hls->flags & HLS_DISCONT_START) && sequence==hls->start_sequence && vs->discontinuity_set==0 ){ avio_printf(hls->m3u8_out, "#EXT-X-DISCONTINUITY\n"); @@ -1439,7 +1447,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) ret = ff_hls_write_file_entry(hls->m3u8_out, en->discont, byterange_mode, en->duration, hls->flags & HLS_ROUND_DURATIONS, en->size, en->pos, vs->baseurl, - en->filename, prog_date_time_p); + en->filename, prog_date_time_p, en->keyframe_size, en->keyframe_pos, hls->flags & HLS_I_FRAMES_ONLY); if (ret < 0) { av_log(s, AV_LOG_WARNING, "ff_hls_write_file_entry get error\n"); } @@ -1455,11 +1463,11 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) goto fail; } ff_hls_write_playlist_header(hls->sub_m3u8_out, hls->version, hls->allowcache, - target_duration, sequence, PLAYLIST_TYPE_NONE); + target_duration, sequence, PLAYLIST_TYPE_NONE, 0); for (en = vs->segments; en; en = en->next) { ret = ff_hls_write_file_entry(hls->sub_m3u8_out, 0, byterange_mode, en->duration, 0, en->size, en->pos, - vs->baseurl, en->sub_filename, NULL); + vs->baseurl, en->sub_filename, NULL, 0, 0, 0); if (ret < 0) { av_log(s, AV_LOG_WARNING, "ff_hls_write_file_entry get error\n"); } @@ -2205,7 +2213,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) vs->start_pts = pkt->pts; } - if (vs->has_video) { + if (vs->has_video) { can_split = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && ((pkt->flags & AV_PKT_FLAG_KEY) || (hls->flags & HLS_SPLIT_BY_TIME)); is_ref_pkt = (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) && (pkt->stream_index == vs->reference_stream_index); @@ -2240,6 +2248,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) av_write_frame(vs->avf, NULL); /* Flush any buffered data */ new_start_pos = avio_tell(vs->avf->pb); + if (hls->segment_type != SEGMENT_TYPE_FMP4) { avio_flush(oc->pb); vs->size = new_start_pos - vs->start_pos; @@ -2368,6 +2377,13 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) vs->packets_written++; if (oc->pb) { ret = ff_write_chained(oc, stream_index, pkt, s, 0); + if ((st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) && (pkt->flags & AV_PKT_FLAG_KEY)) { + vs->video_keyframe_size = avio_tell(oc->pb) - vs->video_lastpos; + vs->video_keyframe_pos = vs->start_pos; + } else { + vs->video_lastpos = avio_tell(oc->pb); + } + if (hls->ignore_io_errors) ret = 0; } @@ -2919,6 +2935,7 @@ static const AVOption options[] = { {"second_level_segment_size", "include segment size in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_SIZE }, 0, UINT_MAX, E, "flags"}, {"periodic_rekey", "reload keyinfo file periodically for re-keying", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_PERIODIC_REKEY }, 0, UINT_MAX, E, "flags"}, {"independent_segments", "add EXT-X-INDEPENDENT-SEGMENTS, whenever applicable", 0, AV_OPT_TYPE_CONST, { .i64 = HLS_INDEPENDENT_SEGMENTS }, 0, UINT_MAX, E, "flags"}, + {"iframes_only", "add EXT-X-I-FRAMES-ONLY, whenever applicable", 0, AV_OPT_TYPE_CONST, { .i64 = HLS_I_FRAMES_ONLY }, 0, UINT_MAX, E, "flags"}, #if FF_API_HLS_USE_LOCALTIME {"use_localtime", "set filename expansion with strftime at segment creation(will be deprecated )", OFFSET(use_localtime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, #endif diff --git a/libavformat/hlsplaylist.c b/libavformat/hlsplaylist.c index 0537049a97..e8b566789c 100644 --- a/libavformat/hlsplaylist.c +++ b/libavformat/hlsplaylist.c @@ -76,7 +76,7 @@ void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache, int target_duration, int64_t sequence, - uint32_t playlist_type) { + uint32_t playlist_type, int iframe_mode) { if (!out) return; ff_hls_write_playlist_version(out, version); @@ -92,6 +92,9 @@ void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache, } else if (playlist_type == PLAYLIST_TYPE_VOD) { avio_printf(out, "#EXT-X-PLAYLIST-TYPE:VOD\n"); } + if (iframe_mode) { + avio_printf(out, "#EXT-X-I-FRAMES-ONLY\n"); + } } void ff_hls_write_init_file(AVIOContext *out, char *filename, @@ -108,7 +111,8 @@ int ff_hls_write_file_entry(AVIOContext *out, int insert_discont, double duration, int round_duration, int64_t size, int64_t pos, //Used only if HLS_SINGLE_FILE flag is set char *baseurl, //Ignored if NULL - char *filename, double *prog_date_time) { + char *filename, double *prog_date_time, + int64_t video_keyframe_size, int64_t video_keyframe_pos, int iframe_mode) { if (!out || !filename) return AVERROR(EINVAL); @@ -120,7 +124,8 @@ int ff_hls_write_file_entry(AVIOContext *out, int insert_discont, else avio_printf(out, "#EXTINF:%f,\n", duration); if (byterange_mode) - avio_printf(out, "#EXT-X-BYTERANGE:%"PRId64"@%"PRId64"\n", size, pos); + avio_printf(out, "#EXT-X-BYTERANGE:%"PRId64"@%"PRId64"\n", iframe_mode ? video_keyframe_size : size, + iframe_mode ? video_keyframe_pos : pos); if (prog_date_time) { time_t tt, wrongsecs; diff --git a/libavformat/hlsplaylist.h b/libavformat/hlsplaylist.h index 54c93a3963..8d2d83b661 100644 --- a/libavformat/hlsplaylist.h +++ b/libavformat/hlsplaylist.h @@ -44,7 +44,7 @@ void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, char *codecs, char *ccgroup); void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache, int target_duration, int64_t sequence, - uint32_t playlist_type); + uint32_t playlist_type, int iframe_mode); void ff_hls_write_init_file(AVIOContext *out, char *filename, int byterange_mode, int64_t size, int64_t pos); int ff_hls_write_file_entry(AVIOContext *out, int insert_discont, @@ -52,7 +52,8 @@ int ff_hls_write_file_entry(AVIOContext *out, int insert_discont, double duration, int round_duration, int64_t size, int64_t pos, //Used only if HLS_SINGLE_FILE flag is set char *baseurl, //Ignored if NULL - char *filename, double *prog_date_time); + char *filename, double *prog_date_time, + int64_t video_keyframe_size, int64_t video_keyframe_pos, int iframe_mode); void ff_hls_write_end_list (AVIOContext *out); #endif /* AVFORMAT_HLSPLAYLIST_H_ */