[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