1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/formats/webm/tracks_builder.h"
6
7 #include "base/logging.h"
8 #include "media/formats/webm/webm_constants.h"
9
10 namespace media {
11
12 // Returns size of an integer, formatted using Matroska serialization.
GetUIntMkvSize(uint64 value)13 static int GetUIntMkvSize(uint64 value) {
14 if (value < 0x07FULL)
15 return 1;
16 if (value < 0x03FFFULL)
17 return 2;
18 if (value < 0x01FFFFFULL)
19 return 3;
20 if (value < 0x0FFFFFFFULL)
21 return 4;
22 if (value < 0x07FFFFFFFFULL)
23 return 5;
24 if (value < 0x03FFFFFFFFFFULL)
25 return 6;
26 if (value < 0x01FFFFFFFFFFFFULL)
27 return 7;
28 return 8;
29 }
30
31 // Returns the minimium size required to serialize an integer value.
GetUIntSize(uint64 value)32 static int GetUIntSize(uint64 value) {
33 if (value < 0x0100ULL)
34 return 1;
35 if (value < 0x010000ULL)
36 return 2;
37 if (value < 0x01000000ULL)
38 return 3;
39 if (value < 0x0100000000ULL)
40 return 4;
41 if (value < 0x010000000000ULL)
42 return 5;
43 if (value < 0x01000000000000ULL)
44 return 6;
45 if (value < 0x0100000000000000ULL)
46 return 7;
47 return 8;
48 }
49
MasterElementSize(int element_id,int payload_size)50 static int MasterElementSize(int element_id, int payload_size) {
51 return GetUIntSize(element_id) + GetUIntMkvSize(payload_size) + payload_size;
52 }
53
IntElementSize(int element_id,int value)54 static int IntElementSize(int element_id, int value) {
55 return GetUIntSize(element_id) + 1 + GetUIntSize(value);
56 }
57
DoubleElementSize(int element_id)58 static int DoubleElementSize(int element_id) {
59 return GetUIntSize(element_id) + 1 + 8;
60 }
61
StringElementSize(int element_id,const std::string & value)62 static int StringElementSize(int element_id, const std::string& value) {
63 return GetUIntSize(element_id) +
64 GetUIntMkvSize(value.length()) +
65 value.length();
66 }
67
SerializeInt(uint8 ** buf_ptr,int * buf_size_ptr,int64 value,int size)68 static void SerializeInt(uint8** buf_ptr, int* buf_size_ptr,
69 int64 value, int size) {
70 uint8*& buf = *buf_ptr;
71 int& buf_size = *buf_size_ptr;
72
73 for (int idx = 1; idx <= size; ++idx) {
74 *buf++ = static_cast<uint8>(value >> ((size - idx) * 8));
75 --buf_size;
76 }
77 }
78
SerializeDouble(uint8 ** buf_ptr,int * buf_size_ptr,double value)79 static void SerializeDouble(uint8** buf_ptr, int* buf_size_ptr,
80 double value) {
81 // Use a union to convert |value| to native endian integer bit pattern.
82 union {
83 double src;
84 int64 dst;
85 } tmp;
86 tmp.src = value;
87
88 // Write the bytes from native endian |tmp.dst| to big-endian form in |buf|.
89 SerializeInt(buf_ptr, buf_size_ptr, tmp.dst, 8);
90 }
91
WriteElementId(uint8 ** buf,int * buf_size,int element_id)92 static void WriteElementId(uint8** buf, int* buf_size, int element_id) {
93 SerializeInt(buf, buf_size, element_id, GetUIntSize(element_id));
94 }
95
WriteUInt(uint8 ** buf,int * buf_size,uint64 value)96 static void WriteUInt(uint8** buf, int* buf_size, uint64 value) {
97 const int size = GetUIntMkvSize(value);
98 value |= (1ULL << (size * 7)); // Matroska formatting
99 SerializeInt(buf, buf_size, value, size);
100 }
101
WriteMasterElement(uint8 ** buf,int * buf_size,int element_id,int payload_size)102 static void WriteMasterElement(uint8** buf, int* buf_size,
103 int element_id, int payload_size) {
104 WriteElementId(buf, buf_size, element_id);
105 WriteUInt(buf, buf_size, payload_size);
106 }
107
WriteIntElement(uint8 ** buf,int * buf_size,int element_id,int value)108 static void WriteIntElement(uint8** buf, int* buf_size,
109 int element_id, int value) {
110 WriteElementId(buf, buf_size, element_id);
111
112 const int size = GetUIntSize(value);
113 WriteUInt(buf, buf_size, size);
114
115 SerializeInt(buf, buf_size, value, size);
116 }
117
WriteDoubleElement(uint8 ** buf,int * buf_size,int element_id,double value)118 static void WriteDoubleElement(uint8** buf, int* buf_size,
119 int element_id, double value) {
120 WriteElementId(buf, buf_size, element_id);
121 WriteUInt(buf, buf_size, 8);
122 SerializeDouble(buf, buf_size, value);
123 }
124
WriteStringElement(uint8 ** buf_ptr,int * buf_size_ptr,int element_id,const std::string & value)125 static void WriteStringElement(uint8** buf_ptr, int* buf_size_ptr,
126 int element_id, const std::string& value) {
127 uint8*& buf = *buf_ptr;
128 int& buf_size = *buf_size_ptr;
129
130 WriteElementId(&buf, &buf_size, element_id);
131
132 const uint64 size = value.length();
133 WriteUInt(&buf, &buf_size, size);
134
135 memcpy(buf, value.data(), size);
136 buf += size;
137 buf_size -= size;
138 }
139
TracksBuilder(bool allow_invalid_values)140 TracksBuilder::TracksBuilder(bool allow_invalid_values)
141 : allow_invalid_values_(allow_invalid_values) {}
TracksBuilder()142 TracksBuilder::TracksBuilder()
143 : allow_invalid_values_(false) {}
~TracksBuilder()144 TracksBuilder::~TracksBuilder() {}
145
AddVideoTrack(int track_num,int track_uid,const std::string & codec_id,const std::string & name,const std::string & language,int default_duration,int video_pixel_width,int video_pixel_height)146 void TracksBuilder::AddVideoTrack(
147 int track_num,
148 int track_uid,
149 const std::string& codec_id,
150 const std::string& name,
151 const std::string& language,
152 int default_duration,
153 int video_pixel_width,
154 int video_pixel_height) {
155 AddTrackInternal(track_num, kWebMTrackTypeVideo, track_uid, codec_id, name,
156 language, default_duration, video_pixel_width,
157 video_pixel_height, -1, -1);
158 }
159
AddAudioTrack(int track_num,int track_uid,const std::string & codec_id,const std::string & name,const std::string & language,int default_duration,int audio_channels,double audio_sampling_frequency)160 void TracksBuilder::AddAudioTrack(
161 int track_num,
162 int track_uid,
163 const std::string& codec_id,
164 const std::string& name,
165 const std::string& language,
166 int default_duration,
167 int audio_channels,
168 double audio_sampling_frequency) {
169 AddTrackInternal(track_num, kWebMTrackTypeAudio, track_uid, codec_id, name,
170 language, default_duration, -1, -1, audio_channels,
171 audio_sampling_frequency);
172 }
173
AddTextTrack(int track_num,int track_uid,const std::string & codec_id,const std::string & name,const std::string & language)174 void TracksBuilder::AddTextTrack(
175 int track_num,
176 int track_uid,
177 const std::string& codec_id,
178 const std::string& name,
179 const std::string& language) {
180 AddTrackInternal(track_num, kWebMTrackTypeSubtitlesOrCaptions, track_uid,
181 codec_id, name, language, -1, -1, -1, -1, -1);
182 }
183
Finish()184 std::vector<uint8> TracksBuilder::Finish() {
185 // Allocate the storage
186 std::vector<uint8> buffer;
187 buffer.resize(GetTracksSize());
188
189 // Populate the storage with a tracks header
190 WriteTracks(&buffer[0], buffer.size());
191
192 return buffer;
193 }
194
AddTrackInternal(int track_num,int track_type,int track_uid,const std::string & codec_id,const std::string & name,const std::string & language,int default_duration,int video_pixel_width,int video_pixel_height,int audio_channels,double audio_sampling_frequency)195 void TracksBuilder::AddTrackInternal(
196 int track_num,
197 int track_type,
198 int track_uid,
199 const std::string& codec_id,
200 const std::string& name,
201 const std::string& language,
202 int default_duration,
203 int video_pixel_width,
204 int video_pixel_height,
205 int audio_channels,
206 double audio_sampling_frequency) {
207 tracks_.push_back(Track(track_num, track_type, track_uid, codec_id, name,
208 language, default_duration, video_pixel_width,
209 video_pixel_height, audio_channels,
210 audio_sampling_frequency, allow_invalid_values_));
211 }
212
GetTracksSize() const213 int TracksBuilder::GetTracksSize() const {
214 return MasterElementSize(kWebMIdTracks, GetTracksPayloadSize());
215 }
216
GetTracksPayloadSize() const217 int TracksBuilder::GetTracksPayloadSize() const {
218 int payload_size = 0;
219
220 for (TrackList::const_iterator itr = tracks_.begin();
221 itr != tracks_.end(); ++itr) {
222 payload_size += itr->GetSize();
223 }
224
225 return payload_size;
226 }
227
WriteTracks(uint8 * buf,int buf_size) const228 void TracksBuilder::WriteTracks(uint8* buf, int buf_size) const {
229 WriteMasterElement(&buf, &buf_size, kWebMIdTracks, GetTracksPayloadSize());
230
231 for (TrackList::const_iterator itr = tracks_.begin();
232 itr != tracks_.end(); ++itr) {
233 itr->Write(&buf, &buf_size);
234 }
235 }
236
Track(int track_num,int track_type,int track_uid,const std::string & codec_id,const std::string & name,const std::string & language,int default_duration,int video_pixel_width,int video_pixel_height,int audio_channels,double audio_sampling_frequency,bool allow_invalid_values)237 TracksBuilder::Track::Track(int track_num, int track_type, int track_uid,
238 const std::string& codec_id,
239 const std::string& name,
240 const std::string& language,
241 int default_duration,
242 int video_pixel_width, int video_pixel_height,
243 int audio_channels, double audio_sampling_frequency,
244 bool allow_invalid_values)
245 : track_num_(track_num),
246 track_type_(track_type),
247 track_uid_(track_uid),
248 codec_id_(codec_id),
249 name_(name),
250 language_(language),
251 default_duration_(default_duration),
252 video_pixel_width_(video_pixel_width),
253 video_pixel_height_(video_pixel_height),
254 audio_channels_(audio_channels),
255 audio_sampling_frequency_(audio_sampling_frequency) {
256 if (!allow_invalid_values) {
257 CHECK_GT(track_num_, 0);
258 CHECK_GT(track_type_, 0);
259 CHECK_LT(track_type_, 255);
260 CHECK_GT(track_uid_, 0);
261 if (track_type != kWebMTrackTypeVideo &&
262 track_type != kWebMTrackTypeAudio) {
263 CHECK_EQ(default_duration_, -1);
264 } else {
265 CHECK(default_duration_ == -1 || default_duration_ > 0);
266 }
267
268 if (track_type == kWebMTrackTypeVideo) {
269 CHECK_GT(video_pixel_width_, 0);
270 CHECK_GT(video_pixel_height_, 0);
271 } else {
272 CHECK_EQ(video_pixel_width_, -1);
273 CHECK_EQ(video_pixel_height_, -1);
274 }
275
276 if (track_type == kWebMTrackTypeAudio) {
277 CHECK_GT(audio_channels_, 0);
278 CHECK_GT(audio_sampling_frequency_, 0.0);
279 } else {
280 CHECK_EQ(audio_channels_, -1);
281 CHECK_EQ(audio_sampling_frequency_, -1.0);
282 }
283 }
284 }
285
GetSize() const286 int TracksBuilder::Track::GetSize() const {
287 return MasterElementSize(kWebMIdTrackEntry, GetPayloadSize());
288 }
289
GetVideoPayloadSize() const290 int TracksBuilder::Track::GetVideoPayloadSize() const {
291 int payload_size = 0;
292
293 if (video_pixel_width_ >= 0)
294 payload_size += IntElementSize(kWebMIdPixelWidth, video_pixel_width_);
295 if (video_pixel_height_ >= 0)
296 payload_size += IntElementSize(kWebMIdPixelHeight, video_pixel_height_);
297
298 return payload_size;
299 }
300
GetAudioPayloadSize() const301 int TracksBuilder::Track::GetAudioPayloadSize() const {
302 int payload_size = 0;
303
304 if (audio_channels_ >= 0)
305 payload_size += IntElementSize(kWebMIdChannels, audio_channels_);
306 if (audio_sampling_frequency_ >= 0)
307 payload_size += DoubleElementSize(kWebMIdSamplingFrequency);
308
309 return payload_size;
310 }
311
GetPayloadSize() const312 int TracksBuilder::Track::GetPayloadSize() const {
313 int size = 0;
314
315 size += IntElementSize(kWebMIdTrackNumber, track_num_);
316 size += IntElementSize(kWebMIdTrackType, track_type_);
317 size += IntElementSize(kWebMIdTrackUID, track_uid_);
318
319 if (default_duration_ >= 0)
320 size += IntElementSize(kWebMIdDefaultDuration, default_duration_);
321
322 if (!codec_id_.empty())
323 size += StringElementSize(kWebMIdCodecID, codec_id_);
324
325 if (!name_.empty())
326 size += StringElementSize(kWebMIdName, name_);
327
328 if (!language_.empty())
329 size += StringElementSize(kWebMIdLanguage, language_);
330
331 if (GetVideoPayloadSize() > 0) {
332 size += MasterElementSize(kWebMIdVideo, GetVideoPayloadSize());
333 }
334
335 if (GetAudioPayloadSize() > 0) {
336 size += MasterElementSize(kWebMIdAudio, GetAudioPayloadSize());
337 }
338
339 return size;
340 }
341
Write(uint8 ** buf,int * buf_size) const342 void TracksBuilder::Track::Write(uint8** buf, int* buf_size) const {
343 WriteMasterElement(buf, buf_size, kWebMIdTrackEntry, GetPayloadSize());
344
345 WriteIntElement(buf, buf_size, kWebMIdTrackNumber, track_num_);
346 WriteIntElement(buf, buf_size, kWebMIdTrackType, track_type_);
347 WriteIntElement(buf, buf_size, kWebMIdTrackUID, track_uid_);
348
349 if (default_duration_ >= 0)
350 WriteIntElement(buf, buf_size, kWebMIdDefaultDuration, default_duration_);
351
352 if (!codec_id_.empty())
353 WriteStringElement(buf, buf_size, kWebMIdCodecID, codec_id_);
354
355 if (!name_.empty())
356 WriteStringElement(buf, buf_size, kWebMIdName, name_);
357
358 if (!language_.empty())
359 WriteStringElement(buf, buf_size, kWebMIdLanguage, language_);
360
361 if (GetVideoPayloadSize() > 0) {
362 WriteMasterElement(buf, buf_size, kWebMIdVideo, GetVideoPayloadSize());
363
364 if (video_pixel_width_ >= 0)
365 WriteIntElement(buf, buf_size, kWebMIdPixelWidth, video_pixel_width_);
366
367 if (video_pixel_height_ >= 0)
368 WriteIntElement(buf, buf_size, kWebMIdPixelHeight, video_pixel_height_);
369 }
370
371 if (GetAudioPayloadSize() > 0) {
372 WriteMasterElement(buf, buf_size, kWebMIdAudio, GetAudioPayloadSize());
373
374 if (audio_channels_ >= 0)
375 WriteIntElement(buf, buf_size, kWebMIdChannels, audio_channels_);
376
377 if (audio_sampling_frequency_ >= 0) {
378 WriteDoubleElement(buf, buf_size, kWebMIdSamplingFrequency,
379 audio_sampling_frequency_);
380 }
381 }
382 }
383
384 } // namespace media
385