Index: source/libvpx/third_party/libwebm/mkvmuxerutil.cpp |
diff --git a/source/libvpx/third_party/libwebm/mkvmuxerutil.cpp b/source/libvpx/third_party/libwebm/mkvmuxerutil.cpp |
index 3fb9bc9dc776ca49c8495fcf47db35e6bdbba168..27ab15d51f1ed4b51e822fc07f9484ea0c32ce69 100644 |
--- a/source/libvpx/third_party/libwebm/mkvmuxerutil.cpp |
+++ b/source/libvpx/third_party/libwebm/mkvmuxerutil.cpp |
@@ -15,18 +15,19 @@ |
#include <cassert> |
#include <cmath> |
#include <cstdio> |
-#ifdef _MSC_VER |
-#define _CRT_RAND_S |
-#endif |
#include <cstdlib> |
#include <cstring> |
#include <ctime> |
- |
#include <new> |
#include "mkvwriter.hpp" |
#include "webmids.hpp" |
+#ifdef _MSC_VER |
+// Disable MSVC warnings that suggest making code non-portable. |
+#pragma warning(disable : 4996) |
+#endif |
+ |
namespace mkvmuxer { |
namespace { |
@@ -34,6 +35,144 @@ namespace { |
// Date elements are always 8 octets in size. |
const int kDateElementSize = 8; |
+uint64 WriteBlock(IMkvWriter* writer, const Frame* const frame, int64 timecode, |
+ uint64 timecode_scale) { |
+ uint64 block_additional_elem_size = 0; |
+ uint64 block_addid_elem_size = 0; |
+ uint64 block_more_payload_size = 0; |
+ uint64 block_more_elem_size = 0; |
+ uint64 block_additions_payload_size = 0; |
+ uint64 block_additions_elem_size = 0; |
+ if (frame->additional()) { |
+ block_additional_elem_size = EbmlElementSize( |
+ kMkvBlockAdditional, frame->additional(), frame->additional_length()); |
+ block_addid_elem_size = EbmlElementSize(kMkvBlockAddID, frame->add_id()); |
+ |
+ block_more_payload_size = |
+ block_addid_elem_size + block_additional_elem_size; |
+ block_more_elem_size = |
+ EbmlMasterElementSize(kMkvBlockMore, block_more_payload_size) + |
+ block_more_payload_size; |
+ block_additions_payload_size = block_more_elem_size; |
+ block_additions_elem_size = |
+ EbmlMasterElementSize(kMkvBlockAdditions, |
+ block_additions_payload_size) + |
+ block_additions_payload_size; |
+ } |
+ |
+ uint64 discard_padding_elem_size = 0; |
+ if (frame->discard_padding() != 0) { |
+ discard_padding_elem_size = |
+ EbmlElementSize(kMkvDiscardPadding, frame->discard_padding()); |
+ } |
+ |
+ const uint64 reference_block_timestamp = |
+ frame->reference_block_timestamp() / timecode_scale; |
+ uint64 reference_block_elem_size = 0; |
+ if (!frame->is_key()) { |
+ reference_block_elem_size = |
+ EbmlElementSize(kMkvReferenceBlock, reference_block_timestamp); |
+ } |
+ |
+ const uint64 duration = frame->duration() / timecode_scale; |
+ uint64 block_duration_elem_size = 0; |
+ if (duration > 0) |
+ block_duration_elem_size = EbmlElementSize(kMkvBlockDuration, duration); |
+ |
+ const uint64 block_payload_size = 4 + frame->length(); |
+ const uint64 block_elem_size = |
+ EbmlMasterElementSize(kMkvBlock, block_payload_size) + block_payload_size; |
+ |
+ const uint64 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, kMkvBlockGroup, |
+ block_group_payload_size)) { |
+ return 0; |
+ } |
+ |
+ if (!WriteEbmlMasterElement(writer, 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>(frame->length()))) |
+ return 0; |
+ |
+ if (frame->additional()) { |
+ if (!WriteEbmlMasterElement(writer, kMkvBlockAdditions, |
+ block_additions_payload_size)) { |
+ return 0; |
+ } |
+ |
+ if (!WriteEbmlMasterElement(writer, kMkvBlockMore, block_more_payload_size)) |
+ return 0; |
+ |
+ if (!WriteEbmlElement(writer, kMkvBlockAddID, frame->add_id())) |
+ return 0; |
+ |
+ if (!WriteEbmlElement(writer, kMkvBlockAdditional, frame->additional(), |
+ frame->additional_length())) { |
+ return 0; |
+ } |
+ } |
+ |
+ if (frame->discard_padding() != 0 && |
+ !WriteEbmlElement(writer, kMkvDiscardPadding, frame->discard_padding())) { |
+ return false; |
+ } |
+ |
+ if (!frame->is_key() && |
+ !WriteEbmlElement(writer, kMkvReferenceBlock, |
+ reference_block_timestamp)) { |
+ return false; |
+ } |
+ |
+ if (duration > 0 && !WriteEbmlElement(writer, kMkvBlockDuration, duration)) { |
+ return false; |
+ } |
+ return EbmlMasterElementSize(kMkvBlockGroup, block_group_payload_size) + |
+ block_group_payload_size; |
+} |
+ |
+uint64 WriteSimpleBlock(IMkvWriter* writer, const Frame* const frame, |
+ int64 timecode) { |
+ if (WriteID(writer, kMkvSimpleBlock)) |
+ return 0; |
+ |
+ const int32 size = static_cast<int32>(frame->length()) + 4; |
+ if (WriteUInt(writer, size)) |
+ return 0; |
+ |
+ if (WriteUInt(writer, static_cast<uint64>(frame->track_number()))) |
+ return 0; |
+ |
+ if (SerializeInt(writer, timecode, 2)) |
+ return 0; |
+ |
+ uint64 flags = 0; |
+ if (frame->is_key()) |
+ flags |= 0x80; |
+ |
+ if (SerializeInt(writer, flags, 1)) |
+ return 0; |
+ |
+ if (writer->Write(frame->frame(), static_cast<uint32>(frame->length()))) |
+ return 0; |
+ |
+ return GetUIntSize(kMkvSimpleBlock) + GetCodedUIntSize(size) + 4 + |
+ frame->length(); |
+} |
+ |
} // namespace |
int32 GetCodedUIntSize(uint64 value) { |
@@ -72,6 +211,13 @@ int32 GetUIntSize(uint64 value) { |
return 8; |
} |
+int32 GetIntSize(int64 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 v = (value < 0) ? value ^ -1LL : value; |
+ return GetUIntSize(2 * v); |
+} |
+ |
uint64 EbmlMasterElementSize(uint64 type, uint64 value) { |
// Size of EBML ID |
int32 ebml_size = GetUIntSize(type); |
@@ -83,7 +229,16 @@ uint64 EbmlMasterElementSize(uint64 type, uint64 value) { |
} |
uint64 EbmlElementSize(uint64 type, int64 value) { |
- return EbmlElementSize(type, static_cast<uint64>(value)); |
+ // Size of EBML ID |
+ int32 ebml_size = GetUIntSize(type); |
+ |
+ // Datasize |
+ ebml_size += GetIntSize(value); |
+ |
+ // Size of Datasize |
+ ebml_size++; |
+ |
+ return ebml_size; |
} |
uint64 EbmlElementSize(uint64 type, uint64 value) { |
@@ -144,7 +299,7 @@ uint64 EbmlElementSize(uint64 type, const uint8* value, uint64 size) { |
return ebml_size; |
} |
-uint64 EbmlDateElementSize(uint64 type, int64 value) { |
+uint64 EbmlDateElementSize(uint64 type) { |
// Size of EBML ID |
uint64 ebml_size = GetUIntSize(type); |
@@ -289,6 +444,23 @@ bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value) { |
return true; |
} |
+bool WriteEbmlElement(IMkvWriter* writer, uint64 type, int64 value) { |
+ if (!writer) |
+ return false; |
+ |
+ if (WriteID(writer, type)) |
+ return 0; |
+ |
+ const uint64 size = GetIntSize(value); |
+ if (WriteUInt(writer, size)) |
+ return false; |
+ |
+ if (SerializeInt(writer, value, static_cast<int32>(size))) |
+ return false; |
+ |
+ return true; |
+} |
+ |
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, float value) { |
if (!writer) |
return false; |
@@ -355,289 +527,25 @@ bool WriteEbmlDateElement(IMkvWriter* writer, uint64 type, int64 value) { |
return true; |
} |
-uint64 WriteSimpleBlock(IMkvWriter* writer, const uint8* data, uint64 length, |
- uint64 track_number, int64 timecode, uint64 is_key) { |
- if (!writer) |
- return false; |
- |
- if (!data || length < 1) |
- return false; |
- |
- // Here we only permit track number values to be no greater than |
- // 126, which the largest value we can store having a Matroska |
- // integer representation of only 1 byte. |
- |
- if (track_number < 1 || track_number > 126) |
- return false; |
- |
- // Technically the timestamp for a block can be less than the |
- // timestamp for the cluster itself (remember that block timestamp |
- // is a signed, 16-bit integer). However, as a simplification we |
- // only permit non-negative cluster-relative timestamps for blocks. |
- |
- if (timecode < 0 || timecode > kMaxBlockTimecode) |
- return false; |
- |
- if (WriteID(writer, kMkvSimpleBlock)) |
- return 0; |
- |
- const int32 size = static_cast<int32>(length) + 4; |
- if (WriteUInt(writer, size)) |
- return 0; |
- |
- if (WriteUInt(writer, static_cast<uint64>(track_number))) |
- return 0; |
- |
- if (SerializeInt(writer, timecode, 2)) |
- return 0; |
- |
- uint64 flags = 0; |
- if (is_key) |
- flags |= 0x80; |
- |
- if (SerializeInt(writer, flags, 1)) |
- return 0; |
- |
- if (writer->Write(data, static_cast<uint32>(length))) |
- return 0; |
- |
- const uint64 element_size = |
- GetUIntSize(kMkvSimpleBlock) + GetCodedUIntSize(size) + 4 + length; |
- |
- return element_size; |
-} |
- |
-// We must write the metadata (key)frame as a BlockGroup element, |
-// because we need to specify a duration for the frame. The |
-// BlockGroup element comprises the frame itself and its duration, |
-// and is laid out as follows: |
-// |
-// BlockGroup tag |
-// BlockGroup size |
-// Block tag |
-// Block size |
-// (the frame is the block payload) |
-// Duration tag |
-// Duration size |
-// (duration payload) |
-// |
-uint64 WriteMetadataBlock(IMkvWriter* writer, const uint8* data, uint64 length, |
- uint64 track_number, int64 timecode, |
- uint64 duration) { |
- // We don't backtrack when writing to the stream, so we must |
- // pre-compute the BlockGroup size, by summing the sizes of each |
- // sub-element (the block and the duration). |
- |
- // We use a single byte for the track number of the block, which |
- // means the block header is exactly 4 bytes. |
- |
- // TODO(matthewjheaney): use EbmlMasterElementSize and WriteEbmlMasterElement |
- |
- const uint64 block_payload_size = 4 + length; |
- const int32 block_size = GetCodedUIntSize(block_payload_size); |
- const uint64 block_elem_size = 1 + block_size + block_payload_size; |
- |
- const int32 duration_payload_size = GetUIntSize(duration); |
- const int32 duration_size = GetCodedUIntSize(duration_payload_size); |
- const uint64 duration_elem_size = 1 + duration_size + duration_payload_size; |
- |
- const uint64 blockg_payload_size = block_elem_size + duration_elem_size; |
- const int32 blockg_size = GetCodedUIntSize(blockg_payload_size); |
- const uint64 blockg_elem_size = 1 + blockg_size + blockg_payload_size; |
- |
- if (WriteID(writer, kMkvBlockGroup)) // 1-byte ID size |
- return 0; |
- |
- if (WriteUInt(writer, blockg_payload_size)) |
- return 0; |
- |
- // Write Block element |
- |
- if (WriteID(writer, kMkvBlock)) // 1-byte ID size |
- return 0; |
- |
- if (WriteUInt(writer, block_payload_size)) |
- return 0; |
- |
- // Byte 1 of 4 |
- |
- if (WriteUInt(writer, track_number)) |
- return 0; |
- |
- // Bytes 2 & 3 of 4 |
- |
- if (SerializeInt(writer, timecode, 2)) |
- return 0; |
- |
- // Byte 4 of 4 |
- |
- const uint64 flags = 0; |
- |
- if (SerializeInt(writer, flags, 1)) |
- return 0; |
- |
- // Now write the actual frame (of metadata) |
- |
- if (writer->Write(data, static_cast<uint32>(length))) |
- return 0; |
- |
- // Write Duration element |
- |
- if (WriteID(writer, kMkvBlockDuration)) // 1-byte ID size |
- return 0; |
- |
- if (WriteUInt(writer, duration_payload_size)) |
- return 0; |
- |
- if (SerializeInt(writer, duration, duration_payload_size)) |
- return 0; |
- |
- // Note that we don't write a reference time as part of the block |
- // group; no reference time(s) indicates that this block is a |
- // keyframe. (Unlike the case for a SimpleBlock element, the header |
- // bits of the Block sub-element of a BlockGroup element do not |
- // indicate keyframe status. The keyframe status is inferred from |
- // the absence of reference time sub-elements.) |
- |
- return blockg_elem_size; |
-} |
- |
-// Writes a WebM BlockGroup with BlockAdditional data. The structure is as |
-// follows: |
-// Indentation shows sub-levels |
-// BlockGroup |
-// Block |
-// Data |
-// BlockAdditions |
-// BlockMore |
-// BlockAddID |
-// 1 (Denotes Alpha) |
-// BlockAdditional |
-// Data |
-uint64 WriteBlockWithAdditional(IMkvWriter* writer, const uint8* data, |
- uint64 length, const uint8* additional, |
- uint64 additional_length, uint64 add_id, |
- uint64 track_number, int64 timecode, |
- uint64 is_key) { |
- if (!data || !additional || length < 1 || additional_length < 1) |
+uint64 WriteFrame(IMkvWriter* writer, const Frame* const frame, |
+ Cluster* cluster) { |
+ if (!writer || !frame || !frame->IsValid() || !cluster || |
+ !cluster->timecode_scale()) |
return 0; |
- const uint64 block_payload_size = 4 + length; |
- const uint64 block_elem_size = |
- EbmlMasterElementSize(kMkvBlock, block_payload_size) + block_payload_size; |
- const uint64 block_additional_elem_size = |
- EbmlElementSize(kMkvBlockAdditional, additional, additional_length); |
- const uint64 block_addid_elem_size = EbmlElementSize(kMkvBlockAddID, add_id); |
- |
- const uint64 block_more_payload_size = |
- block_addid_elem_size + block_additional_elem_size; |
- const uint64 block_more_elem_size = |
- EbmlMasterElementSize(kMkvBlockMore, block_more_payload_size) + |
- block_more_payload_size; |
- const uint64 block_additions_payload_size = block_more_elem_size; |
- const uint64 block_additions_elem_size = |
- EbmlMasterElementSize(kMkvBlockAdditions, block_additions_payload_size) + |
- block_additions_payload_size; |
- const uint64 block_group_payload_size = |
- block_elem_size + block_additions_elem_size; |
- const uint64 block_group_elem_size = |
- EbmlMasterElementSize(kMkvBlockGroup, block_group_payload_size) + |
- block_group_payload_size; |
- |
- if (!WriteEbmlMasterElement(writer, kMkvBlockGroup, block_group_payload_size)) |
- return 0; |
- |
- if (!WriteEbmlMasterElement(writer, kMkvBlock, block_payload_size)) |
- return 0; |
- |
- if (WriteUInt(writer, track_number)) |
- return 0; |
- |
- if (SerializeInt(writer, timecode, 2)) |
- return 0; |
- |
- uint64 flags = 0; |
- if (is_key) |
- flags |= 0x80; |
- if (SerializeInt(writer, flags, 1)) |
- return 0; |
- |
- if (writer->Write(data, static_cast<uint32>(length))) |
- return 0; |
- |
- if (!WriteEbmlMasterElement(writer, kMkvBlockAdditions, |
- block_additions_payload_size)) |
- return 0; |
- |
- if (!WriteEbmlMasterElement(writer, kMkvBlockMore, block_more_payload_size)) |
- return 0; |
- |
- if (!WriteEbmlElement(writer, kMkvBlockAddID, add_id)) |
- return 0; |
- |
- if (!WriteEbmlElement(writer, kMkvBlockAdditional, additional, |
- additional_length)) |
- return 0; |
- |
- return block_group_elem_size; |
-} |
- |
-// Writes a WebM BlockGroup with DiscardPadding. The structure is as follows: |
-// Indentation shows sub-levels |
-// BlockGroup |
-// Block |
-// Data |
-// DiscardPadding |
-uint64 WriteBlockWithDiscardPadding(IMkvWriter* writer, const uint8* data, |
- uint64 length, int64 discard_padding, |
- uint64 track_number, int64 timecode, |
- uint64 is_key) { |
- if (!data || length < 1 || discard_padding <= 0) |
- return 0; |
- |
- const uint64 block_payload_size = 4 + length; |
- const uint64 block_elem_size = |
- EbmlMasterElementSize(kMkvBlock, block_payload_size) + block_payload_size; |
- const uint64 discard_padding_elem_size = |
- EbmlElementSize(kMkvDiscardPadding, discard_padding); |
- const uint64 block_group_payload_size = |
- block_elem_size + discard_padding_elem_size; |
- const uint64 block_group_elem_size = |
- EbmlMasterElementSize(kMkvBlockGroup, block_group_payload_size) + |
- block_group_payload_size; |
- |
- if (!WriteEbmlMasterElement(writer, kMkvBlockGroup, block_group_payload_size)) |
- return 0; |
- |
- if (!WriteEbmlMasterElement(writer, kMkvBlock, block_payload_size)) |
- return 0; |
- |
- if (WriteUInt(writer, track_number)) |
- return 0; |
- |
- if (SerializeInt(writer, timecode, 2)) |
- return 0; |
- |
- uint64 flags = 0; |
- if (is_key) |
- flags |= 0x80; |
- if (SerializeInt(writer, flags, 1)) |
- return 0; |
- |
- if (writer->Write(data, static_cast<uint32>(length))) |
- return 0; |
- |
- if (WriteID(writer, kMkvDiscardPadding)) |
+ // 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 relative_timecode = cluster->GetRelativeTimecode( |
+ frame->timestamp() / cluster->timecode_scale()); |
+ if (relative_timecode < 0 || relative_timecode > kMaxBlockTimecode) |
return 0; |
- const uint64 size = GetUIntSize(discard_padding); |
- if (WriteUInt(writer, size)) |
- return false; |
- |
- if (SerializeInt(writer, discard_padding, static_cast<int32>(size))) |
- return false; |
- |
- return block_group_elem_size; |
+ return frame->CanBeSimpleBlock() ? |
+ WriteSimpleBlock(writer, frame, relative_timecode) : |
+ WriteBlock(writer, frame, relative_timecode, |
+ cluster->timecode_scale()); |
} |
uint64 WriteVoidElement(IMkvWriter* writer, uint64 size) { |
@@ -698,10 +606,7 @@ mkvmuxer::uint64 mkvmuxer::MakeUID(unsigned int* seed) { |
// TODO(fgalligan): Move random number generation to platform specific code. |
#ifdef _MSC_VER |
(void)seed; |
- unsigned int random_value; |
- const errno_t e = rand_s(&random_value); |
- (void)e; |
- const int32 nn = random_value; |
+ const int32 nn = rand(); |
#elif __ANDROID__ |
int32 temp_num = 1; |
int fd = open("/dev/urandom", O_RDONLY); |