// 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 #include #include namespace shaka { namespace media { // Returns size of an integer, formatted using Matroska serialization. static int GetUIntMkvSize(uint64_t 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_t 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 UIntElementSize(int element_id, uint64_t 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()) + static_cast(value.length()); } static void SerializeInt(uint8_t** buf_ptr, int* buf_size_ptr, int64_t value, int size) { uint8_t*& buf = *buf_ptr; int& buf_size = *buf_size_ptr; for (int idx = 1; idx <= size; ++idx) { *buf++ = static_cast(value >> ((size - idx) * 8)); --buf_size; } } static void SerializeDouble(uint8_t** buf_ptr, int* buf_size_ptr, double value) { // Use a union to convert |value| to native endian integer bit pattern. union { double src; int64_t 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_t** buf, int* buf_size, int element_id) { SerializeInt(buf, buf_size, element_id, GetUIntSize(element_id)); } static void WriteUInt(uint8_t** buf, int* buf_size, uint64_t value) { const int size = GetUIntMkvSize(value); value |= (1ULL << (size * 7)); // Matroska formatting SerializeInt(buf, buf_size, value, size); } static void WriteMasterElement(uint8_t** buf, int* buf_size, int element_id, int payload_size) { WriteElementId(buf, buf_size, element_id); WriteUInt(buf, buf_size, payload_size); } static void WriteUIntElement(uint8_t** buf, int* buf_size, int element_id, uint64_t 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_t** 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_t** buf_ptr, int* buf_size_ptr, int element_id, const std::string& value) { uint8_t*& buf = *buf_ptr; int& buf_size = *buf_size_ptr; WriteElementId(&buf, &buf_size, element_id); const uint64_t 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, uint64_t 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, uint64_t 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, uint64_t 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 TracksBuilder::Finish() { // Allocate the storage std::vector buffer; buffer.resize(GetTracksSize()); // Populate the storage with a tracks header WriteTracks(&buffer[0], static_cast(buffer.size())); return buffer; } void TracksBuilder::AddTrackInternal(int track_num, int track_type, uint64_t 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_t* 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, uint64_t 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 += UIntElementSize(kWebMIdPixelWidth, video_pixel_width_); if (video_pixel_height_ >= 0) payload_size += UIntElementSize(kWebMIdPixelHeight, video_pixel_height_); return payload_size; } int TracksBuilder::Track::GetAudioPayloadSize() const { int payload_size = 0; if (audio_channels_ >= 0) payload_size += UIntElementSize(kWebMIdChannels, audio_channels_); if (audio_sampling_frequency_ >= 0) payload_size += DoubleElementSize(kWebMIdSamplingFrequency); return payload_size; } int TracksBuilder::Track::GetPayloadSize() const { int size = 0; size += UIntElementSize(kWebMIdTrackNumber, track_num_); size += UIntElementSize(kWebMIdTrackType, track_type_); size += UIntElementSize(kWebMIdTrackUID, track_uid_); if (default_duration_ >= 0) size += UIntElementSize(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_t** buf, int* buf_size) const { WriteMasterElement(buf, buf_size, kWebMIdTrackEntry, GetPayloadSize()); WriteUIntElement(buf, buf_size, kWebMIdTrackNumber, track_num_); WriteUIntElement(buf, buf_size, kWebMIdTrackType, track_type_); WriteUIntElement(buf, buf_size, kWebMIdTrackUID, track_uid_); if (default_duration_ >= 0) WriteUIntElement(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) WriteUIntElement(buf, buf_size, kWebMIdPixelWidth, video_pixel_width_); if (video_pixel_height_ >= 0) WriteUIntElement(buf, buf_size, kWebMIdPixelHeight, video_pixel_height_); } if (GetAudioPayloadSize() > 0) { WriteMasterElement(buf, buf_size, kWebMIdAudio, GetAudioPayloadSize()); if (audio_channels_ >= 0) WriteUIntElement(buf, buf_size, kWebMIdChannels, audio_channels_); if (audio_sampling_frequency_ >= 0) { WriteDoubleElement(buf, buf_size, kWebMIdSamplingFrequency, audio_sampling_frequency_); } } } } // namespace media } // namespace shaka