[FFmpeg-devel] [PATCH 1/1] avformat/hlsenc: closed caption tags in the master playlist
Steven Liu
lingjiujianke at gmail.com
Fri Dec 29 14:05:53 EET 2017
2017-12-29 19:58 GMT+08:00 Dixit, Vishwanath <vdixit at akamai.com>:
>
>
> On 12/29/17 5:11 PM, Steven Liu wrote:
>> 2017-12-29 18:44 GMT+08:00 <vdixit at akamai.com>:
>>> From: Vishwanath Dixit <vdixit at akamai.com>
>>>
>>> ---
>>> doc/muxers.texi | 4 ++++
>>> libavformat/dashenc.c | 2 +-
>>> libavformat/hlsenc.c | 14 +++++++++++++-
>>> libavformat/hlsplaylist.c | 5 ++++-
>>> libavformat/hlsplaylist.h | 3 ++-
>>> 5 files changed, 24 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/doc/muxers.texi b/doc/muxers.texi
>>> index 93db549..8229202 100644
>>> --- a/doc/muxers.texi
>>> +++ b/doc/muxers.texi
>>> @@ -872,6 +872,10 @@ publishing it repeatedly every after 30 segments i.e. every after 60s.
>>> @item http_persistent
>>> Use persistent HTTP connections. Applicable only for HTTP output.
>>>
>>> + at item cc_instream_id
>>> +Add @code{#EXT-X-MEDIA} tag in the master playlist with the specified instream ID. It
>>> +accepts the values in the range 1-4.
>>> +
>>> @end table
>>>
>>> @anchor{ico}
>>> diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c
>>> index 478a384..8797959 100644
>>> --- a/libavformat/dashenc.c
>>> +++ b/libavformat/dashenc.c
>>> @@ -760,7 +760,7 @@ static int write_manifest(AVFormatContext *s, int final)
>>> AVStream *st = s->streams[i];
>>> get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i);
>>> ff_hls_write_stream_info(st, out, st->codecpar->bit_rate,
>>> - playlist_file, NULL);
>>> + playlist_file, NULL, NULL);
>>> }
>>> avio_close(out);
>>> if (use_rename)
>>> diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
>>> index 74f66ce..e5176a8 100644
>>> --- a/libavformat/hlsenc.c
>>> +++ b/libavformat/hlsenc.c
>>> @@ -206,6 +206,7 @@ typedef struct HLSContext {
>>> int http_persistent;
>>> AVIOContext *m3u8_out;
>>> AVIOContext *sub_m3u8_out;
>>> + int cc_instream_id; /* closed captions INSTREAM-ID */
>>> } HLSContext;
>>>
>>> static int mkdir_p(const char *path) {
>>> @@ -1122,6 +1123,7 @@ static int create_master_playlist(AVFormatContext *s,
>>> unsigned int i, j;
>>> int m3u8_name_size, ret, bandwidth;
>>> char *m3u8_rel_name;
>>> + char cc_group[16] = {0};
>>>
>>> input_vs->m3u8_created = 1;
>>> if (!hls->master_m3u8_created) {
>>> @@ -1148,6 +1150,14 @@ static int create_master_playlist(AVFormatContext *s,
>>>
>>> ff_hls_write_playlist_version(hls->m3u8_out, hls->version);
>>>
>>> + if (hls->cc_instream_id) {
>>> + av_strlcpy(cc_group, "group_cc", sizeof(cc_group));
>>
>> Why do you write "group_cc" ? maybe this can make an option be set by
>> user, and set a default string, if it can be set by user, need
>> attention with the printf, about that security.
>>
> Unlike audio rendition streams, in common usage scenarios, there will be only one cc rendition stream and all the video rendition streams refer to the same cc rendition stream. The following example shows a common use case where both video rendition streams (media_0.m3u8 and media_2.m3u8) are referring to the same closed caption rendition with group id “group_cc”.
> #EXTM3U
> #EXT-X-VERSION:3
> #EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,GROUP-ID="group_cc",NAME="captions",INSTREAM-ID="CC1"
> #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="group_aud1",NAME="audio_0",DEFAULT=YES,URI="media_1.m3u8"
> #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="group_aud2",NAME="audio_0",DEFAULT=YES,URI="media_3.m3u8"
> #EXT-X-STREAM-INF:BANDWIDTH=140800,RESOLUTION=1280x720,AUDIO="group_aud1",CLOSED-CAPTIONS="group_cc"
> media_0.m3u8
>
> #EXT-X-STREAM-INF:BANDWIDTH=140800,AUDIO="group_aud1"
> media_1.m3u8
>
> #EXT-X-STREAM-INF:BANDWIDTH=140800,RESOLUTION=1280x720,AUDIO="group_aud2",CLOSED-CAPTIONS="group_cc"
> media_2.m3u8
>
> #EXT-X-STREAM-INF:BANDWIDTH=140800,AUDIO="group_aud2"
> media_3.m3u8
>
> So, currently the group id name is fixed to “group_cc”. However, this functionality can be further extended in the future for different CC groups when there are different closed caption sources are present in the input. Please let me know your further thoughts/suggestions on this.
I think "group_cc" should be a variable, it can be set by the user,
not write to a constant by hlsenc. for example:
#EXTM3U
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Main", \
DEFAULT=YES,URI="low/main/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Centerfield", \
DEFAULT=NO,URI="low/centerfield/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Dugout", \
DEFAULT=NO,URI="low/dugout/audio-video.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS="...",VIDEO="low"
low/main/audio-video.m3u8
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Main", \
DEFAULT=YES,URI="mid/main/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Centerfield", \
DEFAULT=NO,URI="mid/centerfield/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Dugout", \
DEFAULT=NO,URI="mid/dugout/audio-video.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS="...",VIDEO="mid"
mid/main/audio-video.m3u8
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Main", \
DEFAULT=YES,URI="hi/main/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Centerfield", \
DEFAULT=NO,URI="hi/centerfield/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Dugout", \
DEFAULT=NO,URI="hi/dugout/audio-video.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS="...",VIDEO="hi"
hi/main/audio-video.m3u8
What do you think about it??
>
>
>>> + avio_printf(hls->m3u8_out, "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,GROUP-ID=\"%s\"",
>>> + cc_group);
>>> + avio_printf(hls->m3u8_out, ",NAME=\"captions\",INSTREAM-ID=\"CC%d\"\n",
>>> + hls->cc_instream_id);
>>> + }
>>> +
>>> /* For audio only variant streams add #EXT-X-MEDIA tag with attributes*/
>>> for (i = 0; i < hls->nb_varstreams; i++) {
>>> vs = &(hls->var_streams[i]);
>>> @@ -1235,7 +1245,8 @@ static int create_master_playlist(AVFormatContext *s,
>>> bandwidth += bandwidth / 10;
>>>
>>> ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth, m3u8_rel_name,
>>> - aud_st ? vs->agroup : NULL);
>>> + aud_st ? vs->agroup : NULL,
>>> + vid_st ? cc_group : NULL);
>>>
>>> av_freep(&m3u8_rel_name);
>>> }
>>> @@ -2444,6 +2455,7 @@ static const AVOption options[] = {
>>> {"master_pl_name", "Create HLS master playlist with this name", OFFSET(master_pl_name), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
>>> {"master_pl_publish_rate", "Publish master play list every after this many segment intervals", OFFSET(master_publish_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, UINT_MAX, E},
>>> {"http_persistent", "Use persistent HTTP connections", OFFSET(http_persistent), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
>>> + {"cc_instream_id", "Closed captions INSTREAM-ID", OFFSET(cc_instream_id), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 4, E},
>>> { NULL },
>>> };
>>>
>>> diff --git a/libavformat/hlsplaylist.c b/libavformat/hlsplaylist.c
>>> index 42f059a..8619436 100644
>>> --- a/libavformat/hlsplaylist.c
>>> +++ b/libavformat/hlsplaylist.c
>>> @@ -36,7 +36,8 @@ void ff_hls_write_playlist_version(AVIOContext *out, int version) {
>>> }
>>>
>>> void ff_hls_write_stream_info(AVStream *st, AVIOContext *out,
>>> - int bandwidth, char *filename, char *agroup) {
>>> + int bandwidth, char *filename, char *agroup,
>>> + char *cc_group) {
>>> if (!out || !filename)
>>> return;
>>>
>>> @@ -52,6 +53,8 @@ void ff_hls_write_stream_info(AVStream *st, AVIOContext *out,
>>> st->codecpar->height);
>>> if (agroup && strlen(agroup) > 0)
>>> avio_printf(out, ",AUDIO=\"group_%s\"", agroup);
>>> + if (cc_group && strlen(cc_group) > 0)
>>> + avio_printf(out, ",CLOSED-CAPTIONS=\"%s\"", cc_group);
>>> avio_printf(out, "\n%s\n\n", filename);
>>> }
>>>
>>> diff --git a/libavformat/hlsplaylist.h b/libavformat/hlsplaylist.h
>>> index ac03550..71ccee7 100644
>>> --- a/libavformat/hlsplaylist.h
>>> +++ b/libavformat/hlsplaylist.h
>>> @@ -38,7 +38,8 @@ typedef enum {
>>>
>>> void ff_hls_write_playlist_version(AVIOContext *out, int version);
>>> void ff_hls_write_stream_info(AVStream *st, AVIOContext *out,
>>> - int bandwidth, char *filename, char *agroup);
>>> + int bandwidth, char *filename, char *agroup,
>>> + char *cc_group);
>>> void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache,
>>> int target_duration, int64_t sequence,
>>> uint32_t playlist_type);
>>> --
>>> 1.9.1
>>>
More information about the ffmpeg-devel
mailing list