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