mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
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>
This commit is contained in:
parent
09a4853930
commit
86f04b918c
@ -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" \
|
||||
|
@ -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 {
|
||||
@ -340,6 +341,47 @@ fail:
|
||||
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;
|
||||
@ -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;
|
||||
@ -1761,7 +1810,7 @@ fail:
|
||||
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;
|
||||
@ -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);
|
||||
@ -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)
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
@ -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);
|
||||
}
|
||||
@ -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 {
|
||||
@ -2874,6 +2925,7 @@ fail:
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user