avformat/hlsenc: merge mpegts and fmp4 workflow to one workflow

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 <lq@chinaffmpeg.org>
This commit is contained in:
Steven Liu 2019-08-05 10:24:37 +08:00
parent df6e341b04
commit cff309097a
1 changed files with 124 additions and 127 deletions

View File

@ -815,7 +815,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 +824,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 +866,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 +1439,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 +1474,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 +1486,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 +1522,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 +1549,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);
@ -1694,42 +1697,13 @@ static int hls_start(AVFormatContext *s, VariantStream *vs)
}
c->encrypt_started = 1;
}
if ((err = av_dict_set(&options, "encryption_key", c->key_string, 0))
< 0)
goto fail;
err = av_strlcpy(iv_string, c->iv_string, sizeof(iv_string));
if (!err)
if (!err) {
snprintf(iv_string, sizeof(iv_string), "%032"PRIx64, vs->sequence);
if ((err = av_dict_set(&options, "encryption_iv", iv_string, 0)) < 0)
goto fail;
filename = av_asprintf("crypto:%s", oc->url);
if (!filename) {
err = AVERROR(ENOMEM);
goto fail;
}
err = hlsenc_io_open(s, &oc->pb, filename, &options);
av_free(filename);
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;
memset(c->iv_string, 0, sizeof(c->iv_string));
memcpy(c->iv_string, iv_string, sizeof(iv_string));
}
}
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 +1715,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 +2333,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);
@ -2365,14 +2347,10 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
vs->packets_written = 0;
vs->start_pos = range_length;
if (!byterange_mode) {
ff_format_io_close(s, &vs->out);
// ff_format_io_close(s, &vs->out);
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 +2365,44 @@ 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;
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) {
char *filename = NULL;
if (hls->key_info_file || hls->encrypt) {
av_dict_set(&options, "encryption_key", hls->key_string, 0);
av_dict_set(&options, "encryption_iv", hls->iv_string, 0);
filename = av_asprintf("crypto:%s", oc->url);
} else {
filename = av_asprintf("%s", oc->url);
}
vs->size = range_length;
} else {
set_http_options(s, &options, hls);
ret = hlsenc_io_open(s, &vs->out, vs->avf->url, &options);
if (!filename) {
return AVERROR(ENOMEM);
}
ret = hlsenc_io_open(s, &vs->out, filename, &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", filename);
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, filename);
av_free(filename);
}
}
@ -2420,7 +2410,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 +2425,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 +2434,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 +2448,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);
}
@ -2475,13 +2463,12 @@ 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);
vs->video_keyframe_size += pkt->size;
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;
vs->video_keyframe_size = avio_tell(oc->pb);
} else {
vs->video_lastpos = avio_tell(oc->pb);
vs->video_keyframe_pos = avio_tell(vs->out);
}
if (hls->ignore_io_errors)
ret = 0;
}
@ -2530,10 +2517,12 @@ 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++) {
char *filename = NULL;
vs = &hls->var_streams[i];
oc = vs->avf;
vtt_oc = vs->vtt_avf;
old_filename = av_strdup(vs->avf->url);
@ -2542,11 +2531,22 @@ static int hls_write_trailer(struct AVFormatContext *s)
if (!old_filename) {
return AVERROR(ENOMEM);
}
if (hls->key_info_file || hls->encrypt) {
av_dict_set(&options, "encryption_key", hls->key_string, 0);
av_dict_set(&options, "encryption_iv", hls->iv_string, 0);
filename = av_asprintf("crypto:%s", vs->avf->url);
} else {
filename = av_asprintf("%s", vs->avf->url);
}
if (!filename) {
return AVERROR(ENOMEM);
}
if ( hls->segment_type == SEGMENT_TYPE_FMP4) {
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 +2563,25 @@ 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, filename, &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, filename);
av_free(filename);
failed:
av_write_trailer(oc);
@ -2587,12 +2591,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 +2604,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);