依次介绍hlsenc.c的
hls_write_header,hls_start,hls_write_packet,hls_window,hls_append_segment,hls_write_trailer。
hls_write_header
此函数一开始就被调用,只会被调1次。
static int hls_write_header(AVFormatContext*s)
{
HLSContext *hls = s->priv_data;
int ret, i;
char *p;
const char *pattern = "%d.ts";
const char *pattern_localtime_fmt = "-%s.ts";
const char *vtt_pattern = "%d.vtt";
AVDictionary *options = NULL;
int basename_size;
int vtt_basename_size;
hls->sequence =hls->start_sequence;
hls->recording_time = hls->time * AV_TIME_BASE;
hls->start_pts = AV_NOPTS_VALUE;
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 (i = 0; i < s->nb_streams; i++) {
hls->has_video +=
s->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO;
hls->has_subtitle +=
s->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE;
}
if (hls->has_video > 1)
av_log(s, AV_LOG_WARNING,
"More than a single videostream present, "
"expect issues decodingit.\n");
hls->oformat = av_guess_format("mpegts", NULL, NULL);
if (!hls->oformat) {
ret = AVERROR_MUXER_NOT_FOUND;
goto fail;
}
if(hls->has_subtitle) {
hls->vtt_oformat = av_guess_format("webvtt", NULL, NULL);
if (!hls->oformat) {
ret = AVERROR_MUXER_NOT_FOUND;
goto fail;
}
}
if (hls->segment_filename) {
hls->basename = av_strdup(hls->segment_filename);
if (!hls->basename) {
ret = AVERROR(ENOMEM);
goto fail;
}
}else {
if (hls->flags & HLS_SINGLE_FILE)
pattern = ".ts";
if (hls->use_localtime) {
basename_size = strlen(s->filename) + strlen(pattern_localtime_fmt) +1;
} else {
basename_size = strlen(s->filename) + strlen(pattern) + 1;
}
hls->basename = av_malloc(basename_size);
if (!hls->basename) {
ret = AVERROR(ENOMEM);
goto fail;
}
av_strlcpy(hls->basename, s->filename, basename_size);
// s->filename为m3u8名称,如:/home/a.m3u8
p = strrchr(hls->basename, '.');
if (p)
*p = '\0';
if (hls->use_localtime) {
av_strlcat(hls->basename, pattern_localtime_fmt, basename_size);
} else {
av_strlcat(hls->basename, pattern, basename_size);
hls->basename 为ts名称,如:/home/a.ts
}
}
if(hls->has_subtitle) {
if (hls->flags & HLS_SINGLE_FILE)
vtt_pattern = ".vtt";
vtt_basename_size = strlen(s->filename) + strlen(vtt_pattern) + 1;
hls->vtt_basename = av_malloc(vtt_basename_size);
if (!hls->vtt_basename) {
ret = AVERROR(ENOMEM);
goto fail;
}
hls->vtt_m3u8_name = av_malloc(vtt_basename_size);
if (!hls->vtt_m3u8_name ) {
ret = AVERROR(ENOMEM);
goto fail;
}
av_strlcpy(hls->vtt_basename, s->filename, vtt_basename_size);
p = strrchr(hls->vtt_basename, '.');
if (p)
*p = '\0';
if( hls->subtitle_filename ) {
strcpy(hls->vtt_m3u8_name, hls->subtitle_filename);
} else {
strcpy(hls->vtt_m3u8_name, hls->vtt_basename);
av_strlcat(hls->vtt_m3u8_name, "_vtt.m3u8",vtt_basename_size);
}
av_strlcat(hls->vtt_basename, vtt_pattern, vtt_basename_size);
}
if ((ret = hls_mux_init(s)) < 0)
goto fail;
if ((ret = hls_start(s)) < 0) //打开一个文件
goto fail;
av_dict_copy(&options, hls->format_options, 0);
ret = avformat_write_header(hls->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);
goto fail;
}
//av_assert0(s->nb_streams == hls->avf->nb_streams);
for (i = 0; i < s->nb_streams; i++) {
AVStream *inner_st;
AVStream *outer_st = s->streams[i];
if (outer_st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
inner_st = hls->avf->streams[i];
else if (hls->vtt_avf)
inner_st = hls->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);
}
fail:
av_dict_free(&options);
if (ret < 0) {
av_freep(&hls->basename);
av_freep(&hls->vtt_basename);
if (hls->avf)
avformat_free_context(hls->avf);
if (hls->vtt_avf)
avformat_free_context(hls->vtt_avf);
}
return ret;
}
hls_start
打开ts文件。
static int hls_start(AVFormatContext *s)
{
...
if((err = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE,&options)) < 0)
goto fail;
...
}
hls_write_packet
写入每个音视频包,即处理do_video_out/do_audio_out。
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 = hls->recording_time * hls->number;
int is_ref_pkt = 1;
int ret, can_split = 1;
int stream_index = 0;
if( st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE ) {
oc = hls->vtt_avf;
stream_index = 0;
}else {
oc = hls->avf;
stream_index = pkt->stream_index;
}
if (hls->start_pts == AV_NOPTS_VALUE) {
hls->start_pts = pkt->pts;
hls->end_pts = pkt->pts;
}
if (hls->has_video) {
can_split = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO&&
pkt->flags &AV_PKT_FLAG_KEY;
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)
hls->duration = (double)(pkt->pts - hls->end_pts)
* st->time_base.num /st->time_base.den;
if (can_split && av_compare_ts(pkt->pts - hls->start_pts,st->time_base,
end_pts,AV_TIME_BASE_Q) >= 0) {
//根据can_split ,需要重新打开一个文件,例如跑了一会,打印:
printf("%ld,%ld,%ld\n", pkt->pts, hls->start_pts, end_pts); 8528771,7176,22000000
end_pts为hls_time乘以帧数。
int64_t new_start_pos;
av_write_frame(oc, NULL); /* Flush any buffered data */
new_start_pos = avio_tell(hls->avf->pb);
hls->size = new_start_pos - hls->start_pos;
ret = hls_append_segment(s, hls, hls->duration, hls->start_pos,hls->size);
hls->start_pos = new_start_pos;
if (ret < 0)
return ret;
hls->end_pts = pkt->pts;
hls->duration = 0;
if (hls->flags & HLS_SINGLE_FILE) {
if (hls->avf->oformat->priv_class &&hls->avf->priv_data)
av_opt_set(hls->avf->priv_data,"mpegts_flags", "resend_headers", 0);
hls->number++;
} else {
//关闭文件。
ff_format_io_close(s, &oc->pb);
if (hls->vtt_avf)
ff_format_io_close(s,&hls->vtt_avf->pb);
//重现打开一个文件。
ret =hls_start(s);
}
if (ret < 0)
return ret;
if( st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE )
oc = hls->vtt_avf;
else
oc = hls->avf;
//生成2级m3u8
if ((ret = hls_window(s, 0)) < 0)
return ret;
}
ret = ff_write_chained(oc, stream_index, pkt, s, 0);
return ret;
}
hls_window
此函数用于生成2级m3u8,先写个临时文件,再rename,包括EXT-X-DISCONTINUITY等信息。
static int hls_window(AVFormatContext *s,int last){
ff_rename(temp_filename, s->filename, s);
}
hls_append_segment
新生成的片加入到链表中,后面用于生成二级m3u8,
这个函数还可以根据配置执行hls_delete_old_segments,也就是删除旧的切片。
static int
(struct AVFormatContext *s, HLSContext*hls, double duration,
int64_t pos,int64_t size)
{
HLSSegment *en = av_malloc(sizeof(*en));
。。。。。。
}
hls_write_trailer
只调用一次,做一些清理工作。
static int hls_write_trailer(structAVFormatContext *s)
{
...
av_freep(&hls->basename);
...
}
问题
转码后TS时长变少
DURATION_MAX_READ_SIZE设置的小了。