On Tue, Mar 31, 2015 at 04:51:58PM -0700, Vignesh Venkatasubramanian wrote: > This patch adds support for creating DASH manifests for WebM Live > Streams. It also updates the documentation and adds a fate test to > verify the behavior of the new muxer flag. > > Signed-off-by: Vignesh Venkatasubramanian <vigne...@google.com>
[...] > @@ -79,19 +89,42 @@ static double get_duration(AVFormatContext *s) > > static void write_header(AVFormatContext *s) > { > + WebMDashMuxContext *w = s->priv_data; > double min_buffer_time = 1.0; > + time_t local_time; > + struct tm* gmt; > + char* gmt_iso = av_malloc(21); > avio_printf(s->pb, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); > avio_printf(s->pb, "<MPD\n"); > avio_printf(s->pb, " > xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"); > avio_printf(s->pb, " xmlns=\"urn:mpeg:DASH:schema:MPD:2011\"\n"); > avio_printf(s->pb, " > xsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011\"\n"); > - avio_printf(s->pb, " type=\"static\"\n"); > - avio_printf(s->pb, " mediaPresentationDuration=\"PT%gS\"\n", > - get_duration(s)); > - avio_printf(s->pb, " minBufferTime=\"PT%gS\"\n", > - min_buffer_time); > - avio_printf(s->pb, " > profiles=\"urn:webm:dash:profile:webm-on-demand:2012\""); > - avio_printf(s->pb, ">\n"); > + avio_printf(s->pb, " type=\"%s\"\n", w->is_live ? "dynamic" : "static"); > + if (!w->is_live) { > + avio_printf(s->pb, " mediaPresentationDuration=\"PT%gS\"\n", > + get_duration(s)); > + } > + avio_printf(s->pb, " minBufferTime=\"PT%gS\"\n", min_buffer_time); > + avio_printf(s->pb, " profiles=\"%s\"%s", > + w->is_live ? "urn:mpeg:dash:profile:isoff-live:2011" : > "urn:webm:dash:profile:webm-on-demand:2012", > + w->is_live ? "\n" : ">\n"); > + time(&local_time); > + gmt = gmtime(&local_time); this should be using gmtime_r() please see libavutil/time_internal.h: > + strftime(gmt_iso, 21, "%FT%TZ", gmt); > + if (w->debug_mode) { > + av_strlcpy(gmt_iso, "", 1); > + } > + if (w->is_live) { > + avio_printf(s->pb, " availabilityStartTime=\"%s\"\n", gmt_iso); > + avio_printf(s->pb, " timeShiftBufferDepth=\"PT%gS\"", > w->time_shift_buffer_depth); > + avio_printf(s->pb, ">\n"); > + avio_printf(s->pb, "<UTCTiming\n"); > + avio_printf(s->pb, " schemeIdUri=\"%s\"\n", > + w->utc_timing_url ? "urn:mpeg:dash:utc:http-iso:2014" : > "urn:mpeg:dash:utc:direct:2012"); > + avio_printf(s->pb, " value=\"%s\"/>\n", > + w->utc_timing_url ? w->utc_timing_url : gmt_iso); > + } > + av_free(gmt_iso); > } > > static void write_footer(AVFormatContext *s) > @@ -137,33 +170,47 @@ static int bitstream_switching(AVFormatContext *s, > AdaptationSet *as) { > * Writes a Representation within an Adaptation Set. Returns 0 on success and > * < 0 on failure. > */ > -static int write_representation(AVFormatContext *s, AVStream *stream, int id, > +static int write_representation(AVFormatContext *s, AVStream *stream, char > *id, > int output_width, int output_height, > int output_sample_rate) { > + WebMDashMuxContext *w = s->priv_data; > AVDictionaryEntry *irange = av_dict_get(stream->metadata, > INITIALIZATION_RANGE, NULL, 0); > AVDictionaryEntry *cues_start = av_dict_get(stream->metadata, > CUES_START, NULL, 0); > AVDictionaryEntry *cues_end = av_dict_get(stream->metadata, CUES_END, > NULL, 0); > AVDictionaryEntry *filename = av_dict_get(stream->metadata, FILENAME, > NULL, 0); > AVDictionaryEntry *bandwidth = av_dict_get(stream->metadata, BANDWIDTH, > NULL, 0); > - if (!irange || cues_start == NULL || cues_end == NULL || filename == > NULL || > - !bandwidth) { > + if ((w->is_live && (!filename)) || > + (!w->is_live && (!irange || !cues_start || !cues_end || !filename || > !bandwidth))) { > return -1; > } > - avio_printf(s->pb, "<Representation id=\"%d\"", id); > - avio_printf(s->pb, " bandwidth=\"%s\"", bandwidth->value); > + avio_printf(s->pb, "<Representation id=\"%s\"", id); > + // FIXME: For live, This should be obtained from the input file or as an > AVOption. > + avio_printf(s->pb, " bandwidth=\"%s\"", > + w->is_live ? (stream->codec->codec_type == > AVMEDIA_TYPE_AUDIO ? "128000" : "1000000") : bandwidth->value); > if (stream->codec->codec_type == AVMEDIA_TYPE_VIDEO && output_width) > avio_printf(s->pb, " width=\"%d\"", stream->codec->width); > if (stream->codec->codec_type == AVMEDIA_TYPE_VIDEO && output_height) > avio_printf(s->pb, " height=\"%d\"", stream->codec->height); > if (stream->codec->codec_type = AVMEDIA_TYPE_AUDIO && output_sample_rate) > avio_printf(s->pb, " audioSamplingRate=\"%d\"", > stream->codec->sample_rate); > - avio_printf(s->pb, ">\n"); > - avio_printf(s->pb, "<BaseURL>%s</BaseURL>\n", filename->value); > - avio_printf(s->pb, "<SegmentBase\n"); > - avio_printf(s->pb, " indexRange=\"%s-%s\">\n", cues_start->value, > cues_end->value); > - avio_printf(s->pb, "<Initialization\n"); > - avio_printf(s->pb, " range=\"0-%s\" />\n", irange->value); > - avio_printf(s->pb, "</SegmentBase>\n"); > + if (w->is_live) { > + // For live streams, Codec and Mime Type always go in the > Representation tag. > + avio_printf(s->pb, " codecs=\"%s\"", > get_codec_name(stream->codec->codec_id)); > + avio_printf(s->pb, " mimeType=\"%s/webm\"", > + stream->codec->codec_type == AVMEDIA_TYPE_VIDEO ? > "video" : "audio"); > + // For live streams, subsegments always start with key frames. So > this > + // is always 1. > + avio_printf(s->pb, " startsWithSAP=\"1\""); > + avio_printf(s->pb, ">"); > + } else { > + avio_printf(s->pb, ">\n"); > + avio_printf(s->pb, "<BaseURL>%s</BaseURL>\n", filename->value); > + avio_printf(s->pb, "<SegmentBase\n"); > + avio_printf(s->pb, " indexRange=\"%s-%s\">\n", cues_start->value, > cues_end->value); > + avio_printf(s->pb, "<Initialization\n"); > + avio_printf(s->pb, " range=\"0-%s\" />\n", irange->value); > + avio_printf(s->pb, "</SegmentBase>\n"); > + } > avio_printf(s->pb, "</Representation>\n"); > return 0; > } > @@ -208,6 +255,53 @@ static int check_matching_sample_rate(AVFormatContext > *s, AdaptationSet *as) { > } > > /* > + * Parses a live header filename and computes the representation id, > + * initialization pattern and the media pattern. Pass NULL if you don't want > to > + * compute any of those 3. Returns 0 on success and non-zero on failure. > + * > + * Name of the header file should conform to the following pattern: > + * <file_description>_<representation_id>.hdr where <file_description> can be > + * anything. The chunks should be named according to the following pattern: > + * <file_description>_<representation_id>_<chunk_number>.chk > + */ > +static int parse_filename(char *filename, char **representation_id, > + char **initialization_pattern, char > **media_pattern) { > + char *filename_str; > + char *underscore_pos = NULL; > + char *period_pos = NULL; > + char *temp_pos = NULL; > + filename_str = av_mallocz(strlen(filename) + 1); > + if (!filename_str) return AVERROR(ENOMEM); > + strncpy(filename_str, filename, strlen(filename)); av_strdup [...] -- Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB If you think the mosad wants you dead since a long time then you are either wrong or dead since a long time.
signature.asc
Description: Digital signature
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel