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