diff --git a/libavformat/webmdashenc.c b/libavformat/webmdashenc.c index 849b241e52..768b5d2383 100644 --- a/libavformat/webmdashenc.c +++ b/libavformat/webmdashenc.c @@ -132,6 +132,80 @@ static int bitstream_switching(AVFormatContext *s, AdaptationSet *as) { return 1; } +/* + * 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, + int output_width, int output_height, + int output_sample_rate) { + 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) { + return -1; + } + avio_printf(s->pb, "pb, " bandwidth=\"%s\"", 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, "%s\n", filename->value); + avio_printf(s->pb, "pb, " indexRange=\"%s-%s\">\n", cues_start->value, cues_end->value); + avio_printf(s->pb, "pb, " range=\"0-%s\" />\n", irange->value); + avio_printf(s->pb, "\n"); + avio_printf(s->pb, "\n"); + return 0; +} + +/* + * Checks if width of all streams are the same. Returns 1 if true, 0 otherwise. + */ +static int check_matching_width(AVFormatContext *s, AdaptationSet *as) { + int first_width, i; + if (as->nb_streams < 2) return 1; + first_width = s->streams[as->streams[0]]->codec->width; + for (i = 1; i < as->nb_streams; i++) + if (first_width != s->streams[as->streams[i]]->codec->width) + return 0; + return 1; +} + +/* + * Checks if height of all streams are the same. Returns 1 if true, 0 otherwise. + */ +static int check_matching_height(AVFormatContext *s, AdaptationSet *as) { + int first_height, i; + if (as->nb_streams < 2) return 1; + first_height = s->streams[as->streams[0]]->codec->height; + for (i = 1; i < as->nb_streams; i++) + if (first_height != s->streams[as->streams[i]]->codec->height) + return 0; + return 1; +} + +/* + * Checks if sample rate of all streams are the same. Returns 1 if true, 0 otherwise. + */ +static int check_matching_sample_rate(AVFormatContext *s, AdaptationSet *as) { + int first_sample_rate, i; + if (as->nb_streams < 2) return 1; + first_sample_rate = s->streams[as->streams[0]]->codec->sample_rate; + for (i = 1; i < as->nb_streams; i++) + if (first_sample_rate != s->streams[as->streams[i]]->codec->sample_rate) + return 0; + return 1; +} + /* * Writes an Adaptation Set. Returns 0 on success and < 0 on failure. */ @@ -140,10 +214,22 @@ static int write_adaptation_set(AVFormatContext *s, int as_index) WebMDashMuxContext *w = s->priv_data; AdaptationSet *as = &w->as[as_index]; AVCodecContext *codec = s->streams[as->streams[0]]->codec; + AVDictionaryEntry *lang; int i; static const char boolean[2][6] = { "false", "true" }; int subsegmentStartsWithSAP = 1; - AVDictionaryEntry *lang; + + // Width, Height and Sample Rate will go in the AdaptationSet tag if they + // are the same for all contained Representations. otherwise, they will go + // on their respective Representation tag. + int width_in_as = 1, height_in_as = 1, sample_rate_in_as = 1; + if (codec->codec_type == AVMEDIA_TYPE_VIDEO) { + width_in_as = check_matching_width(s, as); + height_in_as = check_matching_height(s, as); + } else { + sample_rate_in_as = check_matching_sample_rate(s, as); + } + avio_printf(s->pb, "id); avio_printf(s->pb, " mimeType=\"%s/webm\"", codec->codec_type == AVMEDIA_TYPE_VIDEO ? "video" : "audio"); @@ -152,12 +238,12 @@ static int write_adaptation_set(AVFormatContext *s, int as_index) lang = av_dict_get(s->streams[as->streams[0]]->metadata, "language", NULL, 0); if (lang) avio_printf(s->pb, " lang=\"%s\"", lang->value); - if (codec->codec_type == AVMEDIA_TYPE_VIDEO) { + if (codec->codec_type == AVMEDIA_TYPE_VIDEO && width_in_as) avio_printf(s->pb, " width=\"%d\"", codec->width); + if (codec->codec_type == AVMEDIA_TYPE_VIDEO && height_in_as) avio_printf(s->pb, " height=\"%d\"", codec->height); - } else { + if (codec->codec_type == AVMEDIA_TYPE_AUDIO && sample_rate_in_as) avio_printf(s->pb, " audioSamplingRate=\"%d\"", codec->sample_rate); - } avio_printf(s->pb, " bitstreamSwitching=\"%s\"", boolean[bitstream_switching(s, as)]); @@ -173,26 +259,8 @@ static int write_adaptation_set(AVFormatContext *s, int as_index) avio_printf(s->pb, ">\n"); for (i = 0; i < as->nb_streams; i++) { - AVStream *stream = s->streams[as->streams[i]]; - 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) { - return -1; - } - avio_printf(s->pb, "pb, " bandwidth=\"%s\"", bandwidth->value); - avio_printf(s->pb, ">\n"); - avio_printf(s->pb, "%s\n", filename->value); - avio_printf(s->pb, "pb, " indexRange=\"%s-%s\">\n", cues_start->value, cues_end->value); - avio_printf(s->pb, "pb, " range=\"0-%s\" />\n", irange->value); - avio_printf(s->pb, "\n"); - avio_printf(s->pb, "\n"); + write_representation(s, s->streams[as->streams[i]], i, + !width_in_as, !height_in_as, !sample_rate_in_as); } avio_printf(s->pb, "\n"); return 0; diff --git a/tests/fate/vpx.mak b/tests/fate/vpx.mak index 0f84ef5ac3..be3e95672b 100644 --- a/tests/fate/vpx.mak +++ b/tests/fate/vpx.mak @@ -37,6 +37,9 @@ fate-webm-dash-manifest-unaligned-video-streams: CMD = run ffmpeg -f webm_dash_m FATE_VP8-$(call DEMDEC, WEBM_DASH_MANIFEST, VP8) += fate-webm-dash-manifest-unaligned-audio-streams fate-webm-dash-manifest-unaligned-audio-streams: CMD = run ffmpeg -f webm_dash_manifest -i $(TARGET_SAMPLES)/vp8/dash_audio1.webm -f webm_dash_manifest -i $(TARGET_SAMPLES)/vp8/dash_audio3.webm -c copy -map 0 -map 1 -f webm_dash_manifest -adaptation_sets "id=0,streams=0,1" - +FATE_VP8-$(call DEMDEC, WEBM_DASH_MANIFEST, VP8) += fate-webm-dash-manifest-representations +fate-webm-dash-manifest-representations: CMD = run ffmpeg -f webm_dash_manifest -i $(TARGET_SAMPLES)/vp8/dash_video1.webm -f webm_dash_manifest -i $(TARGET_SAMPLES)/vp8/dash_video4.webm -c copy -map 0 -map 1 -f webm_dash_manifest -adaptation_sets "id=0,streams=0,1" - + FATE_SAMPLES_AVCONV += $(FATE_VP6-yes) fate-vp6: $(FATE_VP6-yes) diff --git a/tests/ref/fate/webm-dash-manifest-representations b/tests/ref/fate/webm-dash-manifest-representations new file mode 100644 index 0000000000..8556ecebee --- /dev/null +++ b/tests/ref/fate/webm-dash-manifest-representations @@ -0,0 +1,30 @@ + + + + + +dash_video1.webm + + + + + +dash_video4.webm + + + + + + +