1 /*
2 * WebM DASH Manifest XML muxer
3 * Copyright (c) 2014 Vignesh Venkatasubramanian
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /*
23 * WebM DASH Specification:
24 * https://sites.google.com/a/webmproject.org/wiki/adaptive-streaming/webm-dash-specification
25 * ISO DASH Specification:
26 * http://standards.iso.org/ittf/PubliclyAvailableStandards/c065274_ISO_IEC_23009-1_2014.zip
27 */
28
30 #include <stdint.h>
31 #include <string.h>
32
36
41
43 char id[10];
47
62
64 {
65 switch (codec_id) {
67 return "vp8";
69 return "vp9";
71 return "vorbis";
73 return "opus";
74 }
76 }
77
79 {
80 int i = 0;
81 double max = 0.0;
85 if (!duration || atof(duration->
value) < 0)
continue;
86 if (atof(duration->
value) > max) max = atof(duration->
value);
87 }
88 return max / 1000;
89 }
90
92 {
94 double min_buffer_time = 1.0;
95 avio_printf(s->
pb,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
97 avio_printf(s->
pb,
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
98 avio_printf(s->
pb,
" xmlns=\"urn:mpeg:DASH:schema:MPD:2011\"\n");
99 avio_printf(s->
pb,
" xsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011\"\n");
104 }
105 avio_printf(s->
pb,
" minBufferTime=\"PT%gS\"\n", min_buffer_time);
107 w->
is_live ?
"urn:mpeg:dash:profile:isoff-live:2011" :
"urn:webm:dash:profile:webm-on-demand:2012",
110 time_t local_time = time(
NULL);
111 struct tm gmt_buffer;
112 struct tm *gmt =
gmtime_r(&local_time, &gmt_buffer);
113 char gmt_iso[21];
114 if (!strftime(gmt_iso, 21, "%Y-%m-%dT%H:%M:%SZ", gmt)) {
116 }
119 }
120 avio_printf(s->
pb,
" availabilityStartTime=\"%s\"\n", gmt_iso);
126 avio_printf(s->
pb,
" schemeIdUri=\"urn:mpeg:dash:utc:http-iso:2014\"\n");
128 }
129 }
130 return 0;
131 }
132
134 {
136 }
137
139 int i;
142 if (!gold) return 0;
146 if (!ts || strncmp(gold->
value, ts->
value, strlen(gold->
value)))
return 0;
147 }
148 return 1;
149 }
150
152 int i;
156 if (!gold_track_num) return 0;
161 if (!track_num ||
162 strncmp(gold_track_num->
value, track_num->
value, strlen(gold_track_num->
value)) ||
166 return 0;
167 }
168 }
169 return 1;
170 }
171
172 /*
173 * Writes a Representation within an Adaptation Set. Returns 0 on success and
174 * < 0 on failure.
175 */
177 int output_width, int output_height,
178 int output_sample_rate) {
185 const char *bandwidth_str;
186 if ((w->
is_live && (!filename)) ||
187 (!w->
is_live && (!irange || !cues_start || !cues_end || !filename || !bandwidth))) {
189 }
191 // if bandwidth for live was not provided, use a default
192 if (w->
is_live && !bandwidth) {
194 } else {
195 bandwidth_str = bandwidth->
value;
196 }
205 // For live streams, Codec and Mime Type always go in the Representation tag.
209 // For live streams, subsegments always start with key frames. So this
210 // is always 1.
213 } else {
221 }
223 return 0;
224 }
225
226 /*
227 * Checks if width of all streams are the same. Returns 1 if true, 0 otherwise.
228 */
230 int first_width, i;
235 return 0;
236 return 1;
237 }
238
239 /*
240 * Checks if height of all streams are the same. Returns 1 if true, 0 otherwise.
241 */
243 int first_height, i;
248 return 0;
249 return 1;
250 }
251
252 /*
253 * Checks if sample rate of all streams are the same. Returns 1 if true, 0 otherwise.
254 */
256 int first_sample_rate, i;
261 return 0;
262 return 1;
263 }
264
267 int i;
268 for (i = 0; i < w->
nb_as; i++) {
270 }
273 }
274
275 /*
276 * Parses a live header filename and computes the representation id,
277 * initialization pattern and the media pattern. Pass NULL if you don't want to
278 * compute any of those 3. Returns 0 on success and non-zero on failure.
279 *
280 * Name of the header file should conform to the following pattern:
281 * <file_description>_<representation_id>.hdr where <file_description> can be
282 * anything. The chunks should be named according to the following pattern:
283 * <file_description>_<representation_id>_<chunk_number>.chk
284 */
286 char **initialization_pattern, char **media_pattern) {
287 char *underscore_pos =
NULL;
288 char *period_pos =
NULL;
289 char *temp_pos =
NULL;
290 char *filename_str =
av_strdup(filename);
291 int ret = 0;
292
293 if (!filename_str) {
296 }
298 while (temp_pos) {
299 underscore_pos = temp_pos + 1;
301 }
302 if (!underscore_pos) {
305 }
307 if (!period_pos) {
310 }
311 *(underscore_pos - 1) = 0;
312 if (representation_id) {
313 *representation_id =
av_malloc(period_pos - underscore_pos + 1);
314 if (!(*representation_id)) {
317 }
318 av_strlcpy(*representation_id, underscore_pos, period_pos - underscore_pos + 1);
319 }
320 if (initialization_pattern) {
321 *initialization_pattern =
av_asprintf(
"%s_$RepresentationID$.hdr",
322 filename_str);
323 if (!(*initialization_pattern)) {
326 }
327 }
328 if (media_pattern) {
329 *media_pattern =
av_asprintf(
"%s_$RepresentationID$_$Number$.chk",
330 filename_str);
331 if (!(*media_pattern)) {
334 }
335 }
336
339 return ret;
340 }
341
342 /*
343 * Writes an Adaptation Set. Returns 0 on success and < 0 on failure.
344 */
346 {
351 int i;
352 static const char boolean[2][6] = { "false", "true" };
353 int subsegmentStartsWithSAP = 1;
354
355 // Width, Height and Sample Rate will go in the AdaptationSet tag if they
356 // are the same for all contained Representations. otherwise, they will go
357 // on their respective Representation tag. For live streams, they always go
358 // in the Representation tag.
359 int width_in_as = 1, height_in_as = 1, sample_rate_in_as = 1;
363 } else {
365 }
366
371
374
381
386
390 if (!w->
is_live && (!kf || !strncmp(kf->
value,
"0", 1))) subsegmentStartsWithSAP = 0;
391 }
392 avio_printf(s->
pb,
" subsegmentStartsWithSAP=\"%d\"", subsegmentStartsWithSAP);
394
398 char *initialization_pattern =
NULL;
399 char *media_pattern =
NULL;
401 &media_pattern);
402 if (ret) return ret;
403 avio_printf(s->
pb,
"<ContentComponent id=\"1\" type=\"%s\"/>\n",
410 avio_printf(s->
pb,
" initialization=\"%s\"", initialization_pattern);
412 av_free(initialization_pattern);
414 }
415
417 char *representation_id =
NULL;
418 int ret;
422 if (!filename)
425 return ret;
426 } else {
428 if (!representation_id)
return AVERROR(ENOMEM);
429 }
431 representation_id, !width_in_as,
432 !height_in_as, !sample_rate_in_as);
434 if (ret) return ret;
435 }
437 return 0;
438 }
439
441 {
442 int ret;
444 if (!q)
447 ret = atoi(q);
449 return ret;
450 }
451
453 {
456 char *q;
457 enum { new_set, parsed_id, parsing_streams }
state;
461 }
462 // syntax id=0,streams=0,1,2 id=1,streams=3,4 and so on
465 if (*p == ' ')
466 continue;
467 else if (
state == new_set && !strncmp(p,
"id=", 3)) {
475 p += 3; // consume "id="
477 while (*p != ',') *q++ = *p++;
478 *q = 0;
479 p++;
481 }
else if (
state == parsed_id && !strncmp(p,
"streams=", 8)) {
482 p += 8; // consume "streams="
483 state = parsing_streams;
484 }
else if (
state == parsing_streams) {
486 q = p;
487 while (*q != '0円' && *q != ',' && *q != ' ') q++;
496 }
497 if (*q == '0円') break;
498 if (*q ==
' ')
state = new_set;
499 p = ++q;
500 } else {
501 return -1;
502 }
503 }
504 return 0;
505 }
506
508 {
509 int i;
511 int ret;
514 if (ret < 0) {
516 }
518 if (ret < 0) {
520 }
525 }
527
528 for (i = 0; i < w->
nb_as; i++) {
530 if (ret < 0) {
532 }
533 }
534
539 return ret < 0 ? ret : 0;
540 }
541
543 {
545 }
546
548 {
550 return 0;
551 }
552
553 #define OFFSET(x) offsetof(WebMDashMuxContext, x)
555 {
"adaptation_sets",
"Adaptation sets. Syntax: id=0,streams=0,1,2 id=1,streams=3,4 and so on",
OFFSET(adaptation_sets),
AV_OPT_TYPE_STRING, { 0 }, 0, 0,
AV_OPT_FLAG_ENCODING_PARAM },
561 {
"time_shift_buffer_depth",
"Smallest time (in seconds) shifting buffer for which any Representation is guaranteed to be available.",
OFFSET(time_shift_buffer_depth),
AV_OPT_TYPE_DOUBLE, { .dbl = 60.0 }, 1.0, DBL_MAX,
AV_OPT_FLAG_ENCODING_PARAM },
564 };
565
566 #if CONFIG_WEBM_DASH_MANIFEST_MUXER
572 };
573
575 .
name =
"webm_dash_manifest",
577 .mime_type = "application/xml",
578 .extensions = "xml",
583 .priv_class = &webm_dash_class,
584 };
585 #endif
static void write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int unqueue)
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
void * av_realloc(void *ptr, size_t size)
Allocate, reallocate, or free a block of memory.
#define LIBAVUTIL_VERSION_INT
#define INITIALIZATION_RANGE
char * av_stristr(const char *s1, const char *s2)
Locate the first case-independent occurrence in the string haystack of the string needle...
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
static int subsegment_alignment(AVFormatContext *s, AdaptationSet *as)
const char * av_default_item_name(void *ptr)
Return the context name.
This struct describes the properties of an encoded stream.
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
static av_cold int end(AVCodecContext *avctx)
AVStream ** streams
A list of all streams in the file.
AVDictionaryEntry * av_dict_get(const AVDictionary *m, const char *key, const AVDictionaryEntry *prev, int flags)
Get a dictionary entry with matching key.
#define AVERROR_EOF
End of file.
#define AV_OPT_FLAG_ENCODING_PARAM
a generic parameter which can be set by the user for muxing or encoding
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
static const AVOption options[]
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
static int webm_dash_manifest_write_packet(AVFormatContext *s, AVPacket *pkt)
enum AVMediaType codec_type
General type of the encoded data.
size_t av_strlcpy(char *dst, const char *src, size_t size)
Copy the string src to dst, but no more than size - 1 bytes, and null-terminate dst.
static int check_matching_sample_rate(AVFormatContext *s, AdaptationSet *as)
static struct tm * gmtime_r(const time_t *clock, struct tm *result)
int extradata_size
Size of the extradata content in bytes.
static int webm_dash_manifest_write_header(AVFormatContext *s)
char * av_asprintf(const char *fmt,...)
unsigned int nb_streams
Number of elements in AVFormatContext.streams.
static int write_header(AVFormatContext *s)
static int webm_dash_manifest_write_trailer(AVFormatContext *s)
double time_shift_buffer_depth
static void write_footer(AVFormatContext *s)
static int write_trailer(AVFormatContext *s1)
static double get_duration(AVFormatContext *s)
static const char * get_codec_name(int codec_id)
static int to_integer(char *p, int len)
char * av_strdup(const char *s)
Duplicate a string.
AVIOContext * pb
I/O context.
static const AVClass webm_dash_class
Describe the class of an AVClass context structure.
static int parse_filename(char *filename, char **representation_id, char **initialization_pattern, char **media_pattern)
static int check_matching_height(AVFormatContext *s, AdaptationSet *as)
static void free_adaptation_sets(AVFormatContext *s)
int sample_rate
Audio only.
#define AVERROR_UNKNOWN
Unknown error, typically from an external library.
void * priv_data
Format private data.
static int write_adaptation_set(AVFormatContext *s, int as_index)
uint8_t * extradata
Extra binary data needed for initializing the decoder, codec-dependent.
static int bitstream_switching(AVFormatContext *s, AdaptationSet *as)
AVCodecParameters * codecpar
Codec parameters associated with this stream.
static int parse_adaptation_sets(AVFormatContext *s)
static int check_matching_width(AVFormatContext *s, AdaptationSet *as)
This structure stores compressed data.
static int write_representation(AVFormatContext *s, AVStream *stream, char *id, int output_width, int output_height, int output_sample_rate)
int avio_printf(AVIOContext *s, const char *fmt,...) av_printf_format(2
int minimum_update_period