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()
156 : allow_invalid_values_(false) {}
157TracksBuilder::~TracksBuilder() {}
158
159void TracksBuilder::AddVideoTrack(int track_num,
160 uint64_t track_uid,
161 const std::string& codec_id,
162 const std::string& name,
163 const std::string& language,
164 int default_duration,
165 int video_pixel_width,
166 int video_pixel_height) {
167 AddTrackInternal(track_num, kWebMTrackTypeVideo, track_uid, codec_id, name,
168 language, default_duration, video_pixel_width,
169 video_pixel_height, -1, -1);
170}
171
172void TracksBuilder::AddAudioTrack(int track_num,
173 uint64_t track_uid,
174 const std::string& codec_id,
175 const std::string& name,
176 const std::string& language,
177 int default_duration,
178 int audio_channels,
179 double audio_sampling_frequency) {
180 AddTrackInternal(track_num, kWebMTrackTypeAudio, track_uid, codec_id, name,
181 language, default_duration, -1, -1, audio_channels,
182 audio_sampling_frequency);
183}
184
185void TracksBuilder::AddTextTrack(int track_num,
186 uint64_t track_uid,
187 const std::string& codec_id,
188 const std::string& name,
189 const std::string& language) {
190 AddTrackInternal(track_num, kWebMTrackTypeSubtitlesOrCaptions, track_uid,
191 codec_id, name, language, -1, -1, -1, -1, -1);
192}
193
194std::vector<uint8_t> TracksBuilder::Finish() {
195 // Allocate the storage
196 std::vector<uint8_t> buffer;
197 buffer.resize(GetTracksSize());
198
199 // Populate the storage with a tracks header
200 WriteTracks(&buffer[0], static_cast<int>(buffer.size()));
201
202 return buffer;
203}
204
205void TracksBuilder::AddTrackInternal(int track_num,
206 int track_type,
207 uint64_t track_uid,
208 const std::string& codec_id,
209 const std::string& name,
210 const std::string& language,
211 int default_duration,
212 int video_pixel_width,
213 int video_pixel_height,
214 int audio_channels,
215 double audio_sampling_frequency) {
216 tracks_.push_back(Track(track_num, track_type, track_uid, codec_id, name,
217 language, default_duration, video_pixel_width,
218 video_pixel_height, audio_channels,
219 audio_sampling_frequency, allow_invalid_values_));
220}
221
222int TracksBuilder::GetTracksSize() const {
223 return MasterElementSize(kWebMIdTracks, GetTracksPayloadSize());
224}
225
226int TracksBuilder::GetTracksPayloadSize() const {
227 int payload_size = 0;
228
229 for (TrackList::const_iterator itr = tracks_.begin();
230 itr != tracks_.end(); ++itr) {
231 payload_size += itr->GetSize();
232 }
233
234 return payload_size;
235}
236
237void TracksBuilder::WriteTracks(uint8_t* buf, int buf_size) const {
238 WriteMasterElement(&buf, &buf_size, kWebMIdTracks, GetTracksPayloadSize());
239
240 for (TrackList::const_iterator itr = tracks_.begin();
241 itr != tracks_.end(); ++itr) {
242 itr->Write(&buf, &buf_size);
243 }
244}
245
246TracksBuilder::Track::Track(int track_num,
247 int track_type,
248 uint64_t track_uid,
249 const std::string& codec_id,
250 const std::string& name,
251 const std::string& language,
252 int default_duration,
253 int video_pixel_width,
254 int video_pixel_height,
255 int audio_channels,
256 double audio_sampling_frequency,
257 bool allow_invalid_values)
258 : track_num_(track_num),
259 track_type_(track_type),
260 track_uid_(track_uid),
261 codec_id_(codec_id),
262 name_(name),
263 language_(language),
264 default_duration_(default_duration),
265 video_pixel_width_(video_pixel_width),
266 video_pixel_height_(video_pixel_height),
267 audio_channels_(audio_channels),
268 audio_sampling_frequency_(audio_sampling_frequency) {
269 if (!allow_invalid_values) {
270 CHECK_GT(track_num_, 0);
271 CHECK_GT(track_type_, 0);
272 CHECK_LT(track_type_, 255);
273 CHECK_GT(track_uid_, 0);
274 if (track_type != kWebMTrackTypeVideo &&
275 track_type != kWebMTrackTypeAudio) {
276 CHECK_EQ(default_duration_, -1);
277 } else {
278 CHECK(default_duration_ == -1 || default_duration_ > 0);
279 }
280
281 if (track_type == kWebMTrackTypeVideo) {
282 CHECK_GT(video_pixel_width_, 0);
283 CHECK_GT(video_pixel_height_, 0);
284 } else {
285 CHECK_EQ(video_pixel_width_, -1);
286 CHECK_EQ(video_pixel_height_, -1);
287 }
288
289 if (track_type == kWebMTrackTypeAudio) {
290 CHECK_GT(audio_channels_, 0);
291 CHECK_GT(audio_sampling_frequency_, 0.0);
292 } else {
293 CHECK_EQ(audio_channels_, -1);
294 CHECK_EQ(audio_sampling_frequency_, -1.0);
295 }
296 }
297}
298
299int TracksBuilder::Track::GetSize() const {
300 return MasterElementSize(kWebMIdTrackEntry, GetPayloadSize());
301}
302
303int TracksBuilder::Track::GetVideoPayloadSize() const {
304 int payload_size = 0;
305
306 if (video_pixel_width_ >= 0)
307 payload_size += UIntElementSize(kWebMIdPixelWidth, video_pixel_width_);
308 if (video_pixel_height_ >= 0)
309 payload_size += UIntElementSize(kWebMIdPixelHeight, video_pixel_height_);
310
311 return payload_size;
312}
313
314int TracksBuilder::Track::GetAudioPayloadSize() const {
315 int payload_size = 0;
316
317 if (audio_channels_ >= 0)
318 payload_size += UIntElementSize(kWebMIdChannels, audio_channels_);
319 if (audio_sampling_frequency_ >= 0)
320 payload_size += DoubleElementSize(kWebMIdSamplingFrequency);
321
322 return payload_size;
323}
324
325int TracksBuilder::Track::GetPayloadSize() const {
326 int size = 0;
327
328 size += UIntElementSize(kWebMIdTrackNumber, track_num_);
329 size += UIntElementSize(kWebMIdTrackType, track_type_);
330 size += UIntElementSize(kWebMIdTrackUID, track_uid_);
331
332 if (default_duration_ >= 0)
333 size += UIntElementSize(kWebMIdDefaultDuration, default_duration_);
334
335 if (!codec_id_.empty())
336 size += StringElementSize(kWebMIdCodecID, codec_id_);
337
338 if (!name_.empty())
339 size += StringElementSize(kWebMIdName, name_);
340
341 if (!language_.empty())
342 size += StringElementSize(kWebMIdLanguage, language_);
343
344 if (GetVideoPayloadSize() > 0) {
345 size += MasterElementSize(kWebMIdVideo, GetVideoPayloadSize());
346 }
347
348 if (GetAudioPayloadSize() > 0) {
349 size += MasterElementSize(kWebMIdAudio, GetAudioPayloadSize());
350 }
351
352 return size;
353}
354
355void TracksBuilder::Track::Write(uint8_t** buf, int* buf_size) const {
356 WriteMasterElement(buf, buf_size, kWebMIdTrackEntry, GetPayloadSize());
357
358 WriteUIntElement(buf, buf_size, kWebMIdTrackNumber, track_num_);
359 WriteUIntElement(buf, buf_size, kWebMIdTrackType, track_type_);
360 WriteUIntElement(buf, buf_size, kWebMIdTrackUID, track_uid_);
361
362 if (default_duration_ >= 0)
363 WriteUIntElement(buf, buf_size, kWebMIdDefaultDuration, default_duration_);
364
365 if (!codec_id_.empty())
366 WriteStringElement(buf, buf_size, kWebMIdCodecID, codec_id_);
367
368 if (!name_.empty())
369 WriteStringElement(buf, buf_size, kWebMIdName, name_);
370
371 if (!language_.empty())
372 WriteStringElement(buf, buf_size, kWebMIdLanguage, language_);
373
374 if (GetVideoPayloadSize() > 0) {
375 WriteMasterElement(buf, buf_size, kWebMIdVideo, GetVideoPayloadSize());
376
377 if (video_pixel_width_ >= 0)
378 WriteUIntElement(buf, buf_size, kWebMIdPixelWidth, video_pixel_width_);
379
380 if (video_pixel_height_ >= 0)
381 WriteUIntElement(buf, buf_size, kWebMIdPixelHeight, video_pixel_height_);
382 }
383
384 if (GetAudioPayloadSize() > 0) {
385 WriteMasterElement(buf, buf_size, kWebMIdAudio, GetAudioPayloadSize());
386
387 if (audio_channels_ >= 0)
388 WriteUIntElement(buf, buf_size, kWebMIdChannels, audio_channels_);
389
390 if (audio_sampling_frequency_ >= 0) {
391 WriteDoubleElement(buf, buf_size, kWebMIdSamplingFrequency,
392 audio_sampling_frequency_);
393 }
394 }
395}
396
397} // namespace media
398} // namespace shaka
All the methods that are virtual are virtual for mocking.