[FFmpeg-devel] [PATCH] HLS master playlist - peak and average bandwidth settings
Amit Kale
amitk at hotstar.com
Mon Feb 5 12:11:54 EET 2018
Hi,
Kindly below a patch to add peak and average bandwidth settings for HLS
master playlist. It adds two hls_flags as described below.
peak_segment_bw
If this flag is set, BANDWIDTH value in a master playlist entry will be set
to the
peak segment bandwidth.
avg_bw
If this flag is set, AVERAGE-BANDWIDTH value will be added to a master
playlist
entry. This flag implies peak_segment_bw.
It changes behaviors of other codepaths as follows
1. Corrects setting of start_pos single file is not used in
hls_write_packet.
2. Adds a new parameter avgbw to ff_hls_write_playlist_version. If this
parameter is non-null AVERAGE-BANDWIDTH is emitted.
Thanks.
-Amit
Signed-off-by: Amit Kale <amitk at hotstar.com>
--
diff --git a/doc/muxers.texi b/doc/muxers.texi
index d9a5cc03dc..428d4009b3 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -741,6 +741,14 @@ subdirectories.
Possible values:
@table @samp
+ at item peak_segment_bw
+If this flag is set, BANDWIDTH value in a master playlist entry will be
+set to the peak segment bandwidth.
+
+ at item avg_bw
+If this flag is set, AVERAGE-BANDWIDTH value will be added to a master
+playlist entry. This flag implies peak_segment_bw.
+
@item single_file
If this flag is set, the muxer will store all segments in a single MPEG-TS
file, and will use byte ranges in the playlist. HLS playlists generated
with
diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c
index 0f6f4f22fa..a918f7e649 100644
--- a/libavformat/dashenc.c
+++ b/libavformat/dashenc.c
@@ -827,7 +827,7 @@ static int write_manifest(AVFormatContext *s, int final)
stream_bitrate += max_audio_bitrate;
}
get_hls_playlist_name(playlist_file, sizeof(playlist_file),
NULL, i);
- ff_hls_write_stream_info(st, out, stream_bitrate,
playlist_file, agroup, NULL, NULL);
+ ff_hls_write_stream_info(st, out, stream_bitrate, 0,
playlist_file, agroup, NULL, NULL);
}
avio_close(out);
if (use_rename)
diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
index cc13c94e97..e1592e0577 100644
--- a/libavformat/hlsenc.c
+++ b/libavformat/hlsenc.c
@@ -98,6 +98,8 @@ typedef enum HLSFlags {
HLS_TEMP_FILE = (1 << 11),
HLS_PERIODIC_REKEY = (1 << 12),
HLS_INDEPENDENT_SEGMENTS = (1 << 13),
+ HLS_PEAK_SEGMENT_BW = (1 << 14),
+ HLS_AVG_BW = (1 << 15),
} HLSFlags;
typedef enum {
@@ -112,11 +114,14 @@ typedef struct VariantStream {
AVOutputFormat *vtt_oformat;
AVIOContext *out;
int packets_written;
+ int64_t bytes_written;
+ double total_duration;
int init_range_length;
AVFormatContext *avf;
AVFormatContext *vtt_avf;
+ int avg_bandwidth;
int has_video;
int has_subtitle;
int new_start;
@@ -1168,14 +1173,15 @@ static int get_relative_url(const char *master_url,
const char *media_url,
}
static int create_master_playlist(AVFormatContext *s,
- VariantStream * const input_vs)
+ VariantStream * const input_vs,
+ int last)
{
HLSContext *hls = s->priv_data;
VariantStream *vs, *temp_vs;
AVStream *vid_st, *aud_st;
AVDictionary *options = NULL;
unsigned int i, j;
- int m3u8_name_size, ret, bandwidth;
+ int m3u8_name_size, ret, bandwidth, avgbw;
char *m3u8_rel_name, *ccgroup;
ClosedCaptionsStream *ccs;
@@ -1185,7 +1191,7 @@ static int create_master_playlist(AVFormatContext *s,
for (i = 0; i < hls->nb_varstreams; i++)
if (!hls->var_streams[i].m3u8_created)
return 0;
- } else {
+ } else if (!last) {
/* Keep publishing the master playlist at the configured rate */
if (&hls->var_streams[0] != input_vs || !hls->master_publish_rate
||
input_vs->number % hls->master_publish_rate)
@@ -1290,13 +1296,28 @@ static int create_master_playlist(AVFormatContext
*s,
}
}
}
-
- bandwidth = 0;
- if (vid_st)
- bandwidth += vid_st->codecpar->bit_rate;
- if (aud_st)
- bandwidth += aud_st->codecpar->bit_rate;
- bandwidth += bandwidth / 10;
+
+ avgbw = 0;
+ if (last && (hls->flags & HLS_PEAK_SEGMENT_BW || hls->flags &
HLS_AVG_BW)) {
+ HLSSegment *hs = vs->segments;
+ bandwidth = 0;
+ while (hs) {
+ int64_t segment_bandwidth = hs->size * 8 / hs->duration;
+ if (bandwidth < segment_bandwidth)
+ bandwidth = segment_bandwidth;
+ hs = hs->next;
+ }
+ if (hls->flags & HLS_AVG_BW)
+ avgbw = vs->bytes_written / vs->total_duration;
+ } else {
+ bandwidth = 0;
+ avgbw = 0;
+ if (vid_st)
+ bandwidth += vid_st->codecpar->bit_rate;
+ if (aud_st)
+ bandwidth += aud_st->codecpar->bit_rate;
+ bandwidth += bandwidth / 10;
+ }
ccgroup = NULL;
if (vid_st && vs->ccgroup) {
@@ -1313,7 +1334,7 @@ static int create_master_playlist(AVFormatContext *s,
vs->ccgroup);
}
- ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth,
m3u8_rel_name,
+ ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth, avgbw,
m3u8_rel_name,
aud_st ? vs->agroup : NULL, vs->codec_attr, ccgroup);
av_freep(&m3u8_rel_name);
@@ -1437,7 +1458,7 @@ fail:
ff_rename(temp_filename, vs->m3u8_name, s);
if (ret >= 0 && hls->master_pl_name)
- if (create_master_playlist(s, vs) < 0)
+ if (create_master_playlist(s, vs, last) < 0)
av_log(s, AV_LOG_WARNING, "Master playlist creation failed\n");
return ret;
@@ -2179,6 +2200,8 @@ static int hls_write_packet(AVFormatContext *s,
AVPacket *pkt)
new_start_pos = avio_tell(vs->avf->pb);
vs->size = new_start_pos - vs->start_pos;
+ vs->bytes_written += vs->size;
+ vs->total_duration += vs->duration;
if (!byterange_mode) {
if (hls->segment_type == SEGMENT_TYPE_FMP4) {
@@ -2227,7 +2250,11 @@ static int hls_write_packet(AVFormatContext *s,
AVPacket *pkt)
ff_format_io_close(s, &vs->out);
}
ret = hls_append_segment(s, hls, vs, vs->duration, vs->start_pos,
vs->size);
- vs->start_pos = new_start_pos;
+
+ if (hls->flags & HLS_SINGLE_FILE)
+ vs->start_pos = new_start_pos;
+ else
+ vs->start_pos = 0;
if (ret < 0) {
av_free(old_filename);
return ret;
@@ -2268,7 +2295,7 @@ static int hls_write_packet(AVFormatContext *s,
AVPacket *pkt)
vs->packets_written++;
ret = ff_write_chained(oc, stream_index, pkt, s, 0);
-
+
return ret;
}
@@ -2311,6 +2338,9 @@ failed:
av_write_trailer(oc);
if (oc->pb) {
vs->size = avio_tell(vs->avf->pb) - vs->start_pos;
+ vs->bytes_written += vs->size;
+ vs->total_duration += vs->duration;
+
if (hls->segment_type != SEGMENT_TYPE_FMP4)
ff_format_io_close(s, &oc->pb);
@@ -2750,6 +2780,8 @@ static const AVOption options[] = {
{"fmp4", "make segment file to fragment mp4 files in m3u8", 0,
AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_FMP4 }, 0, UINT_MAX, E,
"segment_type"},
{"hls_fmp4_init_filename", "set fragment mp4 file init filename",
OFFSET(fmp4_init_filename), AV_OPT_TYPE_STRING, {.str =
"init.mp4"}, 0, 0, E},
{"hls_flags", "set flags affecting HLS playlist and media file
generation", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E,
"flags"},
+ {"avg_bandwidth", "sets AVERAGE-BANDWIDTH in master play list,
implies peak_segment_bw flag", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_AVG_BW },
0, UINT_MAX, E, "flags"},
+ {"peak_segment_bw", "sets bandwidth in master play list to peak
segment bandwidth", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_PEAK_SEGMENT_BW }, 0,
UINT_MAX, E, "flags"},
{"single_file", "generate a single media file indexed with byte
ranges", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SINGLE_FILE }, 0, UINT_MAX, E,
"flags"},
{"temp_file", "write segment to temporary file and rename when
complete", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_TEMP_FILE }, 0, UINT_MAX, E,
"flags"},
{"delete_segments", "delete segment files that are no longer part of
the playlist", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_DELETE_SEGMENTS }, 0,
UINT_MAX, E, "flags"},
diff --git a/libavformat/hlsplaylist.c b/libavformat/hlsplaylist.c
index efcbff0009..e101cb7777 100644
--- a/libavformat/hlsplaylist.c
+++ b/libavformat/hlsplaylist.c
@@ -46,7 +46,7 @@ void ff_hls_write_audio_rendition(AVIOContext *out, char
*agroup,
}
void ff_hls_write_stream_info(AVStream *st, AVIOContext *out,
- int bandwidth, char *filename, char *agroup,
+ int bandwidth, int avgbw, char *filename,
char *agroup,
char *codecs, char *ccgroup) {
if (!out || !filename)
@@ -59,6 +59,8 @@ void ff_hls_write_stream_info(AVStream *st, AVIOContext
*out,
}
avio_printf(out, "#EXT-X-STREAM-INF:BANDWIDTH=%d", bandwidth);
+ if (avgbw != 0)
+ avio_printf(out, ",AVERAGE-BANDWIDTH=%d", avgbw);
if (st && st->codecpar->width > 0 && st->codecpar->height > 0)
avio_printf(out, ",RESOLUTION=%dx%d", st->codecpar->width,
st->codecpar->height);
diff --git a/libavformat/hlsplaylist.h b/libavformat/hlsplaylist.h
index 5054b01c8f..2b7970b4c5 100644
--- a/libavformat/hlsplaylist.h
+++ b/libavformat/hlsplaylist.h
@@ -40,7 +40,7 @@ void ff_hls_write_playlist_version(AVIOContext *out, int
version);
void ff_hls_write_audio_rendition(AVIOContext *out, char *agroup,
char *filename, int name_id, int
is_default);
void ff_hls_write_stream_info(AVStream *st, AVIOContext *out,
- int bandwidth, char *filename, char *agroup,
+ int bandwidth, int avgbw, char *filename,
char *agroup,
char *codecs, char *ccgroup);
void ff_hls_write_playlist_header(AVIOContext *out, int version, int
allowcache,
int target_duration, int64_t sequence,
--
This message and its attachments are confidential (or legally privileged)
information and are meant solely for the addressee of such message. Any
unauthorized use of the message / its attachments is strictly prohibited.
More information about the ffmpeg-devel
mailing list