diff options
Diffstat (limited to 'chromium/media/formats/webm/tracks_builder.cc')
-rw-r--r-- | chromium/media/formats/webm/tracks_builder.cc | 384 |
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 |