diff --git a/doc/muxers.texi b/doc/muxers.texi index d2d985f1ac..60c22ddd26 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -967,6 +967,21 @@ and they are mapped to the two video only variant streams with audio group names By default, a single hls variant containing all the encoded streams is created. +@example +ffmpeg -re -i in.ts -b:a:0 32k -b:a:1 64k -b:v:0 1000k \ + -map 0:a -map 0:a -map 0:v -f hls \ + -var_stream_map "a:0,agroup:aud_low,default:yes a:1,agroup:aud_low v:0,agroup:aud_low" \ + -master_pl_name master.m3u8 \ + http://example.com/live/out_%v.m3u8 +@end example +This example creates two audio only and one video only variant streams. In +addition to the #EXT-X-STREAM-INF tag for each variant stream in the master +playlist, #EXT-X-MEDIA tag is also added for the two audio only variant streams +and they are mapped to the one video only variant streams with audio group name +'aud_low', and the audio group have default stat is NO or YES. + +By default, a single hls variant containing all the encoded streams is created. + @item cc_stream_map Map string which specifies different closed captions groups and their attributes. The closed captions stream groups are separated by space. diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index eb094f7490..e6628145f8 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -153,6 +153,7 @@ typedef struct VariantStream { CodecAttributeStatus attr_status; unsigned int nb_streams; int m3u8_created; /* status of media play-list creation */ + int is_default; /* default status of audio group */ char *agroup; /* audio group name */ char *ccgroup; /* closed caption group name */ char *baseurl; @@ -228,6 +229,8 @@ typedef struct HLSContext { AVIOContext *sub_m3u8_out; int64_t timeout; int ignore_io_errors; + int has_default_key; /* has DEFAULT field of var_stream_map */ + int has_video_m3u8; /* has video stream m3u8 list */ } HLSContext; static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, @@ -1258,7 +1261,7 @@ static int create_master_playlist(AVFormatContext *s, goto fail; } - ff_hls_write_audio_rendition(hls->m3u8_out, vs->agroup, m3u8_rel_name, i, 1); + ff_hls_write_audio_rendition(hls->m3u8_out, vs->agroup, m3u8_rel_name, i, hls->has_default_key ? vs->is_default : 1); av_freep(&m3u8_rel_name); } @@ -1336,8 +1339,15 @@ static int create_master_playlist(AVFormatContext *s, vs->ccgroup); } - ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth, m3u8_rel_name, - aud_st ? vs->agroup : NULL, vs->codec_attr, ccgroup); + if (!hls->has_default_key || !hls->has_video_m3u8) { + ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth, m3u8_rel_name, + aud_st ? vs->agroup : NULL, vs->codec_attr, ccgroup); + } else { + if (vid_st) { + ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth, m3u8_rel_name, + aud_st ? vs->agroup : NULL, vs->codec_attr, ccgroup); + } + } av_freep(&m3u8_rel_name); } @@ -1819,7 +1829,7 @@ static int parse_variant_stream_mapstring(AVFormatContext *s) /** * Expected format for var_stream_map string is as below: * "a:0,v:0 a:1,v:1" - * "a:0,agroup:a0 a:1,agroup:a1 v:0,agroup:a0 v:1,agroup:a1" + * "a:0,agroup:a0,default:1 a:1,agroup:a1,defalut:0 v:0,agroup:a0 v:1,agroup:a1" * This string specifies how to group the audio, video and subtitle streams * into different variant streams. The variant stream groups are separated * by space. @@ -1850,6 +1860,7 @@ static int parse_variant_stream_mapstring(AVFormatContext *s) if (nb_varstreams < hls->nb_varstreams) { vs = &(hls->var_streams[nb_varstreams]); vs->var_stream_idx = nb_varstreams; + vs->is_default = 0; nb_varstreams++; } else return AVERROR(EINVAL); @@ -1869,7 +1880,12 @@ static int parse_variant_stream_mapstring(AVFormatContext *s) while (keyval = av_strtok(varstr, ",", &saveptr2)) { varstr = NULL; - if (av_strstart(keyval, "agroup:", &val)) { + if (av_strstart(keyval, "default:", &val)) { + vs->is_default = (!av_strncasecmp(val, "YES", strlen("YES")) || + (!av_strncasecmp(val, "1", strlen("1")))); + hls->has_default_key = 1; + continue; + } else if (av_strstart(keyval, "agroup:", &val)) { vs->agroup = av_strdup(val); if (!vs->agroup) return AVERROR(ENOMEM); @@ -1881,6 +1897,7 @@ static int parse_variant_stream_mapstring(AVFormatContext *s) continue; } else if (av_strstart(keyval, "v:", &val)) { codec_type = AVMEDIA_TYPE_VIDEO; + hls->has_video_m3u8 = 1; } else if (av_strstart(keyval, "a:", &val)) { codec_type = AVMEDIA_TYPE_AUDIO; } else if (av_strstart(keyval, "s:", &val)) { @@ -2519,6 +2536,8 @@ static int hls_init(AVFormatContext *s) int vtt_basename_size = 0; int fmp4_init_filename_len = strlen(hls->fmp4_init_filename) + 1; + hls->has_default_key = 0; + hls->has_video_m3u8 = 0; ret = update_variant_stream_info(s); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Variant stream info update failed with status %x\n",