mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
lavf/webm_dash: some fields should go into Representation
Width, Height and Sample Rate should be in the AdaptationSet tag only if all the contained representations have the same width, height and sampling rate. Otherwise they should go into the Representation tag. This patch adds this functionality and a fate test for the same. Signed-off-by: Vignesh Venkatasubramanian <vigneshv@google.com> Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
This commit is contained in:
parent
0d92b0d5f4
commit
a9b10e1510
@ -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, "<Representation id=\"%d\"", id);
|
||||
avio_printf(s->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, "<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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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, "<AdaptationSet id=\"%s\"", as->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, "<Representation id=\"%d\"", i);
|
||||
avio_printf(s->pb, " bandwidth=\"%s\"", bandwidth->value);
|
||||
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");
|
||||
write_representation(s, s->streams[as->streams[i]], i,
|
||||
!width_in_as, !height_in_as, !sample_rate_in_as);
|
||||
}
|
||||
avio_printf(s->pb, "</AdaptationSet>\n");
|
||||
return 0;
|
||||
|
@ -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)
|
||||
|
||||
|
30
tests/ref/fate/webm-dash-manifest-representations
Normal file
30
tests/ref/fate/webm-dash-manifest-representations
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<MPD
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="urn:mpeg:DASH:schema:MPD:2011"
|
||||
xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011"
|
||||
type="static"
|
||||
mediaPresentationDuration="PT32.48S"
|
||||
minBufferTime="PT1S"
|
||||
profiles="urn:webm:dash:profile:webm-on-demand:2012">
|
||||
<Period id="0" start="PT0S" duration="PT32.48S" >
|
||||
<AdaptationSet id="0" mimeType="video/webm" codecs="vp8" lang="eng" bitstreamSwitching="true" subsegmentAlignment="false" subsegmentStartsWithSAP="1">
|
||||
<Representation id="0" bandwidth="302355" width="640" height="360">
|
||||
<BaseURL>dash_video1.webm</BaseURL>
|
||||
<SegmentBase
|
||||
indexRange="1115974-1116097">
|
||||
<Initialization
|
||||
range="0-441" />
|
||||
</SegmentBase>
|
||||
</Representation>
|
||||
<Representation id="1" bandwidth="243938" width="320" height="240">
|
||||
<BaseURL>dash_video4.webm</BaseURL>
|
||||
<SegmentBase
|
||||
indexRange="871124-871645">
|
||||
<Initialization
|
||||
range="0-437" />
|
||||
</SegmentBase>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
</Period>
|
||||
</MPD>
|
Loading…
Reference in New Issue
Block a user