diff options
author | Tom Finegan <tomfinegan@google.com> | 2016-03-26 01:41:49 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2016-03-26 01:41:49 +0000 |
commit | aa1132d1de6d81d276e698947607c56532235c55 (patch) | |
tree | fe71fee867f7044ce132c9b13797fcd000d4438e /third_party/libwebm/mkvmuxer/mkvmuxerutil.cc | |
parent | f94124cf3127795544cd8e1772ebf3d31a80dda0 (diff) | |
parent | 80ddeb281a0d4b3d9a6d39a7ef91370f52845b39 (diff) | |
download | libvpx-aa1132d1de6d81d276e698947607c56532235c55.tar libvpx-aa1132d1de6d81d276e698947607c56532235c55.tar.gz libvpx-aa1132d1de6d81d276e698947607c56532235c55.tar.bz2 libvpx-aa1132d1de6d81d276e698947607c56532235c55.zip |
Merge "third_party: Roll libwebm snapshot."
Diffstat (limited to 'third_party/libwebm/mkvmuxer/mkvmuxerutil.cc')
-rw-r--r-- | third_party/libwebm/mkvmuxer/mkvmuxerutil.cc | 640 |
1 files changed, 640 insertions, 0 deletions
diff --git a/third_party/libwebm/mkvmuxer/mkvmuxerutil.cc b/third_party/libwebm/mkvmuxer/mkvmuxerutil.cc new file mode 100644 index 000000000..ab4cfb0a9 --- /dev/null +++ b/third_party/libwebm/mkvmuxer/mkvmuxerutil.cc @@ -0,0 +1,640 @@ +// Copyright (c) 2012 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. + +#include "mkvmuxer/mkvmuxerutil.h" + +#ifdef __ANDROID__ +#include <fcntl.h> +#endif + +#include <cassert> +#include <cmath> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <ctime> +#include <new> + +#include "common/webmids.h" +#include "mkvmuxer/mkvmuxer.h" +#include "mkvmuxer/mkvwriter.h" + +#ifdef _MSC_VER +// Disable MSVC warnings that suggest making code non-portable. +#pragma warning(disable : 4996) +#endif + +namespace mkvmuxer { + +namespace { + +// Date elements are always 8 octets in size. +const int kDateElementSize = 8; + +uint64_t WriteBlock(IMkvWriter* writer, const Frame* const frame, + int64_t timecode, uint64_t timecode_scale) { + uint64_t block_additional_elem_size = 0; + uint64_t block_addid_elem_size = 0; + uint64_t block_more_payload_size = 0; + uint64_t block_more_elem_size = 0; + uint64_t block_additions_payload_size = 0; + uint64_t block_additions_elem_size = 0; + if (frame->additional()) { + block_additional_elem_size = + EbmlElementSize(libwebm::kMkvBlockAdditional, frame->additional(), + frame->additional_length()); + block_addid_elem_size = + EbmlElementSize(libwebm::kMkvBlockAddID, frame->add_id()); + + block_more_payload_size = + block_addid_elem_size + block_additional_elem_size; + block_more_elem_size = + EbmlMasterElementSize(libwebm::kMkvBlockMore, block_more_payload_size) + + block_more_payload_size; + block_additions_payload_size = block_more_elem_size; + block_additions_elem_size = + EbmlMasterElementSize(libwebm::kMkvBlockAdditions, + block_additions_payload_size) + + block_additions_payload_size; + } + + uint64_t discard_padding_elem_size = 0; + if (frame->discard_padding() != 0) { + discard_padding_elem_size = + EbmlElementSize(libwebm::kMkvDiscardPadding, frame->discard_padding()); + } + + const uint64_t reference_block_timestamp = + frame->reference_block_timestamp() / timecode_scale; + uint64_t reference_block_elem_size = 0; + if (!frame->is_key()) { + reference_block_elem_size = + EbmlElementSize(libwebm::kMkvReferenceBlock, reference_block_timestamp); + } + + const uint64_t duration = frame->duration() / timecode_scale; + uint64_t block_duration_elem_size = 0; + if (duration > 0) + block_duration_elem_size = + EbmlElementSize(libwebm::kMkvBlockDuration, duration); + + const uint64_t block_payload_size = 4 + frame->length(); + const uint64_t block_elem_size = + EbmlMasterElementSize(libwebm::kMkvBlock, block_payload_size) + + block_payload_size; + + const uint64_t block_group_payload_size = + block_elem_size + block_additions_elem_size + block_duration_elem_size + + discard_padding_elem_size + reference_block_elem_size; + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlockGroup, + block_group_payload_size)) { + return 0; + } + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlock, block_payload_size)) + return 0; + + if (WriteUInt(writer, frame->track_number())) + return 0; + + if (SerializeInt(writer, timecode, 2)) + return 0; + + // For a Block, flags is always 0. + if (SerializeInt(writer, 0, 1)) + return 0; + + if (writer->Write(frame->frame(), static_cast<uint32_t>(frame->length()))) + return 0; + + if (frame->additional()) { + if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlockAdditions, + block_additions_payload_size)) { + return 0; + } + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlockMore, + block_more_payload_size)) + return 0; + + if (!WriteEbmlElement(writer, libwebm::kMkvBlockAddID, frame->add_id())) + return 0; + + if (!WriteEbmlElement(writer, libwebm::kMkvBlockAdditional, + frame->additional(), frame->additional_length())) { + return 0; + } + } + + if (frame->discard_padding() != 0 && + !WriteEbmlElement(writer, libwebm::kMkvDiscardPadding, + frame->discard_padding())) { + return false; + } + + if (!frame->is_key() && + !WriteEbmlElement(writer, libwebm::kMkvReferenceBlock, + reference_block_timestamp)) { + return false; + } + + if (duration > 0 && + !WriteEbmlElement(writer, libwebm::kMkvBlockDuration, duration)) { + return false; + } + return EbmlMasterElementSize(libwebm::kMkvBlockGroup, + block_group_payload_size) + + block_group_payload_size; +} + +uint64_t WriteSimpleBlock(IMkvWriter* writer, const Frame* const frame, + int64_t timecode) { + if (WriteID(writer, libwebm::kMkvSimpleBlock)) + return 0; + + const int32_t size = static_cast<int32_t>(frame->length()) + 4; + if (WriteUInt(writer, size)) + return 0; + + if (WriteUInt(writer, static_cast<uint64_t>(frame->track_number()))) + return 0; + + if (SerializeInt(writer, timecode, 2)) + return 0; + + uint64_t flags = 0; + if (frame->is_key()) + flags |= 0x80; + + if (SerializeInt(writer, flags, 1)) + return 0; + + if (writer->Write(frame->frame(), static_cast<uint32_t>(frame->length()))) + return 0; + + return GetUIntSize(libwebm::kMkvSimpleBlock) + GetCodedUIntSize(size) + 4 + + frame->length(); +} + +} // namespace + +int32_t GetCodedUIntSize(uint64_t value) { + if (value < 0x000000000000007FULL) + return 1; + else if (value < 0x0000000000003FFFULL) + return 2; + else if (value < 0x00000000001FFFFFULL) + return 3; + else if (value < 0x000000000FFFFFFFULL) + return 4; + else if (value < 0x00000007FFFFFFFFULL) + return 5; + else if (value < 0x000003FFFFFFFFFFULL) + return 6; + else if (value < 0x0001FFFFFFFFFFFFULL) + return 7; + return 8; +} + +int32_t GetUIntSize(uint64_t value) { + if (value < 0x0000000000000100ULL) + return 1; + else if (value < 0x0000000000010000ULL) + return 2; + else if (value < 0x0000000001000000ULL) + return 3; + else if (value < 0x0000000100000000ULL) + return 4; + else if (value < 0x0000010000000000ULL) + return 5; + else if (value < 0x0001000000000000ULL) + return 6; + else if (value < 0x0100000000000000ULL) + return 7; + return 8; +} + +int32_t GetIntSize(int64_t value) { + // Doubling the requested value ensures positive values with their high bit + // set are written with 0-padding to avoid flipping the signedness. + const uint64_t v = (value < 0) ? value ^ -1LL : value; + return GetUIntSize(2 * v); +} + +uint64_t EbmlMasterElementSize(uint64_t type, uint64_t value) { + // Size of EBML ID + int32_t ebml_size = GetUIntSize(type); + + // Datasize + ebml_size += GetCodedUIntSize(value); + + return ebml_size; +} + +uint64_t EbmlElementSize(uint64_t type, int64_t value) { + // Size of EBML ID + int32_t ebml_size = GetUIntSize(type); + + // Datasize + ebml_size += GetIntSize(value); + + // Size of Datasize + ebml_size++; + + return ebml_size; +} + +uint64_t EbmlElementSize(uint64_t type, uint64_t value) { + // Size of EBML ID + int32_t ebml_size = GetUIntSize(type); + + // Datasize + ebml_size += GetUIntSize(value); + + // Size of Datasize + ebml_size++; + + return ebml_size; +} + +uint64_t EbmlElementSize(uint64_t type, float /* value */) { + // Size of EBML ID + uint64_t ebml_size = GetUIntSize(type); + + // Datasize + ebml_size += sizeof(float); + + // Size of Datasize + ebml_size++; + + return ebml_size; +} + +uint64_t EbmlElementSize(uint64_t type, const char* value) { + if (!value) + return 0; + + // Size of EBML ID + uint64_t ebml_size = GetUIntSize(type); + + // Datasize + ebml_size += strlen(value); + + // Size of Datasize + ebml_size++; + + return ebml_size; +} + +uint64_t EbmlElementSize(uint64_t type, const uint8_t* value, uint64_t size) { + if (!value) + return 0; + + // Size of EBML ID + uint64_t ebml_size = GetUIntSize(type); + + // Datasize + ebml_size += size; + + // Size of Datasize + ebml_size += GetCodedUIntSize(size); + + return ebml_size; +} + +uint64_t EbmlDateElementSize(uint64_t type) { + // Size of EBML ID + uint64_t ebml_size = GetUIntSize(type); + + // Datasize + ebml_size += kDateElementSize; + + // Size of Datasize + ebml_size++; + + return ebml_size; +} + +int32_t SerializeInt(IMkvWriter* writer, int64_t value, int32_t size) { + if (!writer || size < 1 || size > 8) + return -1; + + for (int32_t i = 1; i <= size; ++i) { + const int32_t byte_count = size - i; + const int32_t bit_count = byte_count * 8; + + const int64_t bb = value >> bit_count; + const uint8_t b = static_cast<uint8_t>(bb); + + const int32_t status = writer->Write(&b, 1); + + if (status < 0) + return status; + } + + return 0; +} + +int32_t SerializeFloat(IMkvWriter* writer, float f) { + if (!writer) + return -1; + + assert(sizeof(uint32_t) == sizeof(float)); + // This union is merely used to avoid a reinterpret_cast from float& to + // uint32& which will result in violation of strict aliasing. + union U32 { + uint32_t u32; + float f; + } value; + value.f = f; + + for (int32_t i = 1; i <= 4; ++i) { + const int32_t byte_count = 4 - i; + const int32_t bit_count = byte_count * 8; + + const uint8_t byte = static_cast<uint8_t>(value.u32 >> bit_count); + + const int32_t status = writer->Write(&byte, 1); + + if (status < 0) + return status; + } + + return 0; +} + +int32_t WriteUInt(IMkvWriter* writer, uint64_t value) { + if (!writer) + return -1; + + int32_t size = GetCodedUIntSize(value); + + return WriteUIntSize(writer, value, size); +} + +int32_t WriteUIntSize(IMkvWriter* writer, uint64_t value, int32_t size) { + if (!writer || size < 0 || size > 8) + return -1; + + if (size > 0) { + const uint64_t bit = 1LL << (size * 7); + + if (value > (bit - 2)) + return -1; + + value |= bit; + } else { + size = 1; + int64_t bit; + + for (;;) { + bit = 1LL << (size * 7); + const uint64_t max = bit - 2; + + if (value <= max) + break; + + ++size; + } + + if (size > 8) + return false; + + value |= bit; + } + + return SerializeInt(writer, value, size); +} + +int32_t WriteID(IMkvWriter* writer, uint64_t type) { + if (!writer) + return -1; + + writer->ElementStartNotify(type, writer->Position()); + + const int32_t size = GetUIntSize(type); + + return SerializeInt(writer, type, size); +} + +bool WriteEbmlMasterElement(IMkvWriter* writer, uint64_t type, uint64_t size) { + if (!writer) + return false; + + if (WriteID(writer, type)) + return false; + + if (WriteUInt(writer, size)) + return false; + + return true; +} + +bool WriteEbmlElement(IMkvWriter* writer, uint64_t type, uint64_t value) { + if (!writer) + return false; + + if (WriteID(writer, type)) + return false; + + const uint64_t size = GetUIntSize(value); + if (WriteUInt(writer, size)) + return false; + + if (SerializeInt(writer, value, static_cast<int32_t>(size))) + return false; + + return true; +} + +bool WriteEbmlElement(IMkvWriter* writer, uint64_t type, int64_t value) { + if (!writer) + return false; + + if (WriteID(writer, type)) + return 0; + + const uint64_t size = GetIntSize(value); + if (WriteUInt(writer, size)) + return false; + + if (SerializeInt(writer, value, static_cast<int32_t>(size))) + return false; + + return true; +} + +bool WriteEbmlElement(IMkvWriter* writer, uint64_t type, float value) { + if (!writer) + return false; + + if (WriteID(writer, type)) + return false; + + if (WriteUInt(writer, 4)) + return false; + + if (SerializeFloat(writer, value)) + return false; + + return true; +} + +bool WriteEbmlElement(IMkvWriter* writer, uint64_t type, const char* value) { + if (!writer || !value) + return false; + + if (WriteID(writer, type)) + return false; + + const uint64_t length = strlen(value); + if (WriteUInt(writer, length)) + return false; + + if (writer->Write(value, static_cast<const uint32_t>(length))) + return false; + + return true; +} + +bool WriteEbmlElement(IMkvWriter* writer, uint64_t type, const uint8_t* value, + uint64_t size) { + if (!writer || !value || size < 1) + return false; + + if (WriteID(writer, type)) + return false; + + if (WriteUInt(writer, size)) + return false; + + if (writer->Write(value, static_cast<uint32_t>(size))) + return false; + + return true; +} + +bool WriteEbmlDateElement(IMkvWriter* writer, uint64_t type, int64_t value) { + if (!writer) + return false; + + if (WriteID(writer, type)) + return false; + + if (WriteUInt(writer, kDateElementSize)) + return false; + + if (SerializeInt(writer, value, kDateElementSize)) + return false; + + return true; +} + +uint64_t WriteFrame(IMkvWriter* writer, const Frame* const frame, + Cluster* cluster) { + if (!writer || !frame || !frame->IsValid() || !cluster || + !cluster->timecode_scale()) + return 0; + + // Technically the timecode for a block can be less than the + // timecode for the cluster itself (remember that block timecode + // is a signed, 16-bit integer). However, as a simplification we + // only permit non-negative cluster-relative timecodes for blocks. + const int64_t relative_timecode = cluster->GetRelativeTimecode( + frame->timestamp() / cluster->timecode_scale()); + if (relative_timecode < 0 || relative_timecode > kMaxBlockTimecode) + return 0; + + return frame->CanBeSimpleBlock() ? + WriteSimpleBlock(writer, frame, relative_timecode) : + WriteBlock(writer, frame, relative_timecode, + cluster->timecode_scale()); +} + +uint64_t WriteVoidElement(IMkvWriter* writer, uint64_t size) { + if (!writer) + return false; + + // Subtract one for the void ID and the coded size. + uint64_t void_entry_size = size - 1 - GetCodedUIntSize(size - 1); + uint64_t void_size = + EbmlMasterElementSize(libwebm::kMkvVoid, void_entry_size) + + void_entry_size; + + if (void_size != size) + return 0; + + const int64_t payload_position = writer->Position(); + if (payload_position < 0) + return 0; + + if (WriteID(writer, libwebm::kMkvVoid)) + return 0; + + if (WriteUInt(writer, void_entry_size)) + return 0; + + const uint8_t value = 0; + for (int32_t i = 0; i < static_cast<int32_t>(void_entry_size); ++i) { + if (writer->Write(&value, 1)) + return 0; + } + + const int64_t stop_position = writer->Position(); + if (stop_position < 0 || + stop_position - payload_position != static_cast<int64_t>(void_size)) + return 0; + + return void_size; +} + +void GetVersion(int32_t* major, int32_t* minor, int32_t* build, + int32_t* revision) { + *major = 0; + *minor = 2; + *build = 1; + *revision = 0; +} + +uint64_t MakeUID(unsigned int* seed) { + uint64_t uid = 0; + +#ifdef __MINGW32__ + srand(*seed); +#endif + + for (int i = 0; i < 7; ++i) { // avoid problems with 8-byte values + uid <<= 8; + +// TODO(fgalligan): Move random number generation to platform specific code. +#ifdef _MSC_VER + (void)seed; + const int32_t nn = rand(); +#elif __ANDROID__ + int32_t temp_num = 1; + int fd = open("/dev/urandom", O_RDONLY); + if (fd != -1) { + read(fd, &temp_num, sizeof(int32)); + close(fd); + } + const int32_t nn = temp_num; +#elif defined __MINGW32__ + const int32_t nn = rand(); +#else + const int32_t nn = rand_r(seed); +#endif + const int32_t n = 0xFF & (nn >> 4); // throw away low-order bits + + uid |= n; + } + + return uid; +} + +} // namespace mkvmuxer
\ No newline at end of file |