From patchwork Wed Jul 31 08:07:22 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Liu Steven X-Patchwork-Id: 14165 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 ED9EC447956 for ; Wed, 31 Jul 2019 11:07:53 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id CC92668A391; Wed, 31 Jul 2019 11:07:53 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from smtpbguseast2.qq.com (smtpbguseast2.qq.com [54.204.34.130]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id B6DE56800D2 for ; Wed, 31 Jul 2019 11:07:46 +0300 (EEST) X-QQ-mid: bizesmtp28t1564560461tkhe5s2v Received: from localhost (unknown [106.120.217.162]) by esmtp10.qq.com (ESMTP) with id ; Wed, 31 Jul 2019 16:07:24 +0800 (CST) X-QQ-SSF: 01100000002000K0ZPF0000A0000000 X-QQ-FEAT: 6s0TUQ0M+4gw/0xFXF1Nx+cVz1095CUtR5iPPRf0s6daGAfZJbQd+ihC+cyKf c5LTPYpTllKZBT9zu+kl3QIpHEl3A50EB9ZWFfIQPsgXi2VLix4j59phFpqOwhqu5+Nm/FG VTEHkuq0Az5sHZtFXLm8qKp99X8kJ1F3fKYGQl5EFDftWRr4W5lADVtHDdnJEngWRCQwygv YWY6HvmoXO4/EdYFgtg/b+q2rOO/Wi4Eu0wzu6J47MhhH9aZnVH8PJ6JuGuyIW24jvCppxE FRgQ5rpMQk6Lxvjx5rZr1eP9AM0cwWS7xWsqUijJZFZMltQwC9kDkm7VKVvOQMjL1Xmw== X-QQ-GoodBg: 0 From: Steven Liu To: ffmpeg-devel@ffmpeg.org Date: Wed, 31 Jul 2019 16:07:22 +0800 Message-Id: <20190731080722.10443-1-lq@chinaffmpeg.org> X-Mailer: git-send-email 2.17.2 (Apple Git-113) X-QQ-SENDSIZE: 520 Feedback-ID: bizesmtp:chinaffmpeg.org:qybgforeign:qybgforeign1 X-QQ-Bgrelay: 1 Subject: [FFmpeg-devel] [PATCH v2] avformat/hlsenc: merge mpegts and fmp4 workflow to one workflow 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" write mpegts or fmp4 context into buffer, and flush the buffer into output file when split fragment. merge two format split workflow into one workflow Signed-off-by: Steven Liu --- libavformat/hlsenc.c | 200 ++++++++++++++++++++----------------------- 1 file changed, 92 insertions(+), 108 deletions(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 51310fb528..543ad4e7df 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -459,6 +459,7 @@ static int flush_dynbuf(VariantStream *vs, int *range_length) // write out to file *range_length = avio_close_dyn_buf(ctx->pb, &buffer); + vs->video_lastpos = 0; ctx->pb = NULL; avio_write(vs->out, buffer, *range_length); av_free(buffer); @@ -815,7 +816,7 @@ static int hls_mux_init(AVFormatContext *s, VariantStream *vs) vs->start_pos = 0; vs->new_start = 1; - if (hls->segment_type == SEGMENT_TYPE_FMP4) { + if (hls->segment_type == SEGMENT_TYPE_FMP4 && hls->max_seg_size > 0) { if (hls->http_persistent > 0) { //TODO: Support fragment fmp4 for http persistent in HLS muxer. av_log(s, AV_LOG_WARNING, "http persistent mode is currently unsupported for fragment mp4 in the HLS muxer.\n"); @@ -824,34 +825,38 @@ static int hls_mux_init(AVFormatContext *s, VariantStream *vs) av_log(s, AV_LOG_WARNING, "Multi-file byterange mode is currently unsupported in the HLS muxer.\n"); return AVERROR_PATCHWELCOME; } + } - vs->packets_written = 0; - vs->init_range_length = 0; - set_http_options(s, &options, hls); - if ((ret = avio_open_dyn_buf(&oc->pb)) < 0) - return ret; + vs->packets_written = 0; + vs->init_range_length = 0; + set_http_options(s, &options, hls); + if ((ret = avio_open_dyn_buf(&oc->pb)) < 0) + return ret; + if (hls->segment_type == SEGMENT_TYPE_FMP4) { if (byterange_mode) { ret = hlsenc_io_open(s, &vs->out, vs->basename, &options); } else { ret = hlsenc_io_open(s, &vs->out, vs->base_output_dirname, &options); } - av_dict_free(&options); + } + av_dict_free(&options); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", vs->fmp4_init_filename); + return ret; + } + + if (hls->format_options_str) { + ret = av_dict_parse_string(&hls->format_options, hls->format_options_str, "=", ":", 0); if (ret < 0) { - av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", vs->fmp4_init_filename); + av_log(s, AV_LOG_ERROR, "Could not parse format options list '%s'\n", + hls->format_options_str); return ret; } + } - if (hls->format_options_str) { - ret = av_dict_parse_string(&hls->format_options, hls->format_options_str, "=", ":", 0); - if (ret < 0) { - av_log(s, AV_LOG_ERROR, "Could not parse format options list '%s'\n", - hls->format_options_str); - return ret; - } - } - - av_dict_copy(&options, hls->format_options, 0); + av_dict_copy(&options, hls->format_options, 0); + if (hls->segment_type == SEGMENT_TYPE_FMP4) { av_dict_set(&options, "fflags", "-autobsf", 0); av_dict_set(&options, "movflags", "+frag_custom+dash+delay_moov", AV_DICT_APPEND); ret = avformat_init_output(oc, &options); @@ -862,9 +867,9 @@ static int hls_mux_init(AVFormatContext *s, VariantStream *vs) av_dict_free(&options); return AVERROR(EINVAL); } - avio_flush(oc->pb); - av_dict_free(&options); } + avio_flush(oc->pb); + av_dict_free(&options); return 0; } @@ -1435,7 +1440,6 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) { HLSContext *hls = s->priv_data; HLSSegment *en; - AVFormatContext *oc = vs->avf; int target_duration = 0; int ret = 0; char temp_filename[MAX_URL_SIZE]; @@ -1471,7 +1475,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) set_http_options(s, &options, hls); snprintf(temp_filename, sizeof(temp_filename), use_temp_file ? "%s.tmp" : "%s", vs->m3u8_name); - if ((ret = hlsenc_io_open(s, (byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? &hls->m3u8_out : &oc->pb, temp_filename, &options)) < 0) { + if ((ret = hlsenc_io_open(s, (byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? &hls->m3u8_out : &vs->out, temp_filename, &options)) < 0) { if (hls->ignore_io_errors) ret = 0; goto fail; @@ -1483,33 +1487,33 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) } vs->discontinuity_set = 0; - ff_hls_write_playlist_header((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : oc->pb, hls->version, hls->allowcache, + ff_hls_write_playlist_header((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : vs->out, hls->version, hls->allowcache, 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((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : oc->pb, "#EXT-X-DISCONTINUITY\n"); + avio_printf((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : vs->out, "#EXT-X-DISCONTINUITY\n"); vs->discontinuity_set = 1; } if (vs->has_video && (hls->flags & HLS_INDEPENDENT_SEGMENTS)) { - avio_printf((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : oc->pb, "#EXT-X-INDEPENDENT-SEGMENTS\n"); + avio_printf((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : vs->out, "#EXT-X-INDEPENDENT-SEGMENTS\n"); } for (en = vs->segments; en; en = en->next) { if ((hls->encrypt || hls->key_info_file) && (!key_uri || strcmp(en->key_uri, key_uri) || av_strcasecmp(en->iv_string, iv_string))) { - avio_printf((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : oc->pb, "#EXT-X-KEY:METHOD=AES-128,URI=\"%s\"", en->key_uri); + avio_printf((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : vs->out, "#EXT-X-KEY:METHOD=AES-128,URI=\"%s\"", en->key_uri); if (*en->iv_string) - avio_printf((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : oc->pb, ",IV=0x%s", en->iv_string); - avio_printf((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : oc->pb, "\n"); + avio_printf((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : vs->out, ",IV=0x%s", en->iv_string); + avio_printf((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : vs->out, "\n"); key_uri = en->key_uri; iv_string = en->iv_string; } if ((hls->segment_type == SEGMENT_TYPE_FMP4) && (en == vs->segments)) { - ff_hls_write_init_file((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : oc->pb, (hls->flags & HLS_SINGLE_FILE) ? en->filename : vs->fmp4_init_filename, + ff_hls_write_init_file((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : vs->out, (hls->flags & HLS_SINGLE_FILE) ? en->filename : vs->fmp4_init_filename, hls->flags & HLS_SINGLE_FILE, vs->init_range_length, 0); } - ret = ff_hls_write_file_entry((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : oc->pb, en->discont, byterange_mode, + ret = ff_hls_write_file_entry((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : vs->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->keyframe_size, en->keyframe_pos, hls->flags & HLS_I_FRAMES_ONLY); @@ -1519,7 +1523,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) } if (last && (hls->flags & HLS_OMIT_ENDLIST)==0) - ff_hls_write_end_list((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : oc->pb); + ff_hls_write_end_list((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : vs->out); if (vs->vtt_m3u8_name) { snprintf(temp_vtt_filename, sizeof(temp_vtt_filename), use_temp_file ? "%s.tmp" : "%s", vs->vtt_m3u8_name); @@ -1546,7 +1550,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) fail: av_dict_free(&options); - hlsenc_io_close(s, (byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? &hls->m3u8_out : &oc->pb, temp_filename); + hlsenc_io_close(s, (byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? &hls->m3u8_out : &vs->out, temp_filename); hlsenc_io_close(s, &hls->sub_m3u8_out, vs->vtt_m3u8_name); if (use_temp_file) { ff_rename(temp_filename, vs->m3u8_name, s); @@ -1713,23 +1717,7 @@ static int hls_start(AVFormatContext *s, VariantStream *vs) av_dict_free(&options); if (err < 0) return err; - } else if (c->segment_type != SEGMENT_TYPE_FMP4) { - if ((err = hlsenc_io_open(s, &oc->pb, oc->url, &options)) < 0) { - if (c->ignore_io_errors) - err = 0; - goto fail; - } } - if (vs->vtt_basename) { - set_http_options(s, &options, c); - if ((err = hlsenc_io_open(s, &vtt_oc->pb, vtt_oc->url, &options)) < 0) { - if (c->ignore_io_errors) - err = 0; - goto fail; - } - } - av_dict_free(&options); - if (c->segment_type != SEGMENT_TYPE_FMP4) { /* We only require one PAT/PMT per segment. */ if (oc->oformat->priv_class && oc->priv_data) { @@ -1741,7 +1729,23 @@ static int hls_start(AVFormatContext *s, VariantStream *vs) av_opt_set(oc->priv_data, "sdt_period", period, 0); av_opt_set(oc->priv_data, "pat_period", period, 0); } + if (c->flags & HLS_SINGLE_FILE) { + if ((err = hlsenc_io_open(s, &vs->out, oc->url, &options)) < 0) { + if (c->ignore_io_errors) + err = 0; + goto fail; + } + } + } + if (vs->vtt_basename) { + set_http_options(s, &options, c); + if ((err = hlsenc_io_open(s, &vtt_oc->pb, vtt_oc->url, &options)) < 0) { + if (c->ignore_io_errors) + err = 0; + goto fail; + } } + av_dict_free(&options); if (vs->vtt_basename) { err = avformat_write_header(vtt_oc,NULL); @@ -2343,20 +2347,12 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) int64_t new_start_pos; int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0); - 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; - } else { - vs->size = new_start_pos; - } - + av_write_frame(oc, NULL); /* Flush any buffered data */ + new_start_pos = avio_tell(oc->pb); + vs->size = new_start_pos - vs->start_pos; + avio_flush(oc->pb); if (hls->segment_type == SEGMENT_TYPE_FMP4) { if (!vs->init_range_length) { - avio_flush(oc->pb); range_length = avio_close_dyn_buf(oc->pb, &buffer); avio_write(vs->out, buffer, range_length); av_free(buffer); @@ -2369,10 +2365,6 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) hlsenc_io_close(s, &vs->out, vs->base_output_dirname); } } - } else { - if (!byterange_mode) { - hlsenc_io_close(s, &oc->pb, oc->url); - } } if (!byterange_mode) { if (vs->vtt_avf) { @@ -2387,32 +2379,32 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) // look to rename the asset name if (use_temp_file) { - if (!(hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size <= 0)) - if ((vs->avf->oformat->priv_class && vs->avf->priv_data) && hls->segment_type != SEGMENT_TYPE_FMP4) - av_opt_set(vs->avf->priv_data, "mpegts_flags", "resend_headers", 0); + av_dict_set(&options, "mpegts_flags", "resend_headers", 0); } - if (hls->segment_type == SEGMENT_TYPE_FMP4) { - if (hls->flags & HLS_SINGLE_FILE) { - ret = flush_dynbuf(vs, &range_length); - if (ret < 0) { - return ret; - } - vs->size = range_length; - } else { - set_http_options(s, &options, hls); - ret = hlsenc_io_open(s, &vs->out, vs->avf->url, &options); + if (hls->flags & HLS_SINGLE_FILE) { + ret = flush_dynbuf(vs, &range_length); + if (ret < 0) { + return ret; + } + vs->size = range_length; + } else { + set_http_options(s, &options, hls); + if ((hls->max_seg_size > 0 && (vs->size >= hls->max_seg_size)) || !byterange_mode) { + ret = hlsenc_io_open(s, &vs->out, oc->url, &options); if (ret < 0) { av_log(s, hls->ignore_io_errors ? AV_LOG_WARNING : AV_LOG_ERROR, - "Failed to open file '%s'\n", vs->avf->url); + "Failed to open file '%s'\n", oc->url); return hls->ignore_io_errors ? 0 : ret; } - write_styp(vs->out); + if (hls->segment_type == SEGMENT_TYPE_FMP4) { + write_styp(vs->out); + } ret = flush_dynbuf(vs, &range_length); if (ret < 0) { return ret; } - ff_format_io_close(s, &vs->out); + hlsenc_io_close(s, &vs->out, oc->url); } } @@ -2420,7 +2412,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) hls_rename_temp_file(s, oc); } - old_filename = av_strdup(vs->avf->url); + old_filename = av_strdup(oc->url); if (!old_filename) { return AVERROR(ENOMEM); } @@ -2435,11 +2427,6 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) } } - if (hls->segment_type != SEGMENT_TYPE_FMP4) { - vs->start_pos = new_start_pos; - } else { - vs->start_pos += vs->size; - } // if we're building a VOD playlist, skip writing the manifest multiple times, and just wait until the end if (hls->pl_type != PLAYLIST_TYPE_VOD) { if ((ret = hls_window(s, 0, vs)) < 0) { @@ -2449,8 +2436,10 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) if (hls->flags & HLS_SINGLE_FILE) { vs->number++; + vs->start_pos += vs->size; } else if (hls->max_seg_size > 0) { - if (vs->start_pos >= hls->max_seg_size) { + vs->start_pos = new_start_pos; + if (vs->size >= hls->max_seg_size) { vs->sequence++; sls_flag_file_rename(hls, vs, old_filename); ret = hls_start(s, vs); @@ -2461,6 +2450,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) } vs->number++; } else { + vs->start_pos = new_start_pos; sls_flag_file_rename(hls, vs, old_filename); ret = hls_start(s, vs); } @@ -2481,7 +2471,6 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) } else { vs->video_lastpos = avio_tell(oc->pb); } - if (hls->ignore_io_errors) ret = 0; } @@ -2530,10 +2519,11 @@ static int hls_write_trailer(struct AVFormatContext *s) int i; int ret = 0; VariantStream *vs = NULL; + AVDictionary *options = NULL; + int range_length, byterange_mode; for (i = 0; i < hls->nb_varstreams; i++) { vs = &hls->var_streams[i]; - oc = vs->avf; vtt_oc = vs->vtt_avf; old_filename = av_strdup(vs->avf->url); @@ -2546,7 +2536,6 @@ static int hls_write_trailer(struct AVFormatContext *s) int range_length = 0; if (!vs->init_range_length) { uint8_t *buffer = NULL; - int range_length, byterange_mode; av_write_frame(vs->avf, NULL); /* Flush any buffered data */ avio_flush(oc->pb); @@ -2563,21 +2552,23 @@ static int hls_write_trailer(struct AVFormatContext *s) hlsenc_io_close(s, &vs->out, vs->base_output_dirname); } } - if (!(hls->flags & HLS_SINGLE_FILE)) { - ret = hlsenc_io_open(s, &vs->out, vs->avf->url, NULL); - if (ret < 0) { - av_log(s, AV_LOG_ERROR, "Failed to open file '%s'\n", vs->avf->url); - goto failed; - } - write_styp(vs->out); - } - ret = flush_dynbuf(vs, &range_length); + } + if (!(hls->flags & HLS_SINGLE_FILE)) { + set_http_options(s, &options, hls); + ret = hlsenc_io_open(s, &vs->out, vs->avf->url, &options); if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Failed to open file '%s'\n", vs->avf->url); goto failed; } - vs->size = range_length; - ff_format_io_close(s, &vs->out); + if (hls->segment_type == SEGMENT_TYPE_FMP4) + write_styp(vs->out); } + ret = flush_dynbuf(vs, &range_length); + if (ret < 0) + goto failed; + + vs->size = range_length; + hlsenc_io_close(s, &vs->out, vs->avf->url); failed: av_write_trailer(oc); @@ -2587,12 +2578,6 @@ failed: use_temp_file = proto && !strcmp(proto, "file") && (hls->flags & HLS_TEMP_FILE); } - if (oc->pb) { - if (hls->segment_type != SEGMENT_TYPE_FMP4) { - vs->size = avio_tell(vs->avf->pb) - vs->start_pos; - hlsenc_io_close(s, &vs->avf->pb, vs->avf->url); - } - // rename that segment from .tmp to the real one if (use_temp_file && !(hls->flags & HLS_SINGLE_FILE)) { hls_rename_temp_file(s, oc); @@ -2606,7 +2591,6 @@ failed: /* after av_write_trailer, then duration + 1 duration per packet */ hls_append_segment(s, hls, vs, vs->duration + vs->dpp, vs->start_pos, vs->size); - } sls_flag_file_rename(hls, vs, old_filename);