
Wrapping mkvmuxer and mkvparser in the libwebm namespace is no longer necessary now that the tree reorganization is complete. Put mkvmuxer and mkvparser namespaces back in the global namespace to avoid unnecessary churn in downstream code. Change-Id: I13a4fe0143d20bb2bb6038078c68636ff2af0c29
640 lines
15 KiB
C++
640 lines
15 KiB
C++
// 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
|