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