• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
10 namespace media {
11 
12 // Returns size of an integer, formatted using Matroska serialization.
GetUIntMkvSize(uint64 value)13 static 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.
GetUIntSize(uint64 value)32 static 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 
MasterElementSize(int element_id,int payload_size)50 static int MasterElementSize(int element_id, int payload_size) {
51   return GetUIntSize(element_id) + GetUIntMkvSize(payload_size) + payload_size;
52 }
53 
UIntElementSize(int element_id,uint64 value)54 static int UIntElementSize(int element_id, uint64 value) {
55   return GetUIntSize(element_id) + 1 + GetUIntSize(value);
56 }
57 
DoubleElementSize(int element_id)58 static int DoubleElementSize(int element_id) {
59   return GetUIntSize(element_id) + 1 + 8;
60 }
61 
StringElementSize(int element_id,const std::string & value)62 static int StringElementSize(int element_id, const std::string& value) {
63  return GetUIntSize(element_id) +
64         GetUIntMkvSize(value.length()) +
65         value.length();
66 }
67 
SerializeInt(uint8 ** buf_ptr,int * buf_size_ptr,int64 value,int size)68 static 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 
SerializeDouble(uint8 ** buf_ptr,int * buf_size_ptr,double value)79 static 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 
WriteElementId(uint8 ** buf,int * buf_size,int element_id)92 static void WriteElementId(uint8** buf, int* buf_size, int element_id) {
93   SerializeInt(buf, buf_size, element_id, GetUIntSize(element_id));
94 }
95 
WriteUInt(uint8 ** buf,int * buf_size,uint64 value)96 static 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 
WriteMasterElement(uint8 ** buf,int * buf_size,int element_id,int payload_size)102 static 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 
WriteUIntElement(uint8 ** buf,int * buf_size,int element_id,uint64 value)108 static 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 
WriteDoubleElement(uint8 ** buf,int * buf_size,int element_id,double value)120 static 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 
WriteStringElement(uint8 ** buf_ptr,int * buf_size_ptr,int element_id,const std::string & value)127 static 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 
TracksBuilder(bool allow_invalid_values)142 TracksBuilder::TracksBuilder(bool allow_invalid_values)
143     : allow_invalid_values_(allow_invalid_values) {}
TracksBuilder()144 TracksBuilder::TracksBuilder()
145     : allow_invalid_values_(false) {}
~TracksBuilder()146 TracksBuilder::~TracksBuilder() {}
147 
AddVideoTrack(int track_num,uint64 track_uid,const std::string & codec_id,const std::string & name,const std::string & language,int default_duration,int video_pixel_width,int video_pixel_height)148 void 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 
AddAudioTrack(int track_num,uint64 track_uid,const std::string & codec_id,const std::string & name,const std::string & language,int default_duration,int audio_channels,double audio_sampling_frequency)161 void 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 
AddTextTrack(int track_num,uint64 track_uid,const std::string & codec_id,const std::string & name,const std::string & language)174 void 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 
Finish()183 std::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 
AddTrackInternal(int track_num,int track_type,uint64 track_uid,const std::string & codec_id,const std::string & name,const std::string & language,int default_duration,int video_pixel_width,int video_pixel_height,int audio_channels,double audio_sampling_frequency)194 void 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 
GetTracksSize() const211 int TracksBuilder::GetTracksSize() const {
212   return MasterElementSize(kWebMIdTracks, GetTracksPayloadSize());
213 }
214 
GetTracksPayloadSize() const215 int 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 
WriteTracks(uint8 * buf,int buf_size) const226 void 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 
Track(int track_num,int track_type,uint64 track_uid,const std::string & codec_id,const std::string & name,const std::string & language,int default_duration,int video_pixel_width,int video_pixel_height,int audio_channels,double audio_sampling_frequency,bool allow_invalid_values)235 TracksBuilder::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 
GetSize() const288 int TracksBuilder::Track::GetSize() const {
289   return MasterElementSize(kWebMIdTrackEntry, GetPayloadSize());
290 }
291 
GetVideoPayloadSize() const292 int 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 
GetAudioPayloadSize() const303 int 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 
GetPayloadSize() const314 int 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 
Write(uint8 ** buf,int * buf_size) const344 void 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