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
10namespace media {
11
12// Returns size of an integer, formatted using Matroska serialization.
13static 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.
32static 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
50static int MasterElementSize(int element_id, int payload_size) {
51  return GetUIntSize(element_id) + GetUIntMkvSize(payload_size) + payload_size;
52}
53
54static int UIntElementSize(int element_id, uint64 value) {
55  return GetUIntSize(element_id) + 1 + GetUIntSize(value);
56}
57
58static int DoubleElementSize(int element_id) {
59  return GetUIntSize(element_id) + 1 + 8;
60}
61
62static int StringElementSize(int element_id, const std::string& value) {
63 return GetUIntSize(element_id) +
64        GetUIntMkvSize(value.length()) +
65        value.length();
66}
67
68static 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
79static 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
92static void WriteElementId(uint8** buf, int* buf_size, int element_id) {
93  SerializeInt(buf, buf_size, element_id, GetUIntSize(element_id));
94}
95
96static 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
102static 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
108static void WriteUIntElement(uint8** buf,
109                             int* buf_size,
110                             int element_id,
111                             uint64 value) {
112  WriteElementId(buf, buf_size, element_id);
113
114  const int size = GetUIntSize(value);
115  WriteUInt(buf, buf_size, size);
116
117  SerializeInt(buf, buf_size, value, size);
118}
119
120static void WriteDoubleElement(uint8** buf, int* buf_size,
121                               int element_id, double value) {
122  WriteElementId(buf, buf_size, element_id);
123  WriteUInt(buf, buf_size, 8);
124  SerializeDouble(buf, buf_size, value);
125}
126
127static void WriteStringElement(uint8** buf_ptr, int* buf_size_ptr,
128                               int element_id, const std::string& value) {
129  uint8*& buf = *buf_ptr;
130  int& buf_size = *buf_size_ptr;
131
132  WriteElementId(&buf, &buf_size, element_id);
133
134  const uint64 size = value.length();
135  WriteUInt(&buf, &buf_size, size);
136
137  memcpy(buf, value.data(), size);
138  buf += size;
139  buf_size -= size;
140}
141
142TracksBuilder::TracksBuilder(bool allow_invalid_values)
143    : allow_invalid_values_(allow_invalid_values) {}
144TracksBuilder::TracksBuilder()
145    : allow_invalid_values_(false) {}
146TracksBuilder::~TracksBuilder() {}
147
148void TracksBuilder::AddVideoTrack(int track_num,
149                                  uint64 track_uid,
150                                  const std::string& codec_id,
151                                  const std::string& name,
152                                  const std::string& language,
153                                  int default_duration,
154                                  int video_pixel_width,
155                                  int video_pixel_height) {
156  AddTrackInternal(track_num, kWebMTrackTypeVideo, track_uid, codec_id, name,
157                   language, default_duration, video_pixel_width,
158                   video_pixel_height, -1, -1);
159}
160
161void TracksBuilder::AddAudioTrack(int track_num,
162                                  uint64 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
174void TracksBuilder::AddTextTrack(int track_num,
175                                 uint64 track_uid,
176                                 const std::string& codec_id,
177                                 const std::string& name,
178                                 const std::string& language) {
179  AddTrackInternal(track_num, kWebMTrackTypeSubtitlesOrCaptions, track_uid,
180                   codec_id, name, language, -1, -1, -1, -1, -1);
181}
182
183std::vector<uint8> TracksBuilder::Finish() {
184  // Allocate the storage
185  std::vector<uint8> buffer;
186  buffer.resize(GetTracksSize());
187
188  // Populate the storage with a tracks header
189  WriteTracks(&buffer[0], buffer.size());
190
191  return buffer;
192}
193
194void TracksBuilder::AddTrackInternal(int track_num,
195                                     int track_type,
196                                     uint64 track_uid,
197                                     const std::string& codec_id,
198                                     const std::string& name,
199                                     const std::string& language,
200                                     int default_duration,
201                                     int video_pixel_width,
202                                     int video_pixel_height,
203                                     int audio_channels,
204                                     double audio_sampling_frequency) {
205  tracks_.push_back(Track(track_num, track_type, track_uid, codec_id, name,
206                          language, default_duration, video_pixel_width,
207                          video_pixel_height, audio_channels,
208                          audio_sampling_frequency, allow_invalid_values_));
209}
210
211int TracksBuilder::GetTracksSize() const {
212  return MasterElementSize(kWebMIdTracks, GetTracksPayloadSize());
213}
214
215int TracksBuilder::GetTracksPayloadSize() const {
216  int payload_size = 0;
217
218  for (TrackList::const_iterator itr = tracks_.begin();
219       itr != tracks_.end(); ++itr) {
220    payload_size += itr->GetSize();
221  }
222
223  return payload_size;
224}
225
226void TracksBuilder::WriteTracks(uint8* buf, int buf_size) const {
227  WriteMasterElement(&buf, &buf_size, kWebMIdTracks, GetTracksPayloadSize());
228
229  for (TrackList::const_iterator itr = tracks_.begin();
230       itr != tracks_.end(); ++itr) {
231    itr->Write(&buf, &buf_size);
232  }
233}
234
235TracksBuilder::Track::Track(int track_num,
236                            int track_type,
237                            uint64 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,
243                            int video_pixel_height,
244                            int audio_channels,
245                            double audio_sampling_frequency,
246                            bool allow_invalid_values)
247    : track_num_(track_num),
248      track_type_(track_type),
249      track_uid_(track_uid),
250      codec_id_(codec_id),
251      name_(name),
252      language_(language),
253      default_duration_(default_duration),
254      video_pixel_width_(video_pixel_width),
255      video_pixel_height_(video_pixel_height),
256      audio_channels_(audio_channels),
257      audio_sampling_frequency_(audio_sampling_frequency) {
258  if (!allow_invalid_values) {
259    CHECK_GT(track_num_, 0);
260    CHECK_GT(track_type_, 0);
261    CHECK_LT(track_type_, 255);
262    CHECK_GT(track_uid_, 0);
263    if (track_type != kWebMTrackTypeVideo &&
264        track_type != kWebMTrackTypeAudio) {
265      CHECK_EQ(default_duration_, -1);
266    } else {
267      CHECK(default_duration_ == -1 || default_duration_ > 0);
268    }
269
270    if (track_type == kWebMTrackTypeVideo) {
271      CHECK_GT(video_pixel_width_, 0);
272      CHECK_GT(video_pixel_height_, 0);
273    } else {
274      CHECK_EQ(video_pixel_width_, -1);
275      CHECK_EQ(video_pixel_height_, -1);
276    }
277
278    if (track_type == kWebMTrackTypeAudio) {
279      CHECK_GT(audio_channels_, 0);
280      CHECK_GT(audio_sampling_frequency_, 0.0);
281    } else {
282      CHECK_EQ(audio_channels_, -1);
283      CHECK_EQ(audio_sampling_frequency_, -1.0);
284    }
285  }
286}
287
288int TracksBuilder::Track::GetSize() const {
289  return MasterElementSize(kWebMIdTrackEntry, GetPayloadSize());
290}
291
292int TracksBuilder::Track::GetVideoPayloadSize() const {
293  int payload_size = 0;
294
295  if (video_pixel_width_ >= 0)
296    payload_size += UIntElementSize(kWebMIdPixelWidth, video_pixel_width_);
297  if (video_pixel_height_ >= 0)
298    payload_size += UIntElementSize(kWebMIdPixelHeight, video_pixel_height_);
299
300  return payload_size;
301}
302
303int TracksBuilder::Track::GetAudioPayloadSize() const {
304  int payload_size = 0;
305
306  if (audio_channels_ >= 0)
307    payload_size += UIntElementSize(kWebMIdChannels, audio_channels_);
308  if (audio_sampling_frequency_ >= 0)
309    payload_size += DoubleElementSize(kWebMIdSamplingFrequency);
310
311  return payload_size;
312}
313
314int TracksBuilder::Track::GetPayloadSize() const {
315  int size = 0;
316
317  size += UIntElementSize(kWebMIdTrackNumber, track_num_);
318  size += UIntElementSize(kWebMIdTrackType, track_type_);
319  size += UIntElementSize(kWebMIdTrackUID, track_uid_);
320
321  if (default_duration_ >= 0)
322    size += UIntElementSize(kWebMIdDefaultDuration, default_duration_);
323
324  if (!codec_id_.empty())
325    size += StringElementSize(kWebMIdCodecID, codec_id_);
326
327  if (!name_.empty())
328    size += StringElementSize(kWebMIdName, name_);
329
330  if (!language_.empty())
331    size += StringElementSize(kWebMIdLanguage, language_);
332
333  if (GetVideoPayloadSize() > 0) {
334    size += MasterElementSize(kWebMIdVideo, GetVideoPayloadSize());
335  }
336
337  if (GetAudioPayloadSize() > 0) {
338    size += MasterElementSize(kWebMIdAudio, GetAudioPayloadSize());
339  }
340
341  return size;
342}
343
344void TracksBuilder::Track::Write(uint8** buf, int* buf_size) const {
345  WriteMasterElement(buf, buf_size, kWebMIdTrackEntry, GetPayloadSize());
346
347  WriteUIntElement(buf, buf_size, kWebMIdTrackNumber, track_num_);
348  WriteUIntElement(buf, buf_size, kWebMIdTrackType, track_type_);
349  WriteUIntElement(buf, buf_size, kWebMIdTrackUID, track_uid_);
350
351  if (default_duration_ >= 0)
352    WriteUIntElement(buf, buf_size, kWebMIdDefaultDuration, default_duration_);
353
354  if (!codec_id_.empty())
355    WriteStringElement(buf, buf_size, kWebMIdCodecID, codec_id_);
356
357  if (!name_.empty())
358    WriteStringElement(buf, buf_size, kWebMIdName, name_);
359
360  if (!language_.empty())
361    WriteStringElement(buf, buf_size, kWebMIdLanguage, language_);
362
363  if (GetVideoPayloadSize() > 0) {
364    WriteMasterElement(buf, buf_size, kWebMIdVideo, GetVideoPayloadSize());
365
366    if (video_pixel_width_ >= 0)
367      WriteUIntElement(buf, buf_size, kWebMIdPixelWidth, video_pixel_width_);
368
369    if (video_pixel_height_ >= 0)
370      WriteUIntElement(buf, buf_size, kWebMIdPixelHeight, video_pixel_height_);
371  }
372
373  if (GetAudioPayloadSize() > 0) {
374    WriteMasterElement(buf, buf_size, kWebMIdAudio, GetAudioPayloadSize());
375
376    if (audio_channels_ >= 0)
377      WriteUIntElement(buf, buf_size, kWebMIdChannels, audio_channels_);
378
379    if (audio_sampling_frequency_ >= 0) {
380      WriteDoubleElement(buf, buf_size, kWebMIdSamplingFrequency,
381          audio_sampling_frequency_);
382    }
383  }
384}
385
386}  // namespace media
387