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)
{
HLSContext *hls = s->priv_data;
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;
int basename_size = 0;
int vtt_basename_size = 0, m3u8_name_size = 0;
VariantStream *vs = NULL;
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;
}
for (i = 0; i < hls->nb_varstreams; i++) {
vs = &hls->var_streams[i];
//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) {
av_dict_copy(&options, hls->format_options, 0);
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);
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;
}
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) {
ret = update_master_pl_info(s);
if (ret < 0) {
av_log(s, AV_LOG_ERROR, "Master stream info update failed with status %x\n",
ret);
goto fail;
if (hls->max_seg_size > 0) {
if ((outer_st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) &&
(outer_st->codecpar->bit_rate > hls->max_seg_size)) {
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 (hls->segment_type == SEGMENT_TYPE_FMP4) {
pattern = "%d.m4s";
if (outer_st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
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) ||
(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);
avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den);
}
av_log(hls, AV_LOG_DEBUG, "start_number evaluated to %"PRId64"\n", hls->start_sequence);
}
fail:
for (i = 0; i < hls->nb_varstreams; i++) {
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';
return ret;
}
if (hls->flags & HLS_SPLIT_BY_TIME && hls->flags & HLS_INDEPENDENT_SEGMENTS) {
// Independent segments cannot be guaranteed when splitting by time
hls->flags &= ~HLS_INDEPENDENT_SEGMENTS;
av_log(s, AV_LOG_WARNING,
"'split_by_time' and 'independent_segments' cannot be enabled together. "
"Disabling 'independent_segments' flag\n");
}
static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
{
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;
if (hls->flags & HLS_PROGRAM_DATE_TIME) {
time_t now0;
time(&now0);
vs->initial_prog_date_time = now0;
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 {
oc = vs->avf;
stream_index = j;
}
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;
break;
}
}
for (j = 0; j < vs->nb_streams; j++) {
vs->has_video +=
vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO;
vs->has_subtitle +=
vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE;
if (oc)
break;
}
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 (!oc) {
av_log(s, AV_LOG_ERROR, "Unable to find mapping variant stream\n");
return AVERROR(ENOMEM);
}
if (!vs->oformat) {
ret = AVERROR_MUXER_NOT_FOUND;
goto fail;
}
end_pts = hls->recording_time * vs->number;
if(vs->has_subtitle) {
vs->vtt_oformat = av_guess_format("webvtt", NULL, NULL);
if (!vs->oformat) {
ret = AVERROR_MUXER_NOT_FOUND;
goto fail;
}
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 (hls->segment_filename) {
basename_size = strlen(hls->segment_filename) + 1;
if (hls->nb_varstreams > 1) {
basename_size += strlen(POSTFIX_PATTERN);
if (vs->start_pts == AV_NOPTS_VALUE) {
vs->start_pts = pkt->pts;
}
vs->basename = av_malloc(basename_size);
if (!vs->basename) {
ret = AVERROR(ENOMEM);
goto fail;
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;
}
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 {
if (hls->flags & HLS_SINGLE_FILE) {
if (hls->segment_type == SEGMENT_TYPE_FMP4) {
pattern = ".m4s";
if (pkt->duration) {
vs->duration += (double)(pkt->duration) * st->time_base.num / st->time_base.den;
} 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) {
basename_size += strlen(POSTFIX_PATTERN);
}
if (vs->packets_written && can_split && av_compare_ts(pkt->pts - vs->start_pts, st->time_base,
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 (!vs->basename) {
ret = AVERROR(ENOMEM);
goto fail;
if (!old_filename) {
return AVERROR(ENOMEM);
}
av_strlcpy(vs->basename, s->filename, basename_size);
av_write_frame(vs->avf, NULL); /* Flush any buffered data */
p = strrchr(vs->basename, '.');
if (p)
*p = '\0';
if (hls->use_localtime) {
av_strlcat(vs->basename, pattern_localtime_fmt, basename_size);
new_start_pos = avio_tell(vs->avf->pb);
vs->size = new_start_pos - vs->start_pos;
if (!byterange_mode) {
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 {
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);
}
vs->m3u8_name = av_malloc(m3u8_name_size);
if (!vs->m3u8_name ) {
ret = AVERROR(ENOMEM);
goto fail;
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);
}
av_strlcpy(vs->m3u8_name, s->filename, m3u8_name_size);
if (vs->fmp4_init_mode) {
vs->number--;
}
if (hls->nb_varstreams > 1) {
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;
if (!vs->fmp4_init_mode || byterange_mode)
ret = hls_append_segment(s, hls, vs, vs->duration, vs->start_pos, vs->size);
vs->start_pos = new_start_pos;
if (ret < 0) {
av_free(old_filename);
return ret;
}
if (hls->segment_type == SEGMENT_TYPE_FMP4) {
if (hls->nb_varstreams > 1)
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;
}
vs->end_pts = pkt->pts;
vs->duration = 0;
if (av_strcasecmp(hls->fmp4_init_filename, "init.mp4")) {
fmp4_init_filename_len = 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;
vs->fmp4_init_mode = 0;
if (hls->flags & HLS_SINGLE_FILE) {
vs->number++;
} else if (hls->max_seg_size > 0) {
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--;
}
av_strlcpy(vs->base_output_dirname, vs->fmp4_init_filename,
fmp4_init_filename_len);
vs->number++;
} else {
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;
sls_flag_file_rename(hls, vs, old_filename);
ret = hls_start(s, vs);
}
av_free(old_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 (ret < 0) {
return ret;
}
if (!vs->fmp4_init_mode || byterange_mode)
if ((ret = hls_window(s, 0, vs)) < 0) {
return ret;
}
}
if (!hls->use_localtime) {
ret = sls_flag_check_duration_size_index(hls);
if (ret < 0) {
goto fail;
}
} else {
ret = sls_flag_check_duration_size(hls, vs);
if (ret < 0) {
goto fail;
vs->packets_written++;
ret = ff_write_chained(oc, stream_index, pkt, s, 0);
return ret;
}
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);
}
if(vs->has_subtitle) {
if (hls->flags & HLS_SINGLE_FILE)
vtt_pattern = ".vtt";
vtt_basename_size = strlen(s->filename) + strlen(vtt_pattern) + 1;
if (hls->nb_varstreams > 1) {
vtt_basename_size += strlen(POSTFIX_PATTERN);
/* 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);
}
vs->vtt_basename = av_malloc(vtt_basename_size);
if (!vs->vtt_basename) {
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);
}
vs->vtt_m3u8_name = av_malloc(vtt_basename_size);
if (!vs->vtt_m3u8_name ) {
ret = AVERROR(ENOMEM);
goto fail;
av_freep(&vs->basename);
av_freep(&vs->base_output_dirname);
avformat_free_context(oc);
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 ) {
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);
hls_free_segments(vs->segments);
hls_free_segments(vs->old_segments);
av_free(old_filename);
av_freep(&vs->m3u8_name);
av_freep(&vs->streams);
av_freep(&vs->baseurl);
}
av_strlcat(vs->vtt_basename, vtt_pattern, vtt_basename_size);
if (hls->nb_varstreams > 1) {
ret= format_name(vs->vtt_basename, vtt_basename_size, i);
if (ret < 0)
goto fail;
ret = format_name(vs->vtt_m3u8_name, vtt_basename_size, i);
if (ret < 0)
av_freep(&hls->key_basename);
av_freep(&hls->var_streams);
av_freep(&hls->master_m3u8_url);
return 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;
}
//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) {
vs->baseurl = av_strdup(hls->baseurl);
if (!vs->baseurl) {
ret = AVERROR(ENOMEM);
if (hls->master_pl_name) {
ret = update_master_pl_info(s);
if (ret < 0) {
av_log(s, AV_LOG_ERROR, "Master stream info update failed with status %x\n",
ret);
goto fail;
}
}
if ((hls->flags & HLS_SINGLE_FILE) && (hls->segment_type == SEGMENT_TYPE_FMP4)) {
vs->fmp4_init_filename = av_strdup(vs->basename);
if (!vs->fmp4_init_filename) {
ret = AVERROR(ENOMEM);
goto fail;
if (hls->segment_type == SEGMENT_TYPE_FMP4) {
pattern = "%d.m4s";
}
if ((hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH) ||
(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)
goto fail;
hls->recording_time = (hls->init_time ? hls->init_time : hls->time) * AV_TIME_BASE;
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) {
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;
}
if (hls->flags & HLS_SPLIT_BY_TIME && hls->flags & HLS_INDEPENDENT_SEGMENTS) {
// Independent segments cannot be guaranteed when splitting by time
hls->flags &= ~HLS_INDEPENDENT_SEGMENTS;
av_log(s, AV_LOG_WARNING,
"'split_by_time' and 'independent_segments' cannot be enabled together. "
"Disabling 'independent_segments' flag\n");
}
if (hls->segment_type != SEGMENT_TYPE_FMP4 || hls->flags & HLS_SINGLE_FILE) {
if ((ret = hls_start(s, vs)) < 0)
goto fail;
if (hls->flags & HLS_PROGRAM_DATE_TIME) {
time_t now0;
time(&now0);
vs->initial_prog_date_time = now0;
}
av_dict_copy(&options, hls->format_options, 0);
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);
av_dict_free(&options);
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;
}
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) {
if ((outer_st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) &&
(outer_st->codecpar->bit_rate > hls->max_seg_size)) {
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);
for (j = 0; j < vs->nb_streams; j++) {
vs->has_video += 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)
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)
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 (!vs->oformat) {
ret = AVERROR_MUXER_NOT_FOUND;
goto fail;
}
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 (ret < 0) {
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->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);
if (hls->segment_filename) {
basename_size = strlen(hls->segment_filename) + 1;
if (hls->nb_varstreams > 1) {
basename_size += strlen(POSTFIX_PATTERN);
}
av_freep(&hls->var_streams);
av_freep(&hls->master_m3u8_url);
vs->basename = av_malloc(basename_size);
if (!vs->basename) {
ret = AVERROR(ENOMEM);
goto fail;
}
return ret;
}
static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
{
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;
av_strlcpy(vs->basename, hls->segment_filename, basename_size);
} else {
oc = vs->avf;
stream_index = j;
}
break;
}
if (hls->flags & HLS_SINGLE_FILE) {
if (hls->segment_type == SEGMENT_TYPE_FMP4) {
pattern = ".m4s";
} else {
pattern = ".ts";
}
if (oc)
break;
}
if (!oc) {
av_log(s, AV_LOG_ERROR, "Unable to find mapping variant stream\n");
return AVERROR(ENOMEM);
if (hls->use_localtime) {
basename_size = strlen(s->filename) + strlen(pattern_localtime_fmt) + 1;
} else {
basename_size = strlen(s->filename) + strlen(pattern) + 1;
}
end_pts = hls->recording_time * vs->number;
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 (hls->nb_varstreams > 1) {
basename_size += strlen(POSTFIX_PATTERN);
}
if (vs->start_pts == AV_NOPTS_VALUE) {
vs->start_pts = pkt->pts;
vs->basename = av_malloc(basename_size);
if (!vs->basename) {
ret = AVERROR(ENOMEM);
goto fail;
}
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;
}
if (pkt->pts == AV_NOPTS_VALUE)
is_ref_pkt = can_split = 0;
av_strlcpy(vs->basename, s->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 {
if (pkt->duration) {
vs->duration += (double)(pkt->duration) * st->time_base.num / st->time_base.den;
p = strrchr(vs->basename, '.');
if (p)
*p = '\0';
if (hls->use_localtime) {
av_strlcat(vs->basename, pattern_localtime_fmt, basename_size);
} else {
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;
av_strlcat(vs->basename, pattern, basename_size);
}
}
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,
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);
av_strlcpy(vs->m3u8_name, s->filename, m3u8_name_size);
if (!old_filename) {
return AVERROR(ENOMEM);
if (hls->nb_varstreams > 1) {
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 */
new_start_pos = avio_tell(vs->avf->pb);
vs->size = new_start_pos - vs->start_pos;
if (hls->segment_type == SEGMENT_TYPE_FMP4) {
if (hls->nb_varstreams > 1)
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 (!byterange_mode) {
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);
if (av_strcasecmp(hls->fmp4_init_filename, "init.mp4")) {
fmp4_init_filename_len = 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;
}
av_strlcpy(vs->base_output_dirname, vs->fmp4_init_filename,
fmp4_init_filename_len);
} 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) {
vs->number--;
if (!hls->use_localtime) {
ret = sls_flag_check_duration_size_index(hls);
if (ret < 0) {
goto fail;
}
if (!vs->fmp4_init_mode || byterange_mode)
ret = hls_append_segment(s, hls, vs, vs->duration, vs->start_pos, vs->size);
vs->start_pos = new_start_pos;
} else {
ret = sls_flag_check_duration_size(hls, vs);
if (ret < 0) {
av_free(old_filename);
return ret;
goto fail;
}
}
if (vs->has_subtitle) {
vs->end_pts = pkt->pts;
vs->duration = 0;
if (hls->flags & HLS_SINGLE_FILE)
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;
if (hls->flags & HLS_SINGLE_FILE) {
vs->number++;
} else if (hls->max_seg_size > 0) {
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->vtt_basename = av_malloc(vtt_basename_size);
if (!vs->vtt_basename) {
ret = AVERROR(ENOMEM);
goto fail;
}
vs->number++;
} else {
sls_flag_file_rename(hls, vs, old_filename);
ret = hls_start(s, vs);
vs->vtt_m3u8_name = av_malloc(vtt_basename_size);
if (!vs->vtt_m3u8_name ) {
ret = AVERROR(ENOMEM);
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) {
return ret;
if ( hls->subtitle_filename ) {
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 ((ret = hls_window(s, 0, vs)) < 0) {
return ret;
if (hls->nb_varstreams > 1) {
ret= format_name(vs->vtt_basename, vtt_basename_size, i);
if (ret < 0)
goto fail;
ret = format_name(vs->vtt_m3u8_name, vtt_basename_size, i);
if (ret < 0)
goto fail;
}
}
vs->packets_written++;
ret = ff_write_chained(oc, stream_index, pkt, s, 0);
return ret;
}
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);
if (hls->baseurl) {
vs->baseurl = av_strdup(hls->baseurl);
if (!vs->baseurl) {
ret = AVERROR(ENOMEM);
goto fail;
}
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 */
hls_append_segment(s, hls, vs, vs->duration + vs->dpp, vs->start_pos, vs->size);
if ((hls->flags & HLS_SINGLE_FILE) && (hls->segment_type == SEGMENT_TYPE_FMP4)) {
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);
av_freep(&vs->base_output_dirname);
avformat_free_context(oc);
if ((ret = hls_mux_init(s, vs)) < 0)
goto fail;
vs->avf = NULL;
hls_window(s, 1, vs);
if (hls->flags & HLS_APPEND_LIST) {
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 (vtt_oc) {
av_freep(&vs->vtt_basename);
av_freep(&vs->vtt_m3u8_name);
avformat_free_context(vtt_oc);
if (hls->segment_type != SEGMENT_TYPE_FMP4 || hls->flags & HLS_SINGLE_FILE) {
if ((ret = hls_start(s, vs)) < 0)
goto fail;
}
}
hls_free_segments(vs->segments);
hls_free_segments(vs->old_segments);
av_free(old_filename);
fail:
if (ret < 0) {
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->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->key_basename);
av_freep(&hls->var_streams);
av_freep(&hls->master_m3u8_url);
return 0;
}
return ret;
}
#define OFFSET(x) offsetof(HLSContext, x)
......@@ -2336,6 +2344,7 @@ AVOutputFormat ff_hls_muxer = {
.video_codec = AV_CODEC_ID_H264,
.subtitle_codec = AV_CODEC_ID_WEBVTT,
.flags = AVFMT_NOFILE | AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH,
.init = hls_init,
.write_header = hls_write_header,
.write_packet = hls_write_packet,
.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