Commit 08d28ee1 authored by Steven Liu's avatar Steven Liu

avformat/hlsenc: move init operations from write_header to init

Reviewed-by: 's avatarVishwanath Dixit <vdixit@akamai.com>
Signed-off-by: 's avatarSteven Liu <lq@chinaffmpeg.org>
parent 918de766
...@@ -1640,624 +1640,632 @@ static int hls_write_header(AVFormatContext *s) ...@@ -1640,624 +1640,632 @@ static int hls_write_header(AVFormatContext *s)
{ {
HLSContext *hls = s->priv_data; HLSContext *hls = s->priv_data;
int ret, i, j; int ret, i, j;
char *p = NULL;
const char *pattern = "%d.ts";
const char *pattern_localtime_fmt = get_default_pattern_localtime_fmt(s);
const char *vtt_pattern = "%d.vtt";
AVDictionary *options = NULL; AVDictionary *options = NULL;
int basename_size = 0;
int vtt_basename_size = 0, m3u8_name_size = 0;
VariantStream *vs = NULL; VariantStream *vs = NULL;
int fmp4_init_filename_len = strlen(hls->fmp4_init_filename) + 1;
ret = update_variant_stream_info(s); for (i = 0; i < hls->nb_varstreams; i++) {
if (ret < 0) { vs = &hls->var_streams[i];
av_log(s, AV_LOG_ERROR, "Variant stream info update failed with status %x\n",
ret);
goto fail;
}
//TODO: Updates needed to encryption functionality with periodic re-key when more than one variant streams are present av_dict_copy(&options, hls->format_options, 0);
if (hls->nb_varstreams > 1 && hls->flags & HLS_PERIODIC_REKEY) { ret = avformat_write_header(vs->avf, &options);
if (av_dict_count(options)) {
av_log(s, AV_LOG_ERROR, "Some of provided format options in '%s' are not recognized\n", hls->format_options_str);
ret = AVERROR(EINVAL); ret = AVERROR(EINVAL);
av_log(s, AV_LOG_ERROR, "Periodic re-key not supported when more than one variant streams are present\n"); av_dict_free(&options);
goto fail; goto fail;
} }
av_dict_free(&options);
//av_assert0(s->nb_streams == hls->avf->nb_streams);
for (j = 0; j < vs->nb_streams; j++) {
AVStream *inner_st;
AVStream *outer_st = vs->streams[j];
if (hls->master_pl_name) { if (hls->max_seg_size > 0) {
ret = update_master_pl_info(s); if ((outer_st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) &&
if (ret < 0) { (outer_st->codecpar->bit_rate > hls->max_seg_size)) {
av_log(s, AV_LOG_ERROR, "Master stream info update failed with status %x\n", av_log(s, AV_LOG_WARNING, "Your video bitrate is bigger than hls_segment_size, "
ret); "(%"PRId64 " > %"PRId64 "), the result maybe not be what you want.",
goto fail; outer_st->codecpar->bit_rate, hls->max_seg_size);
} }
} }
if (hls->segment_type == SEGMENT_TYPE_FMP4) { if (outer_st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
pattern = "%d.m4s"; inner_st = vs->avf->streams[j];
else if (vs->vtt_avf)
inner_st = vs->vtt_avf->streams[0];
else {
/* We have a subtitle stream, when the user does not want one */
inner_st = NULL;
continue;
} }
if ((hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH) || avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den);
(hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_FORMATTED_DATETIME)) {
time_t t = time(NULL); // we will need it in either case
if (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH) {
hls->start_sequence = (int64_t)t;
} else if (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_FORMATTED_DATETIME) {
char b[15];
struct tm *p, tmbuf;
if (!(p = localtime_r(&t, &tmbuf)))
return AVERROR(ENOMEM);
if (!strftime(b, sizeof(b), "%Y%m%d%H%M%S", p))
return AVERROR(ENOMEM);
hls->start_sequence = strtoll(b, NULL, 10);
} }
av_log(hls, AV_LOG_DEBUG, "start_number evaluated to %"PRId64"\n", hls->start_sequence);
} }
fail:
for (i = 0; i < hls->nb_varstreams; i++) { return ret;
vs = &hls->var_streams[i]; }
vs->sequence = hls->start_sequence;
hls->recording_time = (hls->init_time ? hls->init_time : hls->time) * AV_TIME_BASE;
vs->start_pts = AV_NOPTS_VALUE;
vs->end_pts = AV_NOPTS_VALUE;
vs->current_segment_final_filename_fmt[0] = '\0';
if (hls->flags & HLS_SPLIT_BY_TIME && hls->flags & HLS_INDEPENDENT_SEGMENTS) { static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
// Independent segments cannot be guaranteed when splitting by time {
hls->flags &= ~HLS_INDEPENDENT_SEGMENTS; HLSContext *hls = s->priv_data;
av_log(s, AV_LOG_WARNING, AVFormatContext *oc = NULL;
"'split_by_time' and 'independent_segments' cannot be enabled together. " AVStream *st = s->streams[pkt->stream_index];
"Disabling 'independent_segments' flag\n"); int64_t end_pts = 0;
} int is_ref_pkt = 1;
int ret = 0, can_split = 1, i, j;
int stream_index = 0;
int range_length = 0;
uint8_t *buffer = NULL;
VariantStream *vs = NULL;
if (hls->flags & HLS_PROGRAM_DATE_TIME) { for (i = 0; i < hls->nb_varstreams; i++) {
time_t now0; vs = &hls->var_streams[i];
time(&now0); for (j = 0; j < vs->nb_streams; j++) {
vs->initial_prog_date_time = now0; if (vs->streams[j] == st) {
if( st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE ) {
oc = vs->vtt_avf;
stream_index = 0;
} else {
oc = vs->avf;
stream_index = j;
} }
break;
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);
goto fail;
} }
} }
for (j = 0; j < vs->nb_streams; j++) { if (oc)
vs->has_video += break;
vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO;
vs->has_subtitle +=
vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE;
} }
if (vs->has_video > 1) if (!oc) {
av_log(s, AV_LOG_WARNING, av_log(s, AV_LOG_ERROR, "Unable to find mapping variant stream\n");
"More than a single video stream present, " return AVERROR(ENOMEM);
"expect issues decoding it.\n");
if (hls->segment_type == SEGMENT_TYPE_FMP4) {
vs->oformat = av_guess_format("mp4", NULL, NULL);
} else {
vs->oformat = av_guess_format("mpegts", NULL, NULL);
} }
if (!vs->oformat) { end_pts = hls->recording_time * vs->number;
ret = AVERROR_MUXER_NOT_FOUND;
goto fail;
}
if(vs->has_subtitle) { if (vs->sequence - vs->nb_entries > hls->start_sequence && hls->init_time > 0) {
vs->vtt_oformat = av_guess_format("webvtt", NULL, NULL); /* reset end_pts, hls->recording_time at end of the init hls list */
if (!vs->oformat) { int init_list_dur = hls->init_time * vs->nb_entries * AV_TIME_BASE;
ret = AVERROR_MUXER_NOT_FOUND; int after_init_list_dur = (vs->sequence - vs->nb_entries ) * hls->time * AV_TIME_BASE;
goto fail; hls->recording_time = hls->time * AV_TIME_BASE;
} end_pts = init_list_dur + after_init_list_dur ;
} }
if (hls->segment_filename) { if (vs->start_pts == AV_NOPTS_VALUE) {
basename_size = strlen(hls->segment_filename) + 1; vs->start_pts = pkt->pts;
if (hls->nb_varstreams > 1) {
basename_size += strlen(POSTFIX_PATTERN);
} }
vs->basename = av_malloc(basename_size);
if (!vs->basename) { if (vs->has_video) {
ret = AVERROR(ENOMEM); can_split = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
goto fail; ((pkt->flags & AV_PKT_FLAG_KEY) || (hls->flags & HLS_SPLIT_BY_TIME));
is_ref_pkt = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO;
} }
if (pkt->pts == AV_NOPTS_VALUE)
is_ref_pkt = can_split = 0;
av_strlcpy(vs->basename, hls->segment_filename, basename_size); if (is_ref_pkt) {
if (vs->end_pts == AV_NOPTS_VALUE)
vs->end_pts = pkt->pts;
if (vs->new_start) {
vs->new_start = 0;
vs->duration = (double)(pkt->pts - vs->end_pts)
* st->time_base.num / st->time_base.den;
vs->dpp = (double)(pkt->duration) * st->time_base.num / st->time_base.den;
} else { } else {
if (hls->flags & HLS_SINGLE_FILE) { if (pkt->duration) {
if (hls->segment_type == SEGMENT_TYPE_FMP4) { vs->duration += (double)(pkt->duration) * st->time_base.num / st->time_base.den;
pattern = ".m4s";
} else { } else {
pattern = ".ts"; av_log(s, AV_LOG_WARNING, "pkt->duration = 0, maybe the hls segment duration will not precise\n");
vs->duration = (double)(pkt->pts - vs->end_pts) * st->time_base.num / st->time_base.den;
} }
} }
if (hls->use_localtime) {
basename_size = strlen(s->filename) + strlen(pattern_localtime_fmt) + 1;
} else {
basename_size = strlen(s->filename) + strlen(pattern) + 1;
} }
if (hls->nb_varstreams > 1) { if (vs->packets_written && can_split && av_compare_ts(pkt->pts - vs->start_pts, st->time_base,
basename_size += strlen(POSTFIX_PATTERN); end_pts, AV_TIME_BASE_Q) >= 0) {
} int64_t new_start_pos;
char *old_filename = av_strdup(vs->avf->filename);
int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0);
vs->basename = av_malloc(basename_size); if (!old_filename) {
if (!vs->basename) { return AVERROR(ENOMEM);
ret = AVERROR(ENOMEM);
goto fail;
} }
av_strlcpy(vs->basename, s->filename, basename_size); av_write_frame(vs->avf, NULL); /* Flush any buffered data */
p = strrchr(vs->basename, '.'); new_start_pos = avio_tell(vs->avf->pb);
if (p) vs->size = new_start_pos - vs->start_pos;
*p = '\0';
if (hls->use_localtime) { if (!byterange_mode) {
av_strlcat(vs->basename, pattern_localtime_fmt, basename_size); if (hls->segment_type == SEGMENT_TYPE_FMP4 && !vs->init_range_length) {
avio_flush(oc->pb);
range_length = avio_close_dyn_buf(oc->pb, &buffer);
avio_write(vs->out, buffer, range_length);
vs->init_range_length = range_length;
avio_open_dyn_buf(&oc->pb);
vs->packets_written = 0;
ff_format_io_close(s, &vs->out);
hlsenc_io_close(s, &vs->out, vs->base_output_dirname);
} else { } else {
av_strlcat(vs->basename, pattern, basename_size); hlsenc_io_close(s, &oc->pb, oc->filename);
} }
if (vs->vtt_avf) {
hlsenc_io_close(s, &vs->vtt_avf->pb, vs->vtt_avf->filename);
} }
m3u8_name_size = strlen(s->filename) + 1;
if (hls->nb_varstreams > 1) {
m3u8_name_size += strlen(POSTFIX_PATTERN);
} }
if ((hls->flags & HLS_TEMP_FILE) && oc->filename[0]) {
vs->m3u8_name = av_malloc(m3u8_name_size); if (!(hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size <= 0))
if (!vs->m3u8_name ) { if ((vs->avf->oformat->priv_class && vs->avf->priv_data) && hls->segment_type != SEGMENT_TYPE_FMP4)
ret = AVERROR(ENOMEM); av_opt_set(vs->avf->priv_data, "mpegts_flags", "resend_headers", 0);
goto fail; hls_rename_temp_file(s, oc);
} }
av_strlcpy(vs->m3u8_name, s->filename, m3u8_name_size); if (vs->fmp4_init_mode) {
vs->number--;
}
if (hls->nb_varstreams > 1) { if (!vs->fmp4_init_mode || byterange_mode)
ret = format_name(vs->basename, basename_size, i); ret = hls_append_segment(s, hls, vs, vs->duration, vs->start_pos, vs->size);
if (ret < 0) vs->start_pos = new_start_pos;
goto fail; if (ret < 0) {
ret = format_name(vs->m3u8_name, m3u8_name_size, i); av_free(old_filename);
if (ret < 0) return ret;
goto fail;
} }
if (hls->segment_type == SEGMENT_TYPE_FMP4) { vs->end_pts = pkt->pts;
if (hls->nb_varstreams > 1) vs->duration = 0;
fmp4_init_filename_len += strlen(POSTFIX_PATTERN);
vs->fmp4_init_filename = av_malloc(fmp4_init_filename_len);
if (!vs->fmp4_init_filename ) {
ret = AVERROR(ENOMEM);
goto fail;
}
av_strlcpy(vs->fmp4_init_filename, hls->fmp4_init_filename,
fmp4_init_filename_len);
if (hls->nb_varstreams > 1) {
ret = format_name(vs->fmp4_init_filename, fmp4_init_filename_len, i);
if (ret < 0)
goto fail;
}
if (av_strcasecmp(hls->fmp4_init_filename, "init.mp4")) { vs->fmp4_init_mode = 0;
fmp4_init_filename_len = strlen(vs->fmp4_init_filename) + 1; if (hls->flags & HLS_SINGLE_FILE) {
vs->base_output_dirname = av_malloc(fmp4_init_filename_len); vs->number++;
if (!vs->base_output_dirname) { } else if (hls->max_seg_size > 0) {
ret = AVERROR(ENOMEM); if (vs->start_pos >= hls->max_seg_size) {
goto fail; vs->sequence++;
sls_flag_file_rename(hls, vs, old_filename);
ret = hls_start(s, vs);
vs->start_pos = 0;
/* When split segment by byte, the duration is short than hls_time,
* so it is not enough one segment duration as hls_time, */
vs->number--;
} }
av_strlcpy(vs->base_output_dirname, vs->fmp4_init_filename, vs->number++;
fmp4_init_filename_len);
} else { } else {
fmp4_init_filename_len = strlen(vs->m3u8_name) + sls_flag_file_rename(hls, vs, old_filename);
strlen(vs->fmp4_init_filename) + 1; ret = hls_start(s, vs);
vs->base_output_dirname = av_malloc(fmp4_init_filename_len);
if (!vs->base_output_dirname) {
ret = AVERROR(ENOMEM);
goto fail;
} }
av_free(old_filename);
av_strlcpy(vs->base_output_dirname, vs->m3u8_name, if (ret < 0) {
fmp4_init_filename_len); return ret;
p = strrchr(vs->base_output_dirname, '/');
if (p) {
*(p + 1) = '\0';
av_strlcat(vs->base_output_dirname, vs->fmp4_init_filename,
fmp4_init_filename_len);
} else {
av_strlcpy(vs->base_output_dirname, vs->fmp4_init_filename,
fmp4_init_filename_len);
} }
if (!vs->fmp4_init_mode || byterange_mode)
if ((ret = hls_window(s, 0, vs)) < 0) {
return ret;
} }
} }
if (!hls->use_localtime) { vs->packets_written++;
ret = sls_flag_check_duration_size_index(hls); ret = ff_write_chained(oc, stream_index, pkt, s, 0);
if (ret < 0) {
goto fail; return ret;
} }
} else {
ret = sls_flag_check_duration_size(hls, vs); static int hls_write_trailer(struct AVFormatContext *s)
if (ret < 0) { {
goto fail; HLSContext *hls = s->priv_data;
AVFormatContext *oc = NULL;
AVFormatContext *vtt_oc = NULL;
char *old_filename = NULL;
int i;
VariantStream *vs = NULL;
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->filename);
if (!old_filename) {
return AVERROR(ENOMEM);
} }
av_write_trailer(oc);
if (oc->pb) {
vs->size = avio_tell(vs->avf->pb) - vs->start_pos;
ff_format_io_close(s, &oc->pb);
if ((hls->flags & HLS_TEMP_FILE) && oc->filename[0]) {
hls_rename_temp_file(s, oc);
} }
if(vs->has_subtitle) {
if (hls->flags & HLS_SINGLE_FILE) /* after av_write_trailer, then duration + 1 duration per packet */
vtt_pattern = ".vtt"; hls_append_segment(s, hls, vs, vs->duration + vs->dpp, vs->start_pos, vs->size);
vtt_basename_size = strlen(s->filename) + strlen(vtt_pattern) + 1;
if (hls->nb_varstreams > 1) {
vtt_basename_size += strlen(POSTFIX_PATTERN);
} }
vs->vtt_basename = av_malloc(vtt_basename_size); sls_flag_file_rename(hls, vs, old_filename);
if (!vs->vtt_basename) {
ret = AVERROR(ENOMEM); if (vtt_oc) {
goto fail; if (vtt_oc->pb)
av_write_trailer(vtt_oc);
vs->size = avio_tell(vs->vtt_avf->pb) - vs->start_pos;
ff_format_io_close(s, &vtt_oc->pb);
} }
vs->vtt_m3u8_name = av_malloc(vtt_basename_size); av_freep(&vs->basename);
if (!vs->vtt_m3u8_name ) { av_freep(&vs->base_output_dirname);
ret = AVERROR(ENOMEM); avformat_free_context(oc);
goto fail;
vs->avf = NULL;
hls_window(s, 1, vs);
av_freep(&vs->fmp4_init_filename);
if (vtt_oc) {
av_freep(&vs->vtt_basename);
av_freep(&vs->vtt_m3u8_name);
avformat_free_context(vtt_oc);
} }
av_strlcpy(vs->vtt_basename, s->filename, vtt_basename_size);
p = strrchr(vs->vtt_basename, '.');
if (p)
*p = '\0';
if( hls->subtitle_filename ) { hls_free_segments(vs->segments);
strcpy(vs->vtt_m3u8_name, hls->subtitle_filename); hls_free_segments(vs->old_segments);
} else { av_free(old_filename);
strcpy(vs->vtt_m3u8_name, vs->vtt_basename); av_freep(&vs->m3u8_name);
av_strlcat(vs->vtt_m3u8_name, "_vtt.m3u8", vtt_basename_size); av_freep(&vs->streams);
av_freep(&vs->baseurl);
} }
av_strlcat(vs->vtt_basename, vtt_pattern, vtt_basename_size);
if (hls->nb_varstreams > 1) { av_freep(&hls->key_basename);
ret= format_name(vs->vtt_basename, vtt_basename_size, i); av_freep(&hls->var_streams);
if (ret < 0) av_freep(&hls->master_m3u8_url);
goto fail; return 0;
ret = format_name(vs->vtt_m3u8_name, vtt_basename_size, i); }
if (ret < 0)
static int hls_init(AVFormatContext *s)
{
int ret = 0;
int i = 0;
int j = 0;
HLSContext *hls = s->priv_data;
const char *pattern = "%d.ts";
VariantStream *vs = NULL;
int basename_size = 0;
const char *pattern_localtime_fmt = get_default_pattern_localtime_fmt(s);
const char *vtt_pattern = "%d.vtt";
char *p = NULL;
int vtt_basename_size = 0, m3u8_name_size = 0;
int fmp4_init_filename_len = strlen(hls->fmp4_init_filename) + 1;
ret = update_variant_stream_info(s);
if (ret < 0) {
av_log(s, AV_LOG_ERROR, "Variant stream info update failed with status %x\n",
ret);
goto fail; goto fail;
} }
//TODO: Updates needed to encryption functionality with periodic re-key when more than one variant streams are present
if (hls->nb_varstreams > 1 && hls->flags & HLS_PERIODIC_REKEY) {
ret = AVERROR(EINVAL);
av_log(s, AV_LOG_ERROR, "Periodic re-key not supported when more than one variant streams are present\n");
goto fail;
} }
if (hls->baseurl) { if (hls->master_pl_name) {
vs->baseurl = av_strdup(hls->baseurl); ret = update_master_pl_info(s);
if (!vs->baseurl) { if (ret < 0) {
ret = AVERROR(ENOMEM); av_log(s, AV_LOG_ERROR, "Master stream info update failed with status %x\n",
ret);
goto fail; goto fail;
} }
} }
if ((hls->flags & HLS_SINGLE_FILE) && (hls->segment_type == SEGMENT_TYPE_FMP4)) { if (hls->segment_type == SEGMENT_TYPE_FMP4) {
vs->fmp4_init_filename = av_strdup(vs->basename); pattern = "%d.m4s";
if (!vs->fmp4_init_filename) { }
ret = AVERROR(ENOMEM); if ((hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH) ||
goto fail; (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_FORMATTED_DATETIME)) {
time_t t = time(NULL); // we will need it in either case
if (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH) {
hls->start_sequence = (int64_t)t;
} else if (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_FORMATTED_DATETIME) {
char b[15];
struct tm *p, tmbuf;
if (!(p = localtime_r(&t, &tmbuf)))
return AVERROR(ENOMEM);
if (!strftime(b, sizeof(b), "%Y%m%d%H%M%S", p))
return AVERROR(ENOMEM);
hls->start_sequence = strtoll(b, NULL, 10);
} }
av_log(hls, AV_LOG_DEBUG, "start_number evaluated to %"PRId64"\n", hls->start_sequence);
} }
if ((ret = hls_mux_init(s, vs)) < 0) hls->recording_time = (hls->init_time ? hls->init_time : hls->time) * AV_TIME_BASE;
goto fail; for (i = 0; i < hls->nb_varstreams; i++) {
vs = &hls->var_streams[i];
vs->sequence = hls->start_sequence;
vs->start_pts = AV_NOPTS_VALUE;
vs->end_pts = AV_NOPTS_VALUE;
vs->current_segment_final_filename_fmt[0] = '\0';
if (hls->flags & HLS_APPEND_LIST) { if (hls->flags & HLS_SPLIT_BY_TIME && hls->flags & HLS_INDEPENDENT_SEGMENTS) {
parse_playlist(s, vs->m3u8_name, vs); // Independent segments cannot be guaranteed when splitting by time
vs->discontinuity = 1; hls->flags &= ~HLS_INDEPENDENT_SEGMENTS;
if (hls->init_time > 0) { av_log(s, AV_LOG_WARNING,
av_log(s, AV_LOG_WARNING, "append_list mode does not support hls_init_time," "'split_by_time' and 'independent_segments' cannot be enabled together. "
" hls_init_time value will have no effect\n"); "Disabling 'independent_segments' flag\n");
hls->init_time = 0;
hls->recording_time = hls->time * AV_TIME_BASE;
}
} }
if (hls->segment_type != SEGMENT_TYPE_FMP4 || hls->flags & HLS_SINGLE_FILE) { if (hls->flags & HLS_PROGRAM_DATE_TIME) {
if ((ret = hls_start(s, vs)) < 0) time_t now0;
goto fail; time(&now0);
vs->initial_prog_date_time = now0;
} }
if (hls->format_options_str) {
av_dict_copy(&options, hls->format_options, 0); ret = av_dict_parse_string(&hls->format_options, hls->format_options_str, "=", ":", 0);
ret = avformat_write_header(vs->avf, &options); if (ret < 0) {
if (av_dict_count(options)) { av_log(s, AV_LOG_ERROR, "Could not parse format options list '%s'\n", hls->format_options_str);
av_log(s, AV_LOG_ERROR, "Some of provided format options in '%s' are not recognized\n", hls->format_options_str);
ret = AVERROR(EINVAL);
av_dict_free(&options);
goto fail; goto fail;
} }
av_dict_free(&options); }
//av_assert0(s->nb_streams == hls->avf->nb_streams);
for (j = 0; j < vs->nb_streams; j++) {
AVStream *inner_st;
AVStream *outer_st = vs->streams[j];
if (hls->max_seg_size > 0) { for (j = 0; j < vs->nb_streams; j++) {
if ((outer_st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) && vs->has_video += vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO;
(outer_st->codecpar->bit_rate > hls->max_seg_size)) { vs->has_subtitle += vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE;
av_log(s, AV_LOG_WARNING, "Your video bitrate is bigger than hls_segment_size, "
"(%"PRId64 " > %"PRId64 "), the result maybe not be what you want.",
outer_st->codecpar->bit_rate, hls->max_seg_size);
} }
if (vs->has_video > 1)
av_log(s, AV_LOG_WARNING, "More than a single video stream present, expect issues decoding it.\n");
if (hls->segment_type == SEGMENT_TYPE_FMP4) {
vs->oformat = av_guess_format("mp4", NULL, NULL);
} else {
vs->oformat = av_guess_format("mpegts", NULL, NULL);
} }
if (outer_st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) if (!vs->oformat) {
inner_st = vs->avf->streams[j]; ret = AVERROR_MUXER_NOT_FOUND;
else if (vs->vtt_avf) goto fail;
inner_st = vs->vtt_avf->streams[0];
else {
/* We have a subtitle stream, when the user does not want one */
inner_st = NULL;
continue;
} }
avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den);
if (vs->has_subtitle) {
vs->vtt_oformat = av_guess_format("webvtt", NULL, NULL);
if (!vs->oformat) {
ret = AVERROR_MUXER_NOT_FOUND;
goto fail;
} }
} }
fail: if (hls->segment_filename) {
basename_size = strlen(hls->segment_filename) + 1;
if (ret < 0) { if (hls->nb_varstreams > 1) {
av_freep(&hls->key_basename); basename_size += strlen(POSTFIX_PATTERN);
for (i = 0; i < hls->nb_varstreams && hls->var_streams; i++) {
vs = &hls->var_streams[i];
av_freep(&vs->basename);
av_freep(&vs->vtt_basename);
av_freep(&vs->fmp4_init_filename);
av_freep(&vs->m3u8_name);
av_freep(&vs->vtt_m3u8_name);
av_freep(&vs->streams);
av_freep(&vs->baseurl);
if (vs->avf)
avformat_free_context(vs->avf);
if (vs->vtt_avf)
avformat_free_context(vs->vtt_avf);
} }
av_freep(&hls->var_streams); vs->basename = av_malloc(basename_size);
av_freep(&hls->master_m3u8_url); if (!vs->basename) {
ret = AVERROR(ENOMEM);
goto fail;
} }
return ret;
}
static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) av_strlcpy(vs->basename, hls->segment_filename, basename_size);
{
HLSContext *hls = s->priv_data;
AVFormatContext *oc = NULL;
AVStream *st = s->streams[pkt->stream_index];
int64_t end_pts = 0;
int is_ref_pkt = 1;
int ret = 0, can_split = 1, i, j;
int stream_index = 0;
int range_length = 0;
uint8_t *buffer = NULL;
VariantStream *vs = NULL;
for (i = 0; i < hls->nb_varstreams; i++) {
vs = &hls->var_streams[i];
for (j = 0; j < vs->nb_streams; j++) {
if (vs->streams[j] == st) {
if( st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE ) {
oc = vs->vtt_avf;
stream_index = 0;
} else { } else {
oc = vs->avf; if (hls->flags & HLS_SINGLE_FILE) {
stream_index = j; if (hls->segment_type == SEGMENT_TYPE_FMP4) {
} pattern = ".m4s";
break; } else {
} pattern = ".ts";
} }
if (oc)
break;
} }
if (!oc) { if (hls->use_localtime) {
av_log(s, AV_LOG_ERROR, "Unable to find mapping variant stream\n"); basename_size = strlen(s->filename) + strlen(pattern_localtime_fmt) + 1;
return AVERROR(ENOMEM); } else {
basename_size = strlen(s->filename) + strlen(pattern) + 1;
} }
end_pts = hls->recording_time * vs->number; if (hls->nb_varstreams > 1) {
basename_size += strlen(POSTFIX_PATTERN);
if (vs->sequence - vs->nb_entries > hls->start_sequence && hls->init_time > 0) {
/* reset end_pts, hls->recording_time at end of the init hls list */
int init_list_dur = hls->init_time * vs->nb_entries * AV_TIME_BASE;
int after_init_list_dur = (vs->sequence - vs->nb_entries ) * hls->time * AV_TIME_BASE;
hls->recording_time = hls->time * AV_TIME_BASE;
end_pts = init_list_dur + after_init_list_dur ;
} }
if (vs->start_pts == AV_NOPTS_VALUE) { vs->basename = av_malloc(basename_size);
vs->start_pts = pkt->pts; if (!vs->basename) {
ret = AVERROR(ENOMEM);
goto fail;
} }
if (vs->has_video) { av_strlcpy(vs->basename, s->filename, basename_size);
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;
}
if (pkt->pts == AV_NOPTS_VALUE)
is_ref_pkt = can_split = 0;
if (is_ref_pkt) { p = strrchr(vs->basename, '.');
if (vs->end_pts == AV_NOPTS_VALUE) if (p)
vs->end_pts = pkt->pts; *p = '\0';
if (vs->new_start) { if (hls->use_localtime) {
vs->new_start = 0; av_strlcat(vs->basename, pattern_localtime_fmt, basename_size);
vs->duration = (double)(pkt->pts - vs->end_pts)
* st->time_base.num / st->time_base.den;
vs->dpp = (double)(pkt->duration) * st->time_base.num / st->time_base.den;
} else {
if (pkt->duration) {
vs->duration += (double)(pkt->duration) * st->time_base.num / st->time_base.den;
} else { } else {
av_log(s, AV_LOG_WARNING, "pkt->duration = 0, maybe the hls segment duration will not precise\n"); av_strlcat(vs->basename, pattern, basename_size);
vs->duration = (double)(pkt->pts - vs->end_pts) * st->time_base.num / st->time_base.den;
} }
} }
m3u8_name_size = strlen(s->filename) + 1;
if (hls->nb_varstreams > 1) {
m3u8_name_size += strlen(POSTFIX_PATTERN);
}
vs->m3u8_name = av_malloc(m3u8_name_size);
if (!vs->m3u8_name ) {
ret = AVERROR(ENOMEM);
goto fail;
} }
if (vs->packets_written && can_split && av_compare_ts(pkt->pts - vs->start_pts, st->time_base, av_strlcpy(vs->m3u8_name, s->filename, m3u8_name_size);
end_pts, AV_TIME_BASE_Q) >= 0) {
int64_t new_start_pos;
char *old_filename = av_strdup(vs->avf->filename);
int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0);
if (!old_filename) { if (hls->nb_varstreams > 1) {
return AVERROR(ENOMEM); ret = format_name(vs->basename, basename_size, i);
if (ret < 0)
goto fail;
ret = format_name(vs->m3u8_name, m3u8_name_size, i);
if (ret < 0)
goto fail;
} }
av_write_frame(vs->avf, NULL); /* Flush any buffered data */ if (hls->segment_type == SEGMENT_TYPE_FMP4) {
if (hls->nb_varstreams > 1)
new_start_pos = avio_tell(vs->avf->pb); fmp4_init_filename_len += strlen(POSTFIX_PATTERN);
vs->size = new_start_pos - vs->start_pos; vs->fmp4_init_filename = av_malloc(fmp4_init_filename_len);
if (!vs->fmp4_init_filename ) {
ret = AVERROR(ENOMEM);
goto fail;
}
av_strlcpy(vs->fmp4_init_filename, hls->fmp4_init_filename,
fmp4_init_filename_len);
if (hls->nb_varstreams > 1) {
ret = format_name(vs->fmp4_init_filename, fmp4_init_filename_len, i);
if (ret < 0)
goto fail;
}
if (!byterange_mode) { if (av_strcasecmp(hls->fmp4_init_filename, "init.mp4")) {
if (hls->segment_type == SEGMENT_TYPE_FMP4 && !vs->init_range_length) { fmp4_init_filename_len = strlen(vs->fmp4_init_filename) + 1;
avio_flush(oc->pb); vs->base_output_dirname = av_malloc(fmp4_init_filename_len);
range_length = avio_close_dyn_buf(oc->pb, &buffer); if (!vs->base_output_dirname) {
avio_write(vs->out, buffer, range_length); ret = AVERROR(ENOMEM);
vs->init_range_length = range_length; goto fail;
avio_open_dyn_buf(&oc->pb); }
vs->packets_written = 0; av_strlcpy(vs->base_output_dirname, vs->fmp4_init_filename,
ff_format_io_close(s, &vs->out); fmp4_init_filename_len);
hlsenc_io_close(s, &vs->out, vs->base_output_dirname);
} else { } else {
hlsenc_io_close(s, &oc->pb, oc->filename); fmp4_init_filename_len = strlen(vs->m3u8_name) +
strlen(vs->fmp4_init_filename) + 1;
vs->base_output_dirname = av_malloc(fmp4_init_filename_len);
if (!vs->base_output_dirname) {
ret = AVERROR(ENOMEM);
goto fail;
} }
if (vs->vtt_avf) {
hlsenc_io_close(s, &vs->vtt_avf->pb, vs->vtt_avf->filename); av_strlcpy(vs->base_output_dirname, vs->m3u8_name,
fmp4_init_filename_len);
p = strrchr(vs->base_output_dirname, '/');
if (p) {
*(p + 1) = '\0';
av_strlcat(vs->base_output_dirname, vs->fmp4_init_filename,
fmp4_init_filename_len);
} else {
av_strlcpy(vs->base_output_dirname, vs->fmp4_init_filename,
fmp4_init_filename_len);
} }
} }
if ((hls->flags & HLS_TEMP_FILE) && oc->filename[0]) {
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);
hls_rename_temp_file(s, oc);
} }
if (vs->fmp4_init_mode) { if (!hls->use_localtime) {
vs->number--; ret = sls_flag_check_duration_size_index(hls);
if (ret < 0) {
goto fail;
} }
} else {
if (!vs->fmp4_init_mode || byterange_mode) ret = sls_flag_check_duration_size(hls, vs);
ret = hls_append_segment(s, hls, vs, vs->duration, vs->start_pos, vs->size);
vs->start_pos = new_start_pos;
if (ret < 0) { if (ret < 0) {
av_free(old_filename); goto fail;
return ret;
} }
}
if (vs->has_subtitle) {
vs->end_pts = pkt->pts; if (hls->flags & HLS_SINGLE_FILE)
vs->duration = 0; vtt_pattern = ".vtt";
vtt_basename_size = strlen(s->filename) + strlen(vtt_pattern) + 1;
if (hls->nb_varstreams > 1) {
vtt_basename_size += strlen(POSTFIX_PATTERN);
}
vs->fmp4_init_mode = 0; vs->vtt_basename = av_malloc(vtt_basename_size);
if (hls->flags & HLS_SINGLE_FILE) { if (!vs->vtt_basename) {
vs->number++; ret = AVERROR(ENOMEM);
} else if (hls->max_seg_size > 0) { goto fail;
if (vs->start_pos >= hls->max_seg_size) {
vs->sequence++;
sls_flag_file_rename(hls, vs, old_filename);
ret = hls_start(s, vs);
vs->start_pos = 0;
/* When split segment by byte, the duration is short than hls_time,
* so it is not enough one segment duration as hls_time, */
vs->number--;
} }
vs->number++; vs->vtt_m3u8_name = av_malloc(vtt_basename_size);
} else { if (!vs->vtt_m3u8_name ) {
sls_flag_file_rename(hls, vs, old_filename); ret = AVERROR(ENOMEM);
ret = hls_start(s, vs); goto fail;
} }
av_free(old_filename); av_strlcpy(vs->vtt_basename, s->filename, vtt_basename_size);
p = strrchr(vs->vtt_basename, '.');
if (p)
*p = '\0';
if (ret < 0) { if ( hls->subtitle_filename ) {
return ret; strcpy(vs->vtt_m3u8_name, hls->subtitle_filename);
} else {
strcpy(vs->vtt_m3u8_name, vs->vtt_basename);
av_strlcat(vs->vtt_m3u8_name, "_vtt.m3u8", vtt_basename_size);
} }
av_strlcat(vs->vtt_basename, vtt_pattern, vtt_basename_size);
if (!vs->fmp4_init_mode || byterange_mode) if (hls->nb_varstreams > 1) {
if ((ret = hls_window(s, 0, vs)) < 0) { ret= format_name(vs->vtt_basename, vtt_basename_size, i);
return ret; if (ret < 0)
goto fail;
ret = format_name(vs->vtt_m3u8_name, vtt_basename_size, i);
if (ret < 0)
goto fail;
} }
} }
vs->packets_written++; if (hls->baseurl) {
ret = ff_write_chained(oc, stream_index, pkt, s, 0); vs->baseurl = av_strdup(hls->baseurl);
if (!vs->baseurl) {
return ret; ret = AVERROR(ENOMEM);
} goto fail;
static int hls_write_trailer(struct AVFormatContext *s)
{
HLSContext *hls = s->priv_data;
AVFormatContext *oc = NULL;
AVFormatContext *vtt_oc = NULL;
char *old_filename = NULL;
int i;
VariantStream *vs = NULL;
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->filename);
if (!old_filename) {
return AVERROR(ENOMEM);
} }
av_write_trailer(oc);
if (oc->pb) {
vs->size = avio_tell(vs->avf->pb) - vs->start_pos;
ff_format_io_close(s, &oc->pb);
if ((hls->flags & HLS_TEMP_FILE) && oc->filename[0]) {
hls_rename_temp_file(s, oc);
} }
/* after av_write_trailer, then duration + 1 duration per packet */ if ((hls->flags & HLS_SINGLE_FILE) && (hls->segment_type == SEGMENT_TYPE_FMP4)) {
hls_append_segment(s, hls, vs, vs->duration + vs->dpp, vs->start_pos, vs->size); vs->fmp4_init_filename = av_strdup(vs->basename);
if (!vs->fmp4_init_filename) {
ret = AVERROR(ENOMEM);
goto fail;
} }
sls_flag_file_rename(hls, vs, old_filename);
if (vtt_oc) {
if (vtt_oc->pb)
av_write_trailer(vtt_oc);
vs->size = avio_tell(vs->vtt_avf->pb) - vs->start_pos;
ff_format_io_close(s, &vtt_oc->pb);
} }
av_freep(&vs->basename); if ((ret = hls_mux_init(s, vs)) < 0)
av_freep(&vs->base_output_dirname); goto fail;
avformat_free_context(oc);
vs->avf = NULL; if (hls->flags & HLS_APPEND_LIST) {
hls_window(s, 1, vs); parse_playlist(s, vs->m3u8_name, vs);
vs->discontinuity = 1;
if (hls->init_time > 0) {
av_log(s, AV_LOG_WARNING, "append_list mode does not support hls_init_time,"
" hls_init_time value will have no effect\n");
hls->init_time = 0;
hls->recording_time = hls->time * AV_TIME_BASE;
}
}
av_freep(&vs->fmp4_init_filename); if (hls->segment_type != SEGMENT_TYPE_FMP4 || hls->flags & HLS_SINGLE_FILE) {
if (vtt_oc) { if ((ret = hls_start(s, vs)) < 0)
av_freep(&vs->vtt_basename); goto fail;
av_freep(&vs->vtt_m3u8_name); }
avformat_free_context(vtt_oc);
} }
hls_free_segments(vs->segments); fail:
hls_free_segments(vs->old_segments); if (ret < 0) {
av_free(old_filename); av_freep(&hls->key_basename);
for (i = 0; i < hls->nb_varstreams && hls->var_streams; i++) {
vs = &hls->var_streams[i];
av_freep(&vs->basename);
av_freep(&vs->vtt_basename);
av_freep(&vs->fmp4_init_filename);
av_freep(&vs->m3u8_name); av_freep(&vs->m3u8_name);
av_freep(&vs->vtt_m3u8_name);
av_freep(&vs->streams); av_freep(&vs->streams);
av_freep(&vs->baseurl); av_freep(&vs->baseurl);
if (vs->avf)
avformat_free_context(vs->avf);
if (vs->vtt_avf)
avformat_free_context(vs->vtt_avf);
} }
av_freep(&hls->key_basename);
av_freep(&hls->var_streams); av_freep(&hls->var_streams);
av_freep(&hls->master_m3u8_url); av_freep(&hls->master_m3u8_url);
return 0; }
return ret;
} }
#define OFFSET(x) offsetof(HLSContext, x) #define OFFSET(x) offsetof(HLSContext, x)
...@@ -2336,6 +2344,7 @@ AVOutputFormat ff_hls_muxer = { ...@@ -2336,6 +2344,7 @@ AVOutputFormat ff_hls_muxer = {
.video_codec = AV_CODEC_ID_H264, .video_codec = AV_CODEC_ID_H264,
.subtitle_codec = AV_CODEC_ID_WEBVTT, .subtitle_codec = AV_CODEC_ID_WEBVTT,
.flags = AVFMT_NOFILE | AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, .flags = AVFMT_NOFILE | AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH,
.init = hls_init,
.write_header = hls_write_header, .write_header = hls_write_header,
.write_packet = hls_write_packet, .write_packet = hls_write_packet,
.write_trailer = hls_write_trailer, .write_trailer = hls_write_trailer,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment