Commit fa7a6dbd authored by Steven Liu's avatar Steven Liu

avformat/hlsenc: add EXT-X-I-FRAMES-ONLY tag support

Signed-off-by: 's avatarSteven Liu <lq@chinaffmpeg.org>
parent 4208b428
...@@ -852,6 +852,10 @@ including the file containing the AES encryption key. ...@@ -852,6 +852,10 @@ including the file containing the AES encryption key.
Add the @code{#EXT-X-INDEPENDENT-SEGMENTS} to playlists that has video segments Add the @code{#EXT-X-INDEPENDENT-SEGMENTS} to playlists that has video segments
and when all the segments of that playlist are guaranteed to start with a Key frame. and when all the segments of that playlist are guaranteed to start with a Key frame.
@item iframes_only
Add the @code{#EXT-X-I-FRAMES-ONLY} to playlists that has video segments
and can play only I-frames in the @code{#EXT-X-BYTERANGE} mode.
@item split_by_time @item split_by_time
Allow segments to start on frames other than keyframes. This improves Allow segments to start on frames other than keyframes. This improves
behavior on some players when the time between keyframes is inconsistent, behavior on some players when the time between keyframes is inconsistent,
......
...@@ -471,7 +471,7 @@ static void write_hls_media_playlist(OutputStream *os, AVFormatContext *s, ...@@ -471,7 +471,7 @@ static void write_hls_media_playlist(OutputStream *os, AVFormatContext *s,
} }
ff_hls_write_playlist_header(c->m3u8_out, 6, -1, target_duration, ff_hls_write_playlist_header(c->m3u8_out, 6, -1, target_duration,
start_number, PLAYLIST_TYPE_NONE); start_number, PLAYLIST_TYPE_NONE, 0);
ff_hls_write_init_file(c->m3u8_out, os->initfile, c->single_file, ff_hls_write_init_file(c->m3u8_out, os->initfile, c->single_file,
os->init_range_length, os->init_start_pos); os->init_range_length, os->init_start_pos);
...@@ -491,7 +491,7 @@ static void write_hls_media_playlist(OutputStream *os, AVFormatContext *s, ...@@ -491,7 +491,7 @@ static void write_hls_media_playlist(OutputStream *os, AVFormatContext *s,
(double) seg->duration / timescale, 0, (double) seg->duration / timescale, 0,
seg->range_length, seg->start_pos, NULL, seg->range_length, seg->start_pos, NULL,
c->single_file ? os->initfile : seg->file, c->single_file ? os->initfile : seg->file,
&prog_date_time); &prog_date_time, 0, 0, 0);
if (ret < 0) { if (ret < 0) {
av_log(os->ctx, AV_LOG_WARNING, "ff_hls_write_file_entry get error\n"); av_log(os->ctx, AV_LOG_WARNING, "ff_hls_write_file_entry get error\n");
} }
......
...@@ -75,6 +75,8 @@ typedef struct HLSSegment { ...@@ -75,6 +75,8 @@ typedef struct HLSSegment {
int discont; int discont;
int64_t pos; int64_t pos;
int64_t size; int64_t size;
int64_t keyframe_pos;
int64_t keyframe_size;
unsigned var_stream_idx; unsigned var_stream_idx;
char key_uri[LINE_BUFFER_SIZE + 1]; char key_uri[LINE_BUFFER_SIZE + 1];
...@@ -99,6 +101,7 @@ typedef enum HLSFlags { ...@@ -99,6 +101,7 @@ typedef enum HLSFlags {
HLS_TEMP_FILE = (1 << 11), HLS_TEMP_FILE = (1 << 11),
HLS_PERIODIC_REKEY = (1 << 12), HLS_PERIODIC_REKEY = (1 << 12),
HLS_INDEPENDENT_SEGMENTS = (1 << 13), HLS_INDEPENDENT_SEGMENTS = (1 << 13),
HLS_I_FRAMES_ONLY = (1 << 14),
} HLSFlags; } HLSFlags;
typedef enum { typedef enum {
...@@ -125,6 +128,9 @@ typedef struct VariantStream { ...@@ -125,6 +128,9 @@ typedef struct VariantStream {
double dpp; // duration per packet double dpp; // duration per packet
int64_t start_pts; int64_t start_pts;
int64_t end_pts; int64_t end_pts;
int64_t video_lastpos;
int64_t video_keyframe_pos;
int64_t video_keyframe_size;
double duration; // last segment duration computed so far, in seconds double duration; // last segment duration computed so far, in seconds
int64_t start_pos; // last segment starting position int64_t start_pos; // last segment starting position
int64_t size; // last segment size int64_t size; // last segment size
...@@ -994,6 +1000,8 @@ static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, ...@@ -994,6 +1000,8 @@ static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls,
en->duration = duration; en->duration = duration;
en->pos = pos; en->pos = pos;
en->size = size; en->size = size;
en->keyframe_pos = vs->video_keyframe_pos;
en->keyframe_size = vs->video_keyframe_size;
en->next = NULL; en->next = NULL;
en->discont = 0; en->discont = 0;
...@@ -1411,7 +1419,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) ...@@ -1411,7 +1419,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs)
vs->discontinuity_set = 0; vs->discontinuity_set = 0;
ff_hls_write_playlist_header(hls->m3u8_out, hls->version, hls->allowcache, ff_hls_write_playlist_header(hls->m3u8_out, hls->version, hls->allowcache,
target_duration, sequence, hls->pl_type); 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 ){ if((hls->flags & HLS_DISCONT_START) && sequence==hls->start_sequence && vs->discontinuity_set==0 ){
avio_printf(hls->m3u8_out, "#EXT-X-DISCONTINUITY\n"); avio_printf(hls->m3u8_out, "#EXT-X-DISCONTINUITY\n");
...@@ -1439,7 +1447,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) ...@@ -1439,7 +1447,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs)
ret = ff_hls_write_file_entry(hls->m3u8_out, en->discont, byterange_mode, ret = ff_hls_write_file_entry(hls->m3u8_out, en->discont, byterange_mode,
en->duration, hls->flags & HLS_ROUND_DURATIONS, en->duration, hls->flags & HLS_ROUND_DURATIONS,
en->size, en->pos, vs->baseurl, en->size, en->pos, vs->baseurl,
en->filename, prog_date_time_p); en->filename, prog_date_time_p, en->keyframe_size, en->keyframe_pos, hls->flags & HLS_I_FRAMES_ONLY);
if (ret < 0) { if (ret < 0) {
av_log(s, AV_LOG_WARNING, "ff_hls_write_file_entry get error\n"); av_log(s, AV_LOG_WARNING, "ff_hls_write_file_entry get error\n");
} }
...@@ -1455,11 +1463,11 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) ...@@ -1455,11 +1463,11 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs)
goto fail; goto fail;
} }
ff_hls_write_playlist_header(hls->sub_m3u8_out, hls->version, hls->allowcache, ff_hls_write_playlist_header(hls->sub_m3u8_out, hls->version, hls->allowcache,
target_duration, sequence, PLAYLIST_TYPE_NONE); target_duration, sequence, PLAYLIST_TYPE_NONE, 0);
for (en = vs->segments; en; en = en->next) { for (en = vs->segments; en; en = en->next) {
ret = ff_hls_write_file_entry(hls->sub_m3u8_out, 0, byterange_mode, ret = ff_hls_write_file_entry(hls->sub_m3u8_out, 0, byterange_mode,
en->duration, 0, en->size, en->pos, en->duration, 0, en->size, en->pos,
vs->baseurl, en->sub_filename, NULL); vs->baseurl, en->sub_filename, NULL, 0, 0, 0);
if (ret < 0) { if (ret < 0) {
av_log(s, AV_LOG_WARNING, "ff_hls_write_file_entry get error\n"); av_log(s, AV_LOG_WARNING, "ff_hls_write_file_entry get error\n");
} }
...@@ -2205,7 +2213,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) ...@@ -2205,7 +2213,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
vs->start_pts = pkt->pts; vs->start_pts = pkt->pts;
} }
if (vs->has_video) { if (vs->has_video) {
can_split = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && can_split = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
((pkt->flags & AV_PKT_FLAG_KEY) || (hls->flags & HLS_SPLIT_BY_TIME)); ((pkt->flags & AV_PKT_FLAG_KEY) || (hls->flags & HLS_SPLIT_BY_TIME));
is_ref_pkt = (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) && (pkt->stream_index == vs->reference_stream_index); is_ref_pkt = (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) && (pkt->stream_index == vs->reference_stream_index);
...@@ -2240,6 +2248,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) ...@@ -2240,6 +2248,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
av_write_frame(vs->avf, NULL); /* Flush any buffered data */ av_write_frame(vs->avf, NULL); /* Flush any buffered data */
new_start_pos = avio_tell(vs->avf->pb); new_start_pos = avio_tell(vs->avf->pb);
if (hls->segment_type != SEGMENT_TYPE_FMP4) { if (hls->segment_type != SEGMENT_TYPE_FMP4) {
avio_flush(oc->pb); avio_flush(oc->pb);
vs->size = new_start_pos - vs->start_pos; vs->size = new_start_pos - vs->start_pos;
...@@ -2368,6 +2377,13 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) ...@@ -2368,6 +2377,13 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
vs->packets_written++; vs->packets_written++;
if (oc->pb) { if (oc->pb) {
ret = ff_write_chained(oc, stream_index, pkt, s, 0); ret = ff_write_chained(oc, stream_index, pkt, s, 0);
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;
} else {
vs->video_lastpos = avio_tell(oc->pb);
}
if (hls->ignore_io_errors) if (hls->ignore_io_errors)
ret = 0; ret = 0;
} }
...@@ -2919,6 +2935,7 @@ static const AVOption options[] = { ...@@ -2919,6 +2935,7 @@ static const AVOption options[] = {
{"second_level_segment_size", "include segment size in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_SIZE }, 0, UINT_MAX, E, "flags"}, {"second_level_segment_size", "include segment size in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_SIZE }, 0, UINT_MAX, E, "flags"},
{"periodic_rekey", "reload keyinfo file periodically for re-keying", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_PERIODIC_REKEY }, 0, UINT_MAX, E, "flags"}, {"periodic_rekey", "reload keyinfo file periodically for re-keying", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_PERIODIC_REKEY }, 0, UINT_MAX, E, "flags"},
{"independent_segments", "add EXT-X-INDEPENDENT-SEGMENTS, whenever applicable", 0, AV_OPT_TYPE_CONST, { .i64 = HLS_INDEPENDENT_SEGMENTS }, 0, UINT_MAX, E, "flags"}, {"independent_segments", "add EXT-X-INDEPENDENT-SEGMENTS, whenever applicable", 0, AV_OPT_TYPE_CONST, { .i64 = HLS_INDEPENDENT_SEGMENTS }, 0, UINT_MAX, E, "flags"},
{"iframes_only", "add EXT-X-I-FRAMES-ONLY, whenever applicable", 0, AV_OPT_TYPE_CONST, { .i64 = HLS_I_FRAMES_ONLY }, 0, UINT_MAX, E, "flags"},
#if FF_API_HLS_USE_LOCALTIME #if FF_API_HLS_USE_LOCALTIME
{"use_localtime", "set filename expansion with strftime at segment creation(will be deprecated )", OFFSET(use_localtime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, {"use_localtime", "set filename expansion with strftime at segment creation(will be deprecated )", OFFSET(use_localtime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
#endif #endif
......
...@@ -76,7 +76,7 @@ void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, ...@@ -76,7 +76,7 @@ void ff_hls_write_stream_info(AVStream *st, AVIOContext *out,
void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache, void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache,
int target_duration, int64_t sequence, int target_duration, int64_t sequence,
uint32_t playlist_type) { uint32_t playlist_type, int iframe_mode) {
if (!out) if (!out)
return; return;
ff_hls_write_playlist_version(out, version); ff_hls_write_playlist_version(out, version);
...@@ -92,6 +92,9 @@ void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache, ...@@ -92,6 +92,9 @@ void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache,
} else if (playlist_type == PLAYLIST_TYPE_VOD) { } else if (playlist_type == PLAYLIST_TYPE_VOD) {
avio_printf(out, "#EXT-X-PLAYLIST-TYPE:VOD\n"); avio_printf(out, "#EXT-X-PLAYLIST-TYPE:VOD\n");
} }
if (iframe_mode) {
avio_printf(out, "#EXT-X-I-FRAMES-ONLY\n");
}
} }
void ff_hls_write_init_file(AVIOContext *out, char *filename, void ff_hls_write_init_file(AVIOContext *out, char *filename,
...@@ -108,7 +111,8 @@ int ff_hls_write_file_entry(AVIOContext *out, int insert_discont, ...@@ -108,7 +111,8 @@ int ff_hls_write_file_entry(AVIOContext *out, int insert_discont,
double duration, int round_duration, double duration, int round_duration,
int64_t size, int64_t pos, //Used only if HLS_SINGLE_FILE flag is set int64_t size, int64_t pos, //Used only if HLS_SINGLE_FILE flag is set
char *baseurl, //Ignored if NULL char *baseurl, //Ignored if NULL
char *filename, double *prog_date_time) { char *filename, double *prog_date_time,
int64_t video_keyframe_size, int64_t video_keyframe_pos, int iframe_mode) {
if (!out || !filename) if (!out || !filename)
return AVERROR(EINVAL); return AVERROR(EINVAL);
...@@ -120,7 +124,8 @@ int ff_hls_write_file_entry(AVIOContext *out, int insert_discont, ...@@ -120,7 +124,8 @@ int ff_hls_write_file_entry(AVIOContext *out, int insert_discont,
else else
avio_printf(out, "#EXTINF:%f,\n", duration); avio_printf(out, "#EXTINF:%f,\n", duration);
if (byterange_mode) if (byterange_mode)
avio_printf(out, "#EXT-X-BYTERANGE:%"PRId64"@%"PRId64"\n", size, pos); avio_printf(out, "#EXT-X-BYTERANGE:%"PRId64"@%"PRId64"\n", iframe_mode ? video_keyframe_size : size,
iframe_mode ? video_keyframe_pos : pos);
if (prog_date_time) { if (prog_date_time) {
time_t tt, wrongsecs; time_t tt, wrongsecs;
......
...@@ -44,7 +44,7 @@ void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, ...@@ -44,7 +44,7 @@ void ff_hls_write_stream_info(AVStream *st, AVIOContext *out,
char *codecs, char *ccgroup); char *codecs, char *ccgroup);
void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache, void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache,
int target_duration, int64_t sequence, int target_duration, int64_t sequence,
uint32_t playlist_type); uint32_t playlist_type, int iframe_mode);
void ff_hls_write_init_file(AVIOContext *out, char *filename, void ff_hls_write_init_file(AVIOContext *out, char *filename,
int byterange_mode, int64_t size, int64_t pos); int byterange_mode, int64_t size, int64_t pos);
int ff_hls_write_file_entry(AVIOContext *out, int insert_discont, int ff_hls_write_file_entry(AVIOContext *out, int insert_discont,
...@@ -52,7 +52,8 @@ int ff_hls_write_file_entry(AVIOContext *out, int insert_discont, ...@@ -52,7 +52,8 @@ int ff_hls_write_file_entry(AVIOContext *out, int insert_discont,
double duration, int round_duration, double duration, int round_duration,
int64_t size, int64_t pos, //Used only if HLS_SINGLE_FILE flag is set int64_t size, int64_t pos, //Used only if HLS_SINGLE_FILE flag is set
char *baseurl, //Ignored if NULL char *baseurl, //Ignored if NULL
char *filename, double *prog_date_time); char *filename, double *prog_date_time,
int64_t video_keyframe_size, int64_t video_keyframe_pos, int iframe_mode);
void ff_hls_write_end_list (AVIOContext *out); void ff_hls_write_end_list (AVIOContext *out);
#endif /* AVFORMAT_HLSPLAYLIST_H_ */ #endif /* AVFORMAT_HLSPLAYLIST_H_ */
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