Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
avformat/hlsenc: enhanced %v handling with variant names
When multiple variant streams are specified by var_stream_map option, %v
placeholder in various names ensures that each variant has its unique
names. Currently %v is substituted by its variant index value (0, 1, 2
etc.). In some use cases it would be handy to specify names for variants
instead of numerical indexes. This patch makes it possible to use names
instead of default indexes. In var_stream_map option each or some of the
variant streams may use an optional name attributum (e.g.
-var_stream_map "v:0,a:0,name:sd v:1,a:1,name:720p") If a name is
specified for a variant, then this name value will be used as
substitution value of %v instead of the default index value.

Signed-off-by: Bela Bodecs <bodecsb@vivanet.hu>
Signed-off-by: Steven Liu <lq@onvideo.cn>
  • Loading branch information
DoubleBB authored and T-bagwell committed Jun 20, 2019
1 parent 09a4853 commit 86f04b9
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 30 deletions.
16 changes: 15 additions & 1 deletion doc/muxers.texi
Expand Up @@ -943,7 +943,21 @@ This example creates two hls variant streams. The first variant stream will
contain video stream of bitrate 1000k and audio stream of bitrate 64k and the
second variant stream will contain video stream of bitrate 256k and audio
stream of bitrate 32k. Here, two media playlist with file names out_0.m3u8 and
out_1.m3u8 will be created.
out_1.m3u8 will be created. If you want something meaningful text instead of indexes
in result names, you may specify names for each or some of the variants
as in the following example.


@example
ffmpeg -re -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k -b:a:1 32k \
-map 0:v -map 0:a -map 0:v -map 0:a -f hls -var_stream_map "v:0,a:0,name:my_hd v:1,a:1,name:my_sd" \
http://example.com/live/out_%v.m3u8
@end example

This example creates two hls variant streams as in the previous one.
But here, the two media playlist with file names out_my_hd.m3u8 and
out_my_sd.m3u8 will be created.

@example
ffmpeg -re -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k \
-map 0:v -map 0:a -map 0:v -f hls -var_stream_map "v:0 a:0 v:1" \
Expand Down
110 changes: 81 additions & 29 deletions libavformat/hlsenc.c
Expand Up @@ -164,6 +164,7 @@ typedef struct VariantStream {
char *agroup; /* audio group name */
char *ccgroup; /* closed caption group name */
char *baseurl;
char *varname; // variant name
} VariantStream;

typedef struct ClosedCaptionsStream {
Expand Down Expand Up @@ -340,6 +341,47 @@ static void write_codec_attr(AVStream *st, VariantStream *vs) {
return;
}

static int replace_str_data_in_filename(char **s, const char *filename, char placeholder, const char *datastring)
{
const char *p;
char *new_filename;
char c;
int addchar_count;
int found_count = 0;
AVBPrint buf;

av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);

p = filename;
for (;;) {
c = *p;
if (c == '\0')
break;
if (c == '%' && *(p+1) == '%') // %%
addchar_count = 2;
else if (c == '%' && *(p+1) == placeholder) {
av_bprintf(&buf, "%s", datastring);
p += 2;
addchar_count = 0;
found_count ++;
} else
addchar_count = 1;

if (addchar_count > 0) {
av_bprint_append_data(&buf, p, addchar_count);
p += addchar_count;
}
}
if (!av_bprint_is_complete(&buf)) {
av_bprint_finalize(&buf, NULL);
return -1;
}
if (av_bprint_finalize(&buf, &new_filename) < 0 || !new_filename)
return -1;
*s = new_filename;
return found_count;
}

static int replace_int_data_in_filename(char **s, const char *filename, char placeholder, int64_t number)
{
const char *p;
Expand Down Expand Up @@ -474,20 +516,27 @@ static int hls_delete_old_segments(AVFormatContext *s, HLSContext *hls,

}

while (segment) {
/* if %v is present in the file's directory
* all segment belongs to the same variant, so do it only once before the loop*/
if (dirname && av_stristr(dirname, "%v")) {
char * r_dirname = dirname;

/* if %v is present in the file's directory */
if (dirname && av_stristr(dirname, "%v")) {

if (!vs->varname) {
if (replace_int_data_in_filename(&r_dirname, dirname, 'v', segment->var_stream_idx) < 1) {
ret = AVERROR(EINVAL);
goto fail;
}
av_free(dirname);
dirname = r_dirname;
} else {
if (replace_str_data_in_filename(&r_dirname, dirname, 'v', vs->varname) < 1) {
ret = AVERROR(EINVAL);
goto fail;
}
}

av_free(dirname);
dirname = r_dirname;
}

while (segment) {
av_log(hls, AV_LOG_DEBUG, "deleting old segment %s\n",
segment->filename);
path_size = (hls->use_localtime_mkdir ? 0 : strlen(dirname)) + strlen(segment->filename) + 1;
Expand Down Expand Up @@ -1761,7 +1810,7 @@ static int validate_name(int nb_vs, const char *fn)
return ret;
}

static int format_name(const char *buf, char **s, int index)
static int format_name(const char *buf, char **s, int index, const char *varname)
{
const char *proto, *dir;
char *orig_buf_dup = NULL, *mod_buf_dup = NULL;
Expand All @@ -1778,9 +1827,16 @@ static int format_name(const char *buf, char **s, int index)
return ret;
}

if (replace_int_data_in_filename(s, orig_buf_dup, 'v', index) < 1) {
ret = AVERROR(EINVAL);
goto fail;
if (!varname) {
if (replace_int_data_in_filename(s, orig_buf_dup, 'v', index) < 1) {
ret = AVERROR(EINVAL);
goto fail;
}
} else {
if (replace_str_data_in_filename(s, orig_buf_dup, 'v', varname) < 1) {
ret = AVERROR(EINVAL);
goto fail;
}
}

proto = avio_find_protocol_name(orig_buf_dup);
Expand Down Expand Up @@ -1898,6 +1954,11 @@ static int parse_variant_stream_mapstring(AVFormatContext *s)
(!av_strncasecmp(val, "1", strlen("1"))));
hls->has_default_key = 1;
continue;
} else if (av_strstart(keyval, "name:", &val)) {
vs->varname = av_strdup(val);
if (!vs->varname)
return AVERROR(ENOMEM);
continue;
} else if (av_strstart(keyval, "agroup:", &val)) {
vs->agroup = av_strdup(val);
if (!vs->agroup)
Expand Down Expand Up @@ -2417,6 +2478,7 @@ static void hls_free_variant_streams(struct HLSContext *hls)
av_freep(&vs->language);
av_freep(&vs->ccgroup);
av_freep(&vs->baseurl);
av_freep(&vs->varname);
}
}

Expand Down Expand Up @@ -2629,12 +2691,7 @@ static int hls_init(AVFormatContext *s)
for (i = 0; i < hls->nb_varstreams; i++) {
vs = &hls->var_streams[i];

vs->m3u8_name = av_strdup(s->url);
if (!vs->m3u8_name ) {
ret = AVERROR(ENOMEM);
goto fail;
}
ret = format_name(s->url, i, vs->m3u8_name);
ret = format_name(s->url, &vs->m3u8_name, i, vs->varname);
if (ret < 0)
goto fail;

Expand Down Expand Up @@ -2695,17 +2752,10 @@ static int hls_init(AVFormatContext *s)
}
}
if (hls->segment_filename) {
basename_size = strlen(hls->segment_filename) + 1;
vs->basename = av_malloc(basename_size);
if (!vs->basename) {
ret = AVERROR(ENOMEM);
goto fail;
}

av_strlcpy(vs->basename, hls->segment_filename, basename_size);
ret = format_name(vs->basename, basename_size, i);
ret = format_name(hls->segment_filename, &vs->basename, i, vs->varname);
if (ret < 0)
goto fail;
basename_size = strlen(vs->basename) + 1;
} else {
if (hls->flags & HLS_SINGLE_FILE) {
if (hls->segment_type == SEGMENT_TYPE_FMP4) {
Expand Down Expand Up @@ -2758,7 +2808,8 @@ static int hls_init(AVFormatContext *s)
fmp4_init_filename_len);
if (hls->nb_varstreams > 1) {
if (av_stristr(vs->fmp4_init_filename, "%v")) {
format_name(vs->fmp4_init_filename, fmp4_init_filename_len, i);
av_freep(&vs->fmp4_init_filename);
format_name(hls->fmp4_init_filename, &vs->fmp4_init_filename, i, vs->varname);
} else {
ret = append_postfix(vs->fmp4_init_filename, fmp4_init_filename_len, i);
}
Expand Down Expand Up @@ -2822,8 +2873,8 @@ static int hls_init(AVFormatContext *s)
*p = '\0';

if ( hls->subtitle_filename ) {
strcpy(vs->vtt_m3u8_name, hls->subtitle_filename);
ret = format_name(vs->vtt_m3u8_name, vtt_basename_size, i);
av_freep(&vs->vtt_m3u8_name);
ret = format_name(hls->subtitle_filename, &vs->vtt_m3u8_name, i, vs->varname);
if (ret < 0)
goto fail;
} else {
Expand Down Expand Up @@ -2874,6 +2925,7 @@ static int hls_init(AVFormatContext *s)
av_freep(&vs->agroup);
av_freep(&vs->ccgroup);
av_freep(&vs->baseurl);
av_freep(&vs->varname);
if (vs->avf)
avformat_free_context(vs->avf);
if (vs->vtt_avf)
Expand Down

0 comments on commit 86f04b9

Please sign in to comment.