summaryrefslogtreecommitdiffstats
path: root/chromium/media/formats/webm/tracks_builder.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/formats/webm/tracks_builder.cc')
-rw-r--r--chromium/media/formats/webm/tracks_builder.cc384
1 files changed, 384 insertions, 0 deletions
diff --git a/chromium/media/formats/webm/tracks_builder.cc b/chromium/media/formats/webm/tracks_builder.cc
new file mode 100644
index 00000000000..fb402c4729c
--- /dev/null
+++ b/chromium/media/formats/webm/tracks_builder.cc
@@ -0,0 +1,384 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/formats/webm/tracks_builder.h"
+
+#include "base/logging.h"
+#include "media/formats/webm/webm_constants.h"
+
+namespace media {
+
+// Returns size of an integer, formatted using Matroska serialization.
+static int GetUIntMkvSize(uint64 value) {
+ if (value < 0x07FULL)
+ return 1;
+ if (value < 0x03FFFULL)
+ return 2;
+ if (value < 0x01FFFFFULL)
+ return 3;
+ if (value < 0x0FFFFFFFULL)
+ return 4;
+ if (value < 0x07FFFFFFFFULL)
+ return 5;
+ if (value < 0x03FFFFFFFFFFULL)
+ return 6;
+ if (value < 0x01FFFFFFFFFFFFULL)
+ return 7;
+ return 8;
+}
+
+// Returns the minimium size required to serialize an integer value.
+static int GetUIntSize(uint64 value) {
+ if (value < 0x0100ULL)
+ return 1;
+ if (value < 0x010000ULL)
+ return 2;
+ if (value < 0x01000000ULL)
+ return 3;
+ if (value < 0x0100000000ULL)
+ return 4;
+ if (value < 0x010000000000ULL)
+ return 5;
+ if (value < 0x01000000000000ULL)
+ return 6;
+ if (value < 0x0100000000000000ULL)
+ return 7;
+ return 8;
+}
+
+static int MasterElementSize(int element_id, int payload_size) {
+ return GetUIntSize(element_id) + GetUIntMkvSize(payload_size) + payload_size;
+}
+
+static int IntElementSize(int element_id, int value) {
+ return GetUIntSize(element_id) + 1 + GetUIntSize(value);
+}
+
+static int DoubleElementSize(int element_id) {
+ return GetUIntSize(element_id) + 1 + 8;
+}
+
+static int StringElementSize(int element_id, const std::string& value) {
+ return GetUIntSize(element_id) +
+ GetUIntMkvSize(value.length()) +
+ value.length();
+}
+
+static void SerializeInt(uint8** buf_ptr, int* buf_size_ptr,
+ int64 value, int size) {
+ uint8*& buf = *buf_ptr;
+ int& buf_size = *buf_size_ptr;
+
+ for (int idx = 1; idx <= size; ++idx) {
+ *buf++ = static_cast<uint8>(value >> ((size - idx) * 8));
+ --buf_size;
+ }
+}
+
+static void SerializeDouble(uint8** buf_ptr, int* buf_size_ptr,
+ double value) {
+ // Use a union to convert |value| to native endian integer bit pattern.
+ union {
+ double src;
+ int64 dst;
+ } tmp;
+ tmp.src = value;
+
+ // Write the bytes from native endian |tmp.dst| to big-endian form in |buf|.
+ SerializeInt(buf_ptr, buf_size_ptr, tmp.dst, 8);
+}
+
+static void WriteElementId(uint8** buf, int* buf_size, int element_id) {
+ SerializeInt(buf, buf_size, element_id, GetUIntSize(element_id));
+}
+
+static void WriteUInt(uint8** buf, int* buf_size, uint64 value) {
+ const int size = GetUIntMkvSize(value);
+ value |= (1ULL << (size * 7)); // Matroska formatting
+ SerializeInt(buf, buf_size, value, size);
+}
+
+static void WriteMasterElement(uint8** buf, int* buf_size,
+ int element_id, int payload_size) {
+ WriteElementId(buf, buf_size, element_id);
+ WriteUInt(buf, buf_size, payload_size);
+}
+
+static void WriteIntElement(uint8** buf, int* buf_size,
+ int element_id, int value) {
+ WriteElementId(buf, buf_size, element_id);
+
+ const int size = GetUIntSize(value);
+ WriteUInt(buf, buf_size, size);
+
+ SerializeInt(buf, buf_size, value, size);
+}
+
+static void WriteDoubleElement(uint8** buf, int* buf_size,
+ int element_id, double value) {
+ WriteElementId(buf, buf_size, element_id);
+ WriteUInt(buf, buf_size, 8);
+ SerializeDouble(buf, buf_size, value);
+}
+
+static void WriteStringElement(uint8** buf_ptr, int* buf_size_ptr,
+ int element_id, const std::string& value) {
+ uint8*& buf = *buf_ptr;
+ int& buf_size = *buf_size_ptr;
+
+ WriteElementId(&buf, &buf_size, element_id);
+
+ const uint64 size = value.length();
+ WriteUInt(&buf, &buf_size, size);
+
+ memcpy(buf, value.data(), size);
+ buf += size;
+ buf_size -= size;
+}
+
+TracksBuilder::TracksBuilder(bool allow_invalid_values)
+ : allow_invalid_values_(allow_invalid_values) {}
+TracksBuilder::TracksBuilder()
+ : allow_invalid_values_(false) {}
+TracksBuilder::~TracksBuilder() {}
+
+void TracksBuilder::AddVideoTrack(
+ int track_num,
+ int 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) {
+ AddTrackInternal(track_num, kWebMTrackTypeVideo, track_uid, codec_id, name,
+ language, default_duration, video_pixel_width,
+ video_pixel_height, -1, -1);
+}
+
+void TracksBuilder::AddAudioTrack(
+ int track_num,
+ int 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) {
+ AddTrackInternal(track_num, kWebMTrackTypeAudio, track_uid, codec_id, name,
+ language, default_duration, -1, -1, audio_channels,
+ audio_sampling_frequency);
+}
+
+void TracksBuilder::AddTextTrack(
+ int track_num,
+ int track_uid,
+ const std::string& codec_id,
+ const std::string& name,
+ const std::string& language) {
+ AddTrackInternal(track_num, kWebMTrackTypeSubtitlesOrCaptions, track_uid,
+ codec_id, name, language, -1, -1, -1, -1, -1);
+}
+
+std::vector<uint8> TracksBuilder::Finish() {
+ // Allocate the storage
+ std::vector<uint8> buffer;
+ buffer.resize(GetTracksSize());
+
+ // Populate the storage with a tracks header
+ WriteTracks(&buffer[0], buffer.size());
+
+ return buffer;
+}
+
+void TracksBuilder::AddTrackInternal(
+ int track_num,
+ int track_type,
+ int 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) {
+ tracks_.push_back(Track(track_num, track_type, track_uid, codec_id, name,
+ language, default_duration, video_pixel_width,
+ video_pixel_height, audio_channels,
+ audio_sampling_frequency, allow_invalid_values_));
+}
+
+int TracksBuilder::GetTracksSize() const {
+ return MasterElementSize(kWebMIdTracks, GetTracksPayloadSize());
+}
+
+int TracksBuilder::GetTracksPayloadSize() const {
+ int payload_size = 0;
+
+ for (TrackList::const_iterator itr = tracks_.begin();
+ itr != tracks_.end(); ++itr) {
+ payload_size += itr->GetSize();
+ }
+
+ return payload_size;
+}
+
+void TracksBuilder::WriteTracks(uint8* buf, int buf_size) const {
+ WriteMasterElement(&buf, &buf_size, kWebMIdTracks, GetTracksPayloadSize());
+
+ for (TrackList::const_iterator itr = tracks_.begin();
+ itr != tracks_.end(); ++itr) {
+ itr->Write(&buf, &buf_size);
+ }
+}
+
+TracksBuilder::Track::Track(int track_num, int track_type, int 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)
+ : track_num_(track_num),
+ track_type_(track_type),
+ track_uid_(track_uid),
+ codec_id_(codec_id),
+ name_(name),
+ language_(language),
+ default_duration_(default_duration),
+ video_pixel_width_(video_pixel_width),
+ video_pixel_height_(video_pixel_height),
+ audio_channels_(audio_channels),
+ audio_sampling_frequency_(audio_sampling_frequency) {
+ if (!allow_invalid_values) {
+ CHECK_GT(track_num_, 0);
+ CHECK_GT(track_type_, 0);
+ CHECK_LT(track_type_, 255);
+ CHECK_GT(track_uid_, 0);
+ if (track_type != kWebMTrackTypeVideo &&
+ track_type != kWebMTrackTypeAudio) {
+ CHECK_EQ(default_duration_, -1);
+ } else {
+ CHECK(default_duration_ == -1 || default_duration_ > 0);
+ }
+
+ if (track_type == kWebMTrackTypeVideo) {
+ CHECK_GT(video_pixel_width_, 0);
+ CHECK_GT(video_pixel_height_, 0);
+ } else {
+ CHECK_EQ(video_pixel_width_, -1);
+ CHECK_EQ(video_pixel_height_, -1);
+ }
+
+ if (track_type == kWebMTrackTypeAudio) {
+ CHECK_GT(audio_channels_, 0);
+ CHECK_GT(audio_sampling_frequency_, 0.0);
+ } else {
+ CHECK_EQ(audio_channels_, -1);
+ CHECK_EQ(audio_sampling_frequency_, -1.0);
+ }
+ }
+}
+
+int TracksBuilder::Track::GetSize() const {
+ return MasterElementSize(kWebMIdTrackEntry, GetPayloadSize());
+}
+
+int TracksBuilder::Track::GetVideoPayloadSize() const {
+ int payload_size = 0;
+
+ if (video_pixel_width_ >= 0)
+ payload_size += IntElementSize(kWebMIdPixelWidth, video_pixel_width_);
+ if (video_pixel_height_ >= 0)
+ payload_size += IntElementSize(kWebMIdPixelHeight, video_pixel_height_);
+
+ return payload_size;
+}
+
+int TracksBuilder::Track::GetAudioPayloadSize() const {
+ int payload_size = 0;
+
+ if (audio_channels_ >= 0)
+ payload_size += IntElementSize(kWebMIdChannels, audio_channels_);
+ if (audio_sampling_frequency_ >= 0)
+ payload_size += DoubleElementSize(kWebMIdSamplingFrequency);
+
+ return payload_size;
+}
+
+int TracksBuilder::Track::GetPayloadSize() const {
+ int size = 0;
+
+ size += IntElementSize(kWebMIdTrackNumber, track_num_);
+ size += IntElementSize(kWebMIdTrackType, track_type_);
+ size += IntElementSize(kWebMIdTrackUID, track_uid_);
+
+ if (default_duration_ >= 0)
+ size += IntElementSize(kWebMIdDefaultDuration, default_duration_);
+
+ if (!codec_id_.empty())
+ size += StringElementSize(kWebMIdCodecID, codec_id_);
+
+ if (!name_.empty())
+ size += StringElementSize(kWebMIdName, name_);
+
+ if (!language_.empty())
+ size += StringElementSize(kWebMIdLanguage, language_);
+
+ if (GetVideoPayloadSize() > 0) {
+ size += MasterElementSize(kWebMIdVideo, GetVideoPayloadSize());
+ }
+
+ if (GetAudioPayloadSize() > 0) {
+ size += MasterElementSize(kWebMIdAudio, GetAudioPayloadSize());
+ }
+
+ return size;
+}
+
+void TracksBuilder::Track::Write(uint8** buf, int* buf_size) const {
+ WriteMasterElement(buf, buf_size, kWebMIdTrackEntry, GetPayloadSize());
+
+ WriteIntElement(buf, buf_size, kWebMIdTrackNumber, track_num_);
+ WriteIntElement(buf, buf_size, kWebMIdTrackType, track_type_);
+ WriteIntElement(buf, buf_size, kWebMIdTrackUID, track_uid_);
+
+ if (default_duration_ >= 0)
+ WriteIntElement(buf, buf_size, kWebMIdDefaultDuration, default_duration_);
+
+ if (!codec_id_.empty())
+ WriteStringElement(buf, buf_size, kWebMIdCodecID, codec_id_);
+
+ if (!name_.empty())
+ WriteStringElement(buf, buf_size, kWebMIdName, name_);
+
+ if (!language_.empty())
+ WriteStringElement(buf, buf_size, kWebMIdLanguage, language_);
+
+ if (GetVideoPayloadSize() > 0) {
+ WriteMasterElement(buf, buf_size, kWebMIdVideo, GetVideoPayloadSize());
+
+ if (video_pixel_width_ >= 0)
+ WriteIntElement(buf, buf_size, kWebMIdPixelWidth, video_pixel_width_);
+
+ if (video_pixel_height_ >= 0)
+ WriteIntElement(buf, buf_size, kWebMIdPixelHeight, video_pixel_height_);
+ }
+
+ if (GetAudioPayloadSize() > 0) {
+ WriteMasterElement(buf, buf_size, kWebMIdAudio, GetAudioPayloadSize());
+
+ if (audio_channels_ >= 0)
+ WriteIntElement(buf, buf_size, kWebMIdChannels, audio_channels_);
+
+ if (audio_sampling_frequency_ >= 0) {
+ WriteDoubleElement(buf, buf_size, kWebMIdSamplingFrequency,
+ audio_sampling_frequency_);
+ }
+ }
+}
+
+} // namespace media