(61d00a474) v0.9.7.1

This commit is contained in:
Regalis
2020-03-04 13:04:10 +01:00
parent 3c50efa5c9
commit 3c09ebe02f
5086 changed files with 786063 additions and 295871 deletions
@@ -0,0 +1,62 @@
#!/bin/sh
##
## Copyright (c) 2015 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.
##
set -e
devnull='> /dev/null 2>&1'
readonly ORIG_PWD="$(pwd)"
elog() {
echo "${0##*/} failed because: $@" 1>&2
}
vlog() {
if [ "${VERBOSE}" = "yes" ]; then
echo "$@"
fi
}
# Terminates script when name of current directory does not match $1.
check_dir() {
current_dir="$(pwd)"
required_dir="$1"
if [ "${current_dir##*/}" != "${required_dir}" ]; then
elog "This script must be run from the ${required_dir} directory."
exit 1
fi
}
# Terminates the script when $1 is not in $PATH. Any arguments required for
# the tool being tested to return a successful exit code can be passed as
# additional arguments.
check_tool() {
tool="$1"
shift
tool_args="$@"
if ! eval "${tool}" ${tool_args} > /dev/null 2>&1; then
elog "${tool} must be in your path."
exit 1
fi
}
# Echoes git describe output for the directory specified by $1 to stdout.
git_describe() {
git_dir="$1"
check_git
echo $(git -C "${git_dir}" describe)
}
# Echoes current git revision for the directory specifed by $1 to stdout.
git_revision() {
git_dir="$1"
check_git
echo $(git -C "${git_dir}" rev-parse HEAD)
}
@@ -0,0 +1,93 @@
// Copyright (c) 2016 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 "common/file_util.h"
#include <sys/stat.h>
#ifndef _MSC_VER
#include <unistd.h> // close()
#endif
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <ios>
#include <string>
namespace libwebm {
std::string GetTempFileName() {
#if !defined _MSC_VER && !defined __MINGW32__
std::string temp_file_name_template_str =
std::string(std::getenv("TEST_TMPDIR") ? std::getenv("TEST_TMPDIR")
: ".") +
"/libwebm_temp.XXXXXX";
char* temp_file_name_template =
new char[temp_file_name_template_str.length() + 1];
memset(temp_file_name_template, 0, temp_file_name_template_str.length() + 1);
temp_file_name_template_str.copy(temp_file_name_template,
temp_file_name_template_str.length(), 0);
int fd = mkstemp(temp_file_name_template);
std::string temp_file_name =
(fd != -1) ? std::string(temp_file_name_template) : std::string();
delete[] temp_file_name_template;
if (fd != -1) {
close(fd);
}
return temp_file_name;
#else
char tmp_file_name[_MAX_PATH];
#if defined _MSC_VER || defined MINGW_HAS_SECURE_API
errno_t err = tmpnam_s(tmp_file_name);
#else
char* fname_pointer = tmpnam(tmp_file_name);
int err = (fname_pointer == &tmp_file_name[0]) ? 0 : -1;
#endif
if (err == 0) {
return std::string(tmp_file_name);
}
return std::string();
#endif
}
uint64_t GetFileSize(const std::string& file_name) {
uint64_t file_size = 0;
#ifndef _MSC_VER
struct stat st;
st.st_size = 0;
if (stat(file_name.c_str(), &st) == 0) {
#else
struct _stat st;
st.st_size = 0;
if (_stat(file_name.c_str(), &st) == 0) {
#endif
file_size = st.st_size;
}
return file_size;
}
bool GetFileContents(const std::string& file_name, std::string* contents) {
std::ifstream file(file_name.c_str());
*contents = std::string(static_cast<size_t>(GetFileSize(file_name)), 0);
if (file.good() && contents->size()) {
file.read(&(*contents)[0], contents->size());
}
return !file.fail();
}
TempFileDeleter::TempFileDeleter() { file_name_ = GetTempFileName(); }
TempFileDeleter::~TempFileDeleter() {
std::ifstream file(file_name_.c_str());
if (file.good()) {
file.close();
std::remove(file_name_.c_str());
}
}
} // namespace libwebm
@@ -0,0 +1,44 @@
// Copyright (c) 2016 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.
#ifndef LIBWEBM_COMMON_FILE_UTIL_H_
#define LIBWEBM_COMMON_FILE_UTIL_H_
#include <stdint.h>
#include <string>
#include "mkvmuxer/mkvmuxertypes.h" // LIBWEBM_DISALLOW_COPY_AND_ASSIGN()
namespace libwebm {
// Returns a temporary file name.
std::string GetTempFileName();
// Returns size of file specified by |file_name|, or 0 upon failure.
uint64_t GetFileSize(const std::string& file_name);
// Gets the contents file_name as a string. Returns false on error.
bool GetFileContents(const std::string& file_name, std::string* contents);
// Manages life of temporary file specified at time of construction. Deletes
// file upon destruction.
class TempFileDeleter {
public:
TempFileDeleter();
explicit TempFileDeleter(std::string file_name) : file_name_(file_name) {}
~TempFileDeleter();
const std::string& name() const { return file_name_; }
private:
std::string file_name_;
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(TempFileDeleter);
};
} // namespace libwebm
#endif // LIBWEBM_COMMON_FILE_UTIL_H_
@@ -0,0 +1,220 @@
// Copyright (c) 2016 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 "hdr_util.h"
#include <climits>
#include <cstddef>
#include <new>
#include "mkvparser/mkvparser.h"
namespace libwebm {
const int Vp9CodecFeatures::kValueNotPresent = INT_MAX;
bool CopyPrimaryChromaticity(const mkvparser::PrimaryChromaticity& parser_pc,
PrimaryChromaticityPtr* muxer_pc) {
muxer_pc->reset(new (std::nothrow)
mkvmuxer::PrimaryChromaticity(parser_pc.x, parser_pc.y));
if (!muxer_pc->get())
return false;
return true;
}
bool MasteringMetadataValuePresent(double value) {
return value != mkvparser::MasteringMetadata::kValueNotPresent;
}
bool CopyMasteringMetadata(const mkvparser::MasteringMetadata& parser_mm,
mkvmuxer::MasteringMetadata* muxer_mm) {
if (MasteringMetadataValuePresent(parser_mm.luminance_max))
muxer_mm->set_luminance_max(parser_mm.luminance_max);
if (MasteringMetadataValuePresent(parser_mm.luminance_min))
muxer_mm->set_luminance_min(parser_mm.luminance_min);
PrimaryChromaticityPtr r_ptr(nullptr);
PrimaryChromaticityPtr g_ptr(nullptr);
PrimaryChromaticityPtr b_ptr(nullptr);
PrimaryChromaticityPtr wp_ptr(nullptr);
if (parser_mm.r) {
if (!CopyPrimaryChromaticity(*parser_mm.r, &r_ptr))
return false;
}
if (parser_mm.g) {
if (!CopyPrimaryChromaticity(*parser_mm.g, &g_ptr))
return false;
}
if (parser_mm.b) {
if (!CopyPrimaryChromaticity(*parser_mm.b, &b_ptr))
return false;
}
if (parser_mm.white_point) {
if (!CopyPrimaryChromaticity(*parser_mm.white_point, &wp_ptr))
return false;
}
if (!muxer_mm->SetChromaticity(r_ptr.get(), g_ptr.get(), b_ptr.get(),
wp_ptr.get())) {
return false;
}
return true;
}
bool ColourValuePresent(long long value) {
return value != mkvparser::Colour::kValueNotPresent;
}
bool CopyColour(const mkvparser::Colour& parser_colour,
mkvmuxer::Colour* muxer_colour) {
if (!muxer_colour)
return false;
if (ColourValuePresent(parser_colour.matrix_coefficients))
muxer_colour->set_matrix_coefficients(parser_colour.matrix_coefficients);
if (ColourValuePresent(parser_colour.bits_per_channel))
muxer_colour->set_bits_per_channel(parser_colour.bits_per_channel);
if (ColourValuePresent(parser_colour.chroma_subsampling_horz)) {
muxer_colour->set_chroma_subsampling_horz(
parser_colour.chroma_subsampling_horz);
}
if (ColourValuePresent(parser_colour.chroma_subsampling_vert)) {
muxer_colour->set_chroma_subsampling_vert(
parser_colour.chroma_subsampling_vert);
}
if (ColourValuePresent(parser_colour.cb_subsampling_horz))
muxer_colour->set_cb_subsampling_horz(parser_colour.cb_subsampling_horz);
if (ColourValuePresent(parser_colour.cb_subsampling_vert))
muxer_colour->set_cb_subsampling_vert(parser_colour.cb_subsampling_vert);
if (ColourValuePresent(parser_colour.chroma_siting_horz))
muxer_colour->set_chroma_siting_horz(parser_colour.chroma_siting_horz);
if (ColourValuePresent(parser_colour.chroma_siting_vert))
muxer_colour->set_chroma_siting_vert(parser_colour.chroma_siting_vert);
if (ColourValuePresent(parser_colour.range))
muxer_colour->set_range(parser_colour.range);
if (ColourValuePresent(parser_colour.transfer_characteristics)) {
muxer_colour->set_transfer_characteristics(
parser_colour.transfer_characteristics);
}
if (ColourValuePresent(parser_colour.primaries))
muxer_colour->set_primaries(parser_colour.primaries);
if (ColourValuePresent(parser_colour.max_cll))
muxer_colour->set_max_cll(parser_colour.max_cll);
if (ColourValuePresent(parser_colour.max_fall))
muxer_colour->set_max_fall(parser_colour.max_fall);
if (parser_colour.mastering_metadata) {
mkvmuxer::MasteringMetadata muxer_mm;
if (!CopyMasteringMetadata(*parser_colour.mastering_metadata, &muxer_mm))
return false;
if (!muxer_colour->SetMasteringMetadata(muxer_mm))
return false;
}
return true;
}
// Format of VPx private data:
//
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ID Byte | Length | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
// | |
// : Bytes 1..Length of Codec Feature :
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// ID Byte Format
// ID byte is an unsigned byte.
// 0 1 2 3 4 5 6 7
// +-+-+-+-+-+-+-+-+
// |X| ID |
// +-+-+-+-+-+-+-+-+
//
// The X bit is reserved.
//
// See the following link for more information:
// http://www.webmproject.org/vp9/profiles/
bool ParseVpxCodecPrivate(const uint8_t* private_data, int32_t length,
Vp9CodecFeatures* features) {
const int kVpxCodecPrivateMinLength = 3;
if (!private_data || !features || length < kVpxCodecPrivateMinLength)
return false;
const uint8_t kVp9ProfileId = 1;
const uint8_t kVp9LevelId = 2;
const uint8_t kVp9BitDepthId = 3;
const uint8_t kVp9ChromaSubsamplingId = 4;
const int kVpxFeatureLength = 1;
int offset = 0;
// Set features to not set.
features->profile = Vp9CodecFeatures::kValueNotPresent;
features->level = Vp9CodecFeatures::kValueNotPresent;
features->bit_depth = Vp9CodecFeatures::kValueNotPresent;
features->chroma_subsampling = Vp9CodecFeatures::kValueNotPresent;
do {
const uint8_t id_byte = private_data[offset++];
const uint8_t length_byte = private_data[offset++];
if (length_byte != kVpxFeatureLength)
return false;
if (id_byte == kVp9ProfileId) {
const int priv_profile = static_cast<int>(private_data[offset++]);
if (priv_profile < 0 || priv_profile > 3)
return false;
if (features->profile != Vp9CodecFeatures::kValueNotPresent &&
features->profile != priv_profile) {
return false;
}
features->profile = priv_profile;
} else if (id_byte == kVp9LevelId) {
const int priv_level = static_cast<int>(private_data[offset++]);
const int kNumLevels = 14;
const int levels[kNumLevels] = {10, 11, 20, 21, 30, 31, 40,
41, 50, 51, 52, 60, 61, 62};
for (int i = 0; i < kNumLevels; ++i) {
if (priv_level == levels[i]) {
if (features->level != Vp9CodecFeatures::kValueNotPresent &&
features->level != priv_level) {
return false;
}
features->level = priv_level;
break;
}
}
if (features->level == Vp9CodecFeatures::kValueNotPresent)
return false;
} else if (id_byte == kVp9BitDepthId) {
const int priv_profile = static_cast<int>(private_data[offset++]);
if (priv_profile != 8 && priv_profile != 10 && priv_profile != 12)
return false;
if (features->bit_depth != Vp9CodecFeatures::kValueNotPresent &&
features->bit_depth != priv_profile) {
return false;
}
features->bit_depth = priv_profile;
} else if (id_byte == kVp9ChromaSubsamplingId) {
const int priv_profile = static_cast<int>(private_data[offset++]);
if (priv_profile != 0 && priv_profile != 2 && priv_profile != 3)
return false;
if (features->chroma_subsampling != Vp9CodecFeatures::kValueNotPresent &&
features->chroma_subsampling != priv_profile) {
return false;
}
features->chroma_subsampling = priv_profile;
} else {
// Invalid ID.
return false;
}
} while (offset + kVpxCodecPrivateMinLength <= length);
return true;
}
} // namespace libwebm
@@ -0,0 +1,71 @@
// Copyright (c) 2016 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.
#ifndef LIBWEBM_COMMON_HDR_UTIL_H_
#define LIBWEBM_COMMON_HDR_UTIL_H_
#include <stdint.h>
#include <memory>
#include "mkvmuxer/mkvmuxer.h"
namespace mkvparser {
struct Colour;
struct MasteringMetadata;
struct PrimaryChromaticity;
} // namespace mkvparser
namespace libwebm {
// Utility types and functions for working with the Colour element and its
// children. Copiers return true upon success. Presence functions return true
// when the specified element is present.
// TODO(tomfinegan): These should be moved to libwebm_utils once c++11 is
// required by libwebm.
// Features of the VP9 codec that may be set in the CodecPrivate of a VP9 video
// stream. A value of kValueNotPresent represents that the value was not set in
// the CodecPrivate.
struct Vp9CodecFeatures {
static const int kValueNotPresent;
Vp9CodecFeatures()
: profile(kValueNotPresent),
level(kValueNotPresent),
bit_depth(kValueNotPresent),
chroma_subsampling(kValueNotPresent) {}
~Vp9CodecFeatures() {}
int profile;
int level;
int bit_depth;
int chroma_subsampling;
};
typedef std::unique_ptr<mkvmuxer::PrimaryChromaticity> PrimaryChromaticityPtr;
bool CopyPrimaryChromaticity(const mkvparser::PrimaryChromaticity& parser_pc,
PrimaryChromaticityPtr* muxer_pc);
bool MasteringMetadataValuePresent(double value);
bool CopyMasteringMetadata(const mkvparser::MasteringMetadata& parser_mm,
mkvmuxer::MasteringMetadata* muxer_mm);
bool ColourValuePresent(long long value);
bool CopyColour(const mkvparser::Colour& parser_colour,
mkvmuxer::Colour* muxer_colour);
// Returns true if |features| is set to one or more valid values.
bool ParseVpxCodecPrivate(const uint8_t* private_data, int32_t length,
Vp9CodecFeatures* features);
} // namespace libwebm
#endif // LIBWEBM_COMMON_HDR_UTIL_H_
@@ -0,0 +1,29 @@
/*
* 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 "common/indent.h"
#include <string>
namespace libwebm {
Indent::Indent(int indent) : indent_(indent), indent_str_() { Update(); }
void Indent::Adjust(int indent) {
indent_ += indent;
if (indent_ < 0)
indent_ = 0;
Update();
}
void Indent::Update() { indent_str_ = std::string(indent_, ' '); }
} // namespace libwebm
@@ -0,0 +1,48 @@
/*
* 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.
*/
#ifndef LIBWEBM_COMMON_INDENT_H_
#define LIBWEBM_COMMON_INDENT_H_
#include <string>
#include "mkvmuxer/mkvmuxertypes.h"
namespace libwebm {
const int kIncreaseIndent = 2;
const int kDecreaseIndent = -2;
// Used for formatting output so objects only have to keep track of spacing
// within their scope.
class Indent {
public:
explicit Indent(int indent);
// Changes the number of spaces output. The value adjusted is relative to
// |indent_|.
void Adjust(int indent);
std::string indent_str() const { return indent_str_; }
private:
// Called after |indent_| is changed. This will set |indent_str_| to the
// proper number of spaces.
void Update();
int indent_;
std::string indent_str_;
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Indent);
};
} // namespace libwebm
#endif // LIBWEBM_COMMON_INDENT_H_
@@ -0,0 +1,110 @@
// Copyright (c) 2015 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 "common/libwebm_util.h"
#include <cassert>
#include <cstdint>
#include <cstdio>
#include <limits>
namespace libwebm {
std::int64_t NanosecondsTo90KhzTicks(std::int64_t nanoseconds) {
const double pts_seconds = nanoseconds / kNanosecondsPerSecond;
return static_cast<std::int64_t>(pts_seconds * 90000);
}
std::int64_t Khz90TicksToNanoseconds(std::int64_t ticks) {
const double seconds = ticks / 90000.0;
return static_cast<std::int64_t>(seconds * kNanosecondsPerSecond);
}
bool ParseVP9SuperFrameIndex(const std::uint8_t* frame,
std::size_t frame_length, Ranges* frame_ranges,
bool* error) {
if (frame == nullptr || frame_length == 0 || frame_ranges == nullptr ||
error == nullptr) {
return false;
}
bool parse_ok = false;
const std::uint8_t marker = frame[frame_length - 1];
const std::uint32_t kHasSuperFrameIndexMask = 0xe0;
const std::uint32_t kSuperFrameMarker = 0xc0;
const std::uint32_t kLengthFieldSizeMask = 0x3;
if ((marker & kHasSuperFrameIndexMask) == kSuperFrameMarker) {
const std::uint32_t kFrameCountMask = 0x7;
const int num_frames = (marker & kFrameCountMask) + 1;
const int length_field_size = ((marker >> 3) & kLengthFieldSizeMask) + 1;
const std::size_t index_length = 2 + length_field_size * num_frames;
if (frame_length < index_length) {
std::fprintf(stderr,
"VP9ParseSuperFrameIndex: Invalid superframe index size.\n");
*error = true;
return false;
}
// Consume the super frame index. Note: it's at the end of the super frame.
const std::size_t length = frame_length - index_length;
if (length >= index_length &&
frame[frame_length - index_length] == marker) {
// Found a valid superframe index.
const std::uint8_t* byte = frame + length + 1;
std::size_t frame_offset = 0;
for (int i = 0; i < num_frames; ++i) {
std::uint32_t child_frame_length = 0;
for (int j = 0; j < length_field_size; ++j) {
child_frame_length |= (*byte++) << (j * 8);
}
if (length - frame_offset < child_frame_length) {
std::fprintf(stderr,
"ParseVP9SuperFrameIndex: Invalid superframe, sub frame "
"larger than entire frame.\n");
*error = true;
return false;
}
frame_ranges->push_back(Range(frame_offset, child_frame_length));
frame_offset += child_frame_length;
}
if (static_cast<int>(frame_ranges->size()) != num_frames) {
std::fprintf(stderr, "VP9Parse: superframe index parse failed.\n");
*error = true;
return false;
}
parse_ok = true;
} else {
std::fprintf(stderr, "VP9Parse: Invalid superframe index.\n");
*error = true;
}
}
return parse_ok;
}
bool WriteUint8(std::uint8_t val, std::FILE* fileptr) {
if (fileptr == nullptr)
return false;
return (std::fputc(val, fileptr) == val);
}
std::uint16_t ReadUint16(const std::uint8_t* buf) {
if (buf == nullptr)
return 0;
return ((buf[0] << 8) | buf[1]);
}
} // namespace libwebm
@@ -0,0 +1,65 @@
// Copyright (c) 2015 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.
#ifndef LIBWEBM_COMMON_LIBWEBM_UTIL_H_
#define LIBWEBM_COMMON_LIBWEBM_UTIL_H_
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <memory>
#include <vector>
namespace libwebm {
const double kNanosecondsPerSecond = 1000000000.0;
// fclose functor for wrapping FILE in std::unique_ptr.
// TODO(tomfinegan): Move this to file_util once c++11 restrictions are
// relaxed.
struct FILEDeleter {
int operator()(std::FILE* f) {
if (f != nullptr)
return fclose(f);
return 0;
}
};
typedef std::unique_ptr<std::FILE, FILEDeleter> FilePtr;
struct Range {
Range(std::size_t off, std::size_t len) : offset(off), length(len) {}
Range() = delete;
Range(const Range&) = default;
Range(Range&&) = default;
~Range() = default;
const std::size_t offset;
const std::size_t length;
};
typedef std::vector<Range> Ranges;
// Converts |nanoseconds| to 90000 Hz clock ticks and vice versa. Each return
// the converted value.
std::int64_t NanosecondsTo90KhzTicks(std::int64_t nanoseconds);
std::int64_t Khz90TicksToNanoseconds(std::int64_t khz90_ticks);
// Returns true and stores frame offsets and lengths in |frame_ranges| when
// |frame| has a valid VP9 super frame index. Sets |error| to true when parsing
// fails for any reason.
bool ParseVP9SuperFrameIndex(const std::uint8_t* frame,
std::size_t frame_length, Ranges* frame_ranges,
bool* error);
// Writes |val| to |fileptr| and returns true upon success.
bool WriteUint8(std::uint8_t val, std::FILE* fileptr);
// Reads 2 bytes from |buf| and returns them as a uint16_t. Returns 0 when |buf|
// is a nullptr.
std::uint16_t ReadUint16(const std::uint8_t* buf);
} // namespace libwebm
#endif // LIBWEBM_COMMON_LIBWEBM_UTIL_H_
@@ -0,0 +1,45 @@
// Copyright (c) 2016 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 "common/video_frame.h"
#include <cstdio>
namespace libwebm {
bool VideoFrame::Buffer::Init(std::size_t new_length) {
capacity = 0;
length = 0;
data.reset(new std::uint8_t[new_length]);
if (data.get() == nullptr) {
fprintf(stderr, "VideoFrame: Out of memory.");
return false;
}
capacity = new_length;
length = 0;
return true;
}
bool VideoFrame::Init(std::size_t length) { return buffer_.Init(length); }
bool VideoFrame::Init(std::size_t length, std::int64_t nano_pts, Codec codec) {
nanosecond_pts_ = nano_pts;
codec_ = codec;
return Init(length);
}
bool VideoFrame::SetBufferLength(std::size_t length) {
if (length > buffer_.capacity || buffer_.data.get() == nullptr)
return false;
buffer_.length = length;
return true;
}
} // namespace libwebm
@@ -0,0 +1,68 @@
// Copyright (c) 2016 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.
#ifndef LIBWEBM_COMMON_VIDEO_FRAME_H_
#define LIBWEBM_COMMON_VIDEO_FRAME_H_
#include <cstdint>
#include <memory>
namespace libwebm {
// VideoFrame is a storage class for compressed video frames.
class VideoFrame {
public:
enum Codec { kVP8, kVP9 };
struct Buffer {
Buffer() = default;
~Buffer() = default;
// Resets |data| to be of size |new_length| bytes, sets |capacity| to
// |new_length|, sets |length| to 0 (aka empty). Returns true for success.
bool Init(std::size_t new_length);
std::unique_ptr<std::uint8_t[]> data;
std::size_t length = 0;
std::size_t capacity = 0;
};
VideoFrame() = default;
~VideoFrame() = default;
VideoFrame(std::int64_t pts_in_nanoseconds, Codec vpx_codec)
: nanosecond_pts_(pts_in_nanoseconds), codec_(vpx_codec) {}
VideoFrame(bool keyframe, std::int64_t pts_in_nanoseconds, Codec vpx_codec)
: keyframe_(keyframe),
nanosecond_pts_(pts_in_nanoseconds),
codec_(vpx_codec) {}
bool Init(std::size_t length);
bool Init(std::size_t length, std::int64_t nano_pts, Codec codec);
// Updates actual length of data stored in |buffer_.data| when it's been
// written via the raw pointer returned from buffer_.data.get().
// Returns false when buffer_.data.get() return nullptr and/or when
// |length| > |buffer_.length|. Returns true otherwise.
bool SetBufferLength(std::size_t length);
// Accessors.
const Buffer& buffer() const { return buffer_; }
bool keyframe() const { return keyframe_; }
std::int64_t nanosecond_pts() const { return nanosecond_pts_; }
Codec codec() const { return codec_; }
// Mutators.
void set_nanosecond_pts(std::int64_t nano_pts) { nanosecond_pts_ = nano_pts; }
private:
Buffer buffer_;
bool keyframe_ = false;
std::int64_t nanosecond_pts_ = 0;
Codec codec_ = kVP9;
};
} // namespace libwebm
#endif // LIBWEBM_COMMON_VIDEO_FRAME_H_
@@ -0,0 +1,266 @@
// Copyright (c) 2016 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 "common/vp9_header_parser.h"
#include <stdio.h>
namespace vp9_parser {
bool Vp9HeaderParser::SetFrame(const uint8_t* frame, size_t length) {
if (!frame || length == 0)
return false;
frame_ = frame;
frame_size_ = length;
bit_offset_ = 0;
profile_ = -1;
show_existing_frame_ = 0;
key_ = 0;
altref_ = 0;
error_resilient_mode_ = 0;
intra_only_ = 0;
reset_frame_context_ = 0;
color_space_ = 0;
color_range_ = 0;
subsampling_x_ = 0;
subsampling_y_ = 0;
refresh_frame_flags_ = 0;
return true;
}
bool Vp9HeaderParser::ParseUncompressedHeader(const uint8_t* frame,
size_t length) {
if (!SetFrame(frame, length))
return false;
const int frame_marker = VpxReadLiteral(2);
if (frame_marker != kVp9FrameMarker) {
fprintf(stderr, "Invalid VP9 frame_marker:%d\n", frame_marker);
return false;
}
profile_ = ReadBit();
profile_ |= ReadBit() << 1;
if (profile_ > 2)
profile_ += ReadBit();
// TODO(fgalligan): Decide how to handle show existing frames.
show_existing_frame_ = ReadBit();
if (show_existing_frame_)
return true;
key_ = !ReadBit();
altref_ = !ReadBit();
error_resilient_mode_ = ReadBit();
if (key_) {
if (!ValidateVp9SyncCode()) {
fprintf(stderr, "Invalid Sync code!\n");
return false;
}
ParseColorSpace();
ParseFrameResolution();
ParseFrameParallelMode();
ParseTileInfo();
} else {
intra_only_ = altref_ ? ReadBit() : 0;
reset_frame_context_ = error_resilient_mode_ ? 0 : VpxReadLiteral(2);
if (intra_only_) {
if (!ValidateVp9SyncCode()) {
fprintf(stderr, "Invalid Sync code!\n");
return false;
}
if (profile_ > 0) {
ParseColorSpace();
} else {
// NOTE: The intra-only frame header does not include the specification
// of either the color format or color sub-sampling in profile 0. VP9
// specifies that the default color format should be YUV 4:2:0 in this
// case (normative).
color_space_ = kVpxCsBt601;
color_range_ = kVpxCrStudioRange;
subsampling_y_ = subsampling_x_ = 1;
bit_depth_ = 8;
}
refresh_frame_flags_ = VpxReadLiteral(kRefFrames);
ParseFrameResolution();
} else {
refresh_frame_flags_ = VpxReadLiteral(kRefFrames);
for (int i = 0; i < kRefsPerFrame; ++i) {
VpxReadLiteral(kRefFrames_LOG2); // Consume ref.
ReadBit(); // Consume ref sign bias.
}
bool found = false;
for (int i = 0; i < kRefsPerFrame; ++i) {
if (ReadBit()) {
// Found previous reference, width and height did not change since
// last frame.
found = true;
break;
}
}
if (!found)
ParseFrameResolution();
}
}
return true;
}
int Vp9HeaderParser::ReadBit() {
const size_t off = bit_offset_;
const size_t byte_offset = off >> 3;
const int bit_shift = 7 - static_cast<int>(off & 0x7);
if (byte_offset < frame_size_) {
const int bit = (frame_[byte_offset] >> bit_shift) & 1;
bit_offset_++;
return bit;
} else {
return 0;
}
}
int Vp9HeaderParser::VpxReadLiteral(int bits) {
int value = 0;
for (int bit = bits - 1; bit >= 0; --bit)
value |= ReadBit() << bit;
return value;
}
bool Vp9HeaderParser::ValidateVp9SyncCode() {
const int sync_code_0 = VpxReadLiteral(8);
const int sync_code_1 = VpxReadLiteral(8);
const int sync_code_2 = VpxReadLiteral(8);
return (sync_code_0 == 0x49 && sync_code_1 == 0x83 && sync_code_2 == 0x42);
}
void Vp9HeaderParser::ParseColorSpace() {
bit_depth_ = 0;
if (profile_ >= 2)
bit_depth_ = ReadBit() ? 12 : 10;
else
bit_depth_ = 8;
color_space_ = VpxReadLiteral(3);
if (color_space_ != kVpxCsSrgb) {
color_range_ = ReadBit();
if (profile_ == 1 || profile_ == 3) {
subsampling_x_ = ReadBit();
subsampling_y_ = ReadBit();
ReadBit();
} else {
subsampling_y_ = subsampling_x_ = 1;
}
} else {
color_range_ = kVpxCrFullRange;
if (profile_ == 1 || profile_ == 3) {
subsampling_y_ = subsampling_x_ = 0;
ReadBit();
}
}
}
void Vp9HeaderParser::ParseFrameResolution() {
width_ = VpxReadLiteral(16) + 1;
height_ = VpxReadLiteral(16) + 1;
}
void Vp9HeaderParser::ParseFrameParallelMode() {
if (ReadBit()) {
VpxReadLiteral(16); // display width
VpxReadLiteral(16); // display height
}
if (!error_resilient_mode_) {
ReadBit(); // Consume refresh frame context
frame_parallel_mode_ = ReadBit();
} else {
frame_parallel_mode_ = 1;
}
}
void Vp9HeaderParser::ParseTileInfo() {
VpxReadLiteral(2); // Consume frame context index
// loopfilter
VpxReadLiteral(6); // Consume filter level
VpxReadLiteral(3); // Consume sharpness level
const bool mode_ref_delta_enabled = ReadBit();
if (mode_ref_delta_enabled) {
const bool mode_ref_delta_update = ReadBit();
if (mode_ref_delta_update) {
const int kMaxRefLFDeltas = 4;
for (int i = 0; i < kMaxRefLFDeltas; ++i) {
if (ReadBit())
VpxReadLiteral(7); // Consume ref_deltas + sign
}
const int kMaxModeDeltas = 2;
for (int i = 0; i < kMaxModeDeltas; ++i) {
if (ReadBit())
VpxReadLiteral(7); // Consume mode_delta + sign
}
}
}
// quantization
VpxReadLiteral(8); // Consume base_q
SkipDeltaQ(); // y dc
SkipDeltaQ(); // uv ac
SkipDeltaQ(); // uv dc
// segmentation
const bool segmentation_enabled = ReadBit();
if (!segmentation_enabled) {
const int aligned_width = AlignPowerOfTwo(width_, kMiSizeLog2);
const int mi_cols = aligned_width >> kMiSizeLog2;
const int aligned_mi_cols = AlignPowerOfTwo(mi_cols, kMiSizeLog2);
const int sb_cols = aligned_mi_cols >> 3; // to_sbs(mi_cols);
int min_log2_n_tiles, max_log2_n_tiles;
for (max_log2_n_tiles = 0;
(sb_cols >> max_log2_n_tiles) >= kMinTileWidthB64;
max_log2_n_tiles++) {
}
max_log2_n_tiles--;
if (max_log2_n_tiles < 0)
max_log2_n_tiles = 0;
for (min_log2_n_tiles = 0; (kMaxTileWidthB64 << min_log2_n_tiles) < sb_cols;
min_log2_n_tiles++) {
}
// columns
const int max_log2_tile_cols = max_log2_n_tiles;
const int min_log2_tile_cols = min_log2_n_tiles;
int max_ones = max_log2_tile_cols - min_log2_tile_cols;
int log2_tile_cols = min_log2_tile_cols;
while (max_ones-- && ReadBit())
log2_tile_cols++;
// rows
int log2_tile_rows = ReadBit();
if (log2_tile_rows)
log2_tile_rows += ReadBit();
row_tiles_ = 1 << log2_tile_rows;
column_tiles_ = 1 << log2_tile_cols;
}
}
void Vp9HeaderParser::SkipDeltaQ() {
if (ReadBit())
VpxReadLiteral(4);
}
int Vp9HeaderParser::AlignPowerOfTwo(int value, int n) {
return (((value) + ((1 << (n)) - 1)) & ~((1 << (n)) - 1));
}
} // namespace vp9_parser
@@ -0,0 +1,125 @@
// Copyright (c) 2016 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.
#ifndef LIBWEBM_COMMON_VP9_HEADER_PARSER_H_
#define LIBWEBM_COMMON_VP9_HEADER_PARSER_H_
#include <stddef.h>
#include <stdint.h>
namespace vp9_parser {
const int kVp9FrameMarker = 2;
const int kMinTileWidthB64 = 4;
const int kMaxTileWidthB64 = 64;
const int kRefFrames = 8;
const int kRefsPerFrame = 3;
const int kRefFrames_LOG2 = 3;
const int kVpxCsBt601 = 1;
const int kVpxCsSrgb = 7;
const int kVpxCrStudioRange = 0;
const int kVpxCrFullRange = 1;
const int kMiSizeLog2 = 3;
// Class to parse the header of a VP9 frame.
class Vp9HeaderParser {
public:
Vp9HeaderParser()
: frame_(NULL),
frame_size_(0),
bit_offset_(0),
profile_(-1),
show_existing_frame_(0),
key_(0),
altref_(0),
error_resilient_mode_(0),
intra_only_(0),
reset_frame_context_(0),
bit_depth_(0),
color_space_(0),
color_range_(0),
subsampling_x_(0),
subsampling_y_(0),
refresh_frame_flags_(0),
width_(0),
height_(0),
row_tiles_(0),
column_tiles_(0),
frame_parallel_mode_(0) {}
// Parse the VP9 uncompressed header. The return values of the remaining
// functions are only valid on success.
bool ParseUncompressedHeader(const uint8_t* frame, size_t length);
size_t frame_size() const { return frame_size_; }
int profile() const { return profile_; }
int key() const { return key_; }
int altref() const { return altref_; }
int error_resilient_mode() const { return error_resilient_mode_; }
int bit_depth() const { return bit_depth_; }
int color_space() const { return color_space_; }
int width() const { return width_; }
int height() const { return height_; }
int refresh_frame_flags() const { return refresh_frame_flags_; }
int row_tiles() const { return row_tiles_; }
int column_tiles() const { return column_tiles_; }
int frame_parallel_mode() const { return frame_parallel_mode_; }
private:
// Set the compressed VP9 frame.
bool SetFrame(const uint8_t* frame, size_t length);
// Returns the next bit of the frame.
int ReadBit();
// Returns the next |bits| of the frame.
int VpxReadLiteral(int bits);
// Returns true if the vp9 sync code is valid.
bool ValidateVp9SyncCode();
// Parses bit_depth_, color_space_, subsampling_x_, subsampling_y_, and
// color_range_.
void ParseColorSpace();
// Parses width and height of the frame.
void ParseFrameResolution();
// Parses frame_parallel_mode_. This function skips over some features.
void ParseFrameParallelMode();
// Parses row and column tiles. This function skips over some features.
void ParseTileInfo();
void SkipDeltaQ();
int AlignPowerOfTwo(int value, int n);
const uint8_t* frame_;
size_t frame_size_;
size_t bit_offset_;
int profile_;
int show_existing_frame_;
int key_;
int altref_;
int error_resilient_mode_;
int intra_only_;
int reset_frame_context_;
int bit_depth_;
int color_space_;
int color_range_;
int subsampling_x_;
int subsampling_y_;
int refresh_frame_flags_;
int width_;
int height_;
int row_tiles_;
int column_tiles_;
int frame_parallel_mode_;
};
} // namespace vp9_parser
#endif // LIBWEBM_COMMON_VP9_HEADER_PARSER_H_
@@ -0,0 +1,181 @@
// Copyright (c) 2016 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 "common/vp9_header_parser.h"
#include <string>
#include "gtest/gtest.h"
#include "common/hdr_util.h"
#include "mkvparser/mkvparser.h"
#include "mkvparser/mkvreader.h"
#include "testing/test_util.h"
namespace {
class Vp9HeaderParserTests : public ::testing::Test {
public:
Vp9HeaderParserTests() : is_reader_open_(false), segment_(NULL) {}
~Vp9HeaderParserTests() override {
CloseReader();
if (segment_ != NULL) {
delete segment_;
segment_ = NULL;
}
}
void CloseReader() {
if (is_reader_open_) {
reader_.Close();
}
is_reader_open_ = false;
}
void CreateAndLoadSegment(const std::string& filename,
int expected_doc_type_ver) {
filename_ = test::GetTestFilePath(filename);
ASSERT_EQ(0, reader_.Open(filename_.c_str()));
is_reader_open_ = true;
pos_ = 0;
mkvparser::EBMLHeader ebml_header;
ebml_header.Parse(&reader_, pos_);
ASSERT_EQ(1, ebml_header.m_version);
ASSERT_EQ(1, ebml_header.m_readVersion);
ASSERT_STREQ("webm", ebml_header.m_docType);
ASSERT_EQ(expected_doc_type_ver, ebml_header.m_docTypeVersion);
ASSERT_EQ(2, ebml_header.m_docTypeReadVersion);
ASSERT_EQ(0, mkvparser::Segment::CreateInstance(&reader_, pos_, segment_));
ASSERT_FALSE(HasFailure());
ASSERT_GE(0, segment_->Load());
}
void CreateAndLoadSegment(const std::string& filename) {
CreateAndLoadSegment(filename, 4);
}
// Load a corrupted segment with no expectation of correctness.
void CreateAndLoadInvalidSegment(const std::string& filename) {
filename_ = test::GetTestFilePath(filename);
ASSERT_EQ(0, reader_.Open(filename_.c_str()));
is_reader_open_ = true;
pos_ = 0;
mkvparser::EBMLHeader ebml_header;
ebml_header.Parse(&reader_, pos_);
ASSERT_EQ(0, mkvparser::Segment::CreateInstance(&reader_, pos_, segment_));
ASSERT_GE(0, segment_->Load());
}
void ProcessTheFrames(bool invalid_bitstream) {
unsigned char* data = NULL;
size_t data_len = 0;
const mkvparser::Tracks* const parser_tracks = segment_->GetTracks();
ASSERT_TRUE(parser_tracks != NULL);
const mkvparser::Cluster* cluster = segment_->GetFirst();
ASSERT_TRUE(cluster != NULL);
while ((cluster != NULL) && !cluster->EOS()) {
const mkvparser::BlockEntry* block_entry;
long status = cluster->GetFirst(block_entry); // NOLINT
ASSERT_EQ(0, status);
while ((block_entry != NULL) && !block_entry->EOS()) {
const mkvparser::Block* const block = block_entry->GetBlock();
ASSERT_TRUE(block != NULL);
const long long trackNum = block->GetTrackNumber(); // NOLINT
const mkvparser::Track* const parser_track =
parser_tracks->GetTrackByNumber(
static_cast<unsigned long>(trackNum)); // NOLINT
ASSERT_TRUE(parser_track != NULL);
const long long track_type = parser_track->GetType(); // NOLINT
if (track_type == mkvparser::Track::kVideo) {
const int frame_count = block->GetFrameCount();
for (int i = 0; i < frame_count; ++i) {
const mkvparser::Block::Frame& frame = block->GetFrame(i);
if (static_cast<size_t>(frame.len) > data_len) {
delete[] data;
data = new unsigned char[frame.len];
ASSERT_TRUE(data != NULL);
data_len = static_cast<size_t>(frame.len);
}
ASSERT_FALSE(frame.Read(&reader_, data));
ASSERT_EQ(parser_.ParseUncompressedHeader(data, data_len),
!invalid_bitstream);
}
}
status = cluster->GetNext(block_entry, block_entry);
ASSERT_EQ(0, status);
}
cluster = segment_->GetNext(cluster);
}
delete[] data;
}
protected:
mkvparser::MkvReader reader_;
bool is_reader_open_;
mkvparser::Segment* segment_;
std::string filename_;
long long pos_; // NOLINT
vp9_parser::Vp9HeaderParser parser_;
};
TEST_F(Vp9HeaderParserTests, VideoOnlyFile) {
ASSERT_NO_FATAL_FAILURE(CreateAndLoadSegment("test_stereo_left_right.webm"));
ProcessTheFrames(false);
EXPECT_EQ(256, parser_.width());
EXPECT_EQ(144, parser_.height());
EXPECT_EQ(1, parser_.column_tiles());
EXPECT_EQ(0, parser_.frame_parallel_mode());
}
TEST_F(Vp9HeaderParserTests, Muxed) {
ASSERT_NO_FATAL_FAILURE(
CreateAndLoadSegment("bbb_480p_vp9_opus_1second.webm", 4));
ProcessTheFrames(false);
EXPECT_EQ(854, parser_.width());
EXPECT_EQ(480, parser_.height());
EXPECT_EQ(2, parser_.column_tiles());
EXPECT_EQ(1, parser_.frame_parallel_mode());
}
TEST_F(Vp9HeaderParserTests, Invalid) {
const char* files[] = {
"invalid/invalid_vp9_bitstream-bug_1416.webm",
"invalid/invalid_vp9_bitstream-bug_1417.webm",
};
for (int i = 0; i < static_cast<int>(sizeof(files) / sizeof(files[0])); ++i) {
SCOPED_TRACE(files[i]);
ASSERT_NO_FATAL_FAILURE(CreateAndLoadInvalidSegment(files[i]));
ProcessTheFrames(true);
CloseReader();
delete segment_;
segment_ = NULL;
}
}
TEST_F(Vp9HeaderParserTests, API) {
vp9_parser::Vp9HeaderParser parser;
uint8_t data;
EXPECT_FALSE(parser.ParseUncompressedHeader(NULL, 0));
EXPECT_FALSE(parser.ParseUncompressedHeader(NULL, 10));
EXPECT_FALSE(parser.ParseUncompressedHeader(&data, 0));
}
} // namespace
int main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
@@ -0,0 +1,269 @@
// Copyright (c) 2016 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 "common/vp9_level_stats.h"
#include <inttypes.h>
#include <limits>
#include <utility>
#include "common/webm_constants.h"
namespace vp9_parser {
const Vp9LevelRow Vp9LevelStats::Vp9LevelTable[kNumVp9Levels] = {
{LEVEL_1, 829440, 36864, 200, 400, 2, 1, 4, 8, 512},
{LEVEL_1_1, 2764800, 73728, 800, 1000, 2, 1, 4, 8, 768},
{LEVEL_2, 4608000, 122880, 1800, 1500, 2, 1, 4, 8, 960},
{LEVEL_2_1, 9216000, 245760, 3600, 2800, 2, 2, 4, 8, 1344},
{LEVEL_3, 20736000, 552960, 7200, 6000, 2, 4, 4, 8, 2048},
{LEVEL_3_1, 36864000, 983040, 12000, 10000, 2, 4, 4, 8, 2752},
{LEVEL_4, 83558400, 2228224, 18000, 16000, 4, 4, 4, 8, 4160},
{LEVEL_4_1, 160432128, 2228224, 30000, 18000, 4, 4, 5, 6, 4160},
{LEVEL_5, 311951360, 8912896, 60000, 36000, 6, 8, 6, 4, 8384},
{LEVEL_5_1, 588251136, 8912896, 120000, 46000, 8, 8, 10, 4, 8384},
// CPB Size = 0 for levels 5_2 to 6_2
{LEVEL_5_2, 1176502272, 8912896, 180000, 0, 8, 8, 10, 4, 8384},
{LEVEL_6, 1176502272, 35651584, 180000, 0, 8, 16, 10, 4, 16832},
{LEVEL_6_1, 2353004544, 35651584, 240000, 0, 8, 16, 10, 4, 16832},
{LEVEL_6_2, 4706009088, 35651584, 480000, 0, 8, 16, 10, 4, 16832}};
void Vp9LevelStats::AddFrame(const Vp9HeaderParser& parser, int64_t time_ns) {
++frames;
if (start_ns_ == -1)
start_ns_ = time_ns;
end_ns_ = time_ns;
const int width = parser.width();
const int height = parser.height();
const int64_t luma_picture_size = width * height;
const int64_t luma_picture_breadth = (width > height) ? width : height;
if (luma_picture_size > max_luma_picture_size_)
max_luma_picture_size_ = luma_picture_size;
if (luma_picture_breadth > max_luma_picture_breadth_)
max_luma_picture_breadth_ = luma_picture_breadth;
total_compressed_size_ += parser.frame_size();
while (!luma_window_.empty() &&
luma_window_.front().first <
(time_ns - (libwebm::kNanosecondsPerSecondi - 1))) {
current_luma_size_ -= luma_window_.front().second;
luma_window_.pop();
}
current_luma_size_ += luma_picture_size;
luma_window_.push(std::make_pair(time_ns, luma_picture_size));
if (current_luma_size_ > max_luma_size_) {
max_luma_size_ = current_luma_size_;
max_luma_end_ns_ = luma_window_.back().first;
}
// Record CPB stats.
// Remove all frames that are less than window size.
while (cpb_window_.size() > 3) {
current_cpb_size_ -= cpb_window_.front().second;
cpb_window_.pop();
}
cpb_window_.push(std::make_pair(time_ns, parser.frame_size()));
current_cpb_size_ += parser.frame_size();
if (current_cpb_size_ > max_cpb_size_) {
max_cpb_size_ = current_cpb_size_;
max_cpb_start_ns_ = cpb_window_.front().first;
max_cpb_end_ns_ = cpb_window_.back().first;
}
if (max_cpb_window_size_ < static_cast<int64_t>(cpb_window_.size())) {
max_cpb_window_size_ = cpb_window_.size();
max_cpb_window_end_ns_ = time_ns;
}
// Record altref stats.
if (parser.altref()) {
const int delta_altref = frames_since_last_altref;
if (first_altref) {
first_altref = false;
} else if (delta_altref < minimum_altref_distance) {
minimum_altref_distance = delta_altref;
min_altref_end_ns = time_ns;
}
frames_since_last_altref = 0;
} else {
++frames_since_last_altref;
++displayed_frames;
// TODO(fgalligan): Add support for other color formats. Currently assuming
// 420.
total_uncompressed_bits_ +=
(luma_picture_size * parser.bit_depth() * 3) / 2;
}
// Count max reference frames.
if (parser.key() == 1) {
frames_refreshed_ = 0;
} else {
frames_refreshed_ |= parser.refresh_frame_flags();
int ref_frame_count = frames_refreshed_ & 1;
for (int i = 1; i < kMaxVp9RefFrames; ++i) {
ref_frame_count += (frames_refreshed_ >> i) & 1;
}
if (ref_frame_count > max_frames_refreshed_)
max_frames_refreshed_ = ref_frame_count;
}
// Count max tiles.
const int tiles = parser.column_tiles();
if (tiles > max_column_tiles_)
max_column_tiles_ = tiles;
}
Vp9Level Vp9LevelStats::GetLevel() const {
const int64_t max_luma_sample_rate = GetMaxLumaSampleRate();
const int64_t max_luma_picture_size = GetMaxLumaPictureSize();
const int64_t max_luma_picture_breadth = GetMaxLumaPictureBreadth();
const double average_bitrate = GetAverageBitRate();
const double max_cpb_size = GetMaxCpbSize();
const double compresion_ratio = GetCompressionRatio();
const int max_column_tiles = GetMaxColumnTiles();
const int min_altref_distance = GetMinimumAltrefDistance();
const int max_ref_frames = GetMaxReferenceFrames();
int level_index = 0;
Vp9Level max_level = LEVEL_UNKNOWN;
const double grace_multiplier =
max_luma_sample_rate_grace_percent_ / 100.0 + 1.0;
for (int i = 0; i < kNumVp9Levels; ++i) {
if (max_luma_sample_rate <=
Vp9LevelTable[i].max_luma_sample_rate * grace_multiplier) {
if (max_level < Vp9LevelTable[i].level) {
max_level = Vp9LevelTable[i].level;
level_index = i;
}
break;
}
}
for (int i = 0; i < kNumVp9Levels; ++i) {
if (max_luma_picture_size <= Vp9LevelTable[i].max_luma_picture_size) {
if (max_level < Vp9LevelTable[i].level) {
max_level = Vp9LevelTable[i].level;
level_index = i;
}
break;
}
}
for (int i = 0; i < kNumVp9Levels; ++i) {
if (max_luma_picture_breadth <= Vp9LevelTable[i].max_luma_picture_breadth) {
if (max_level < Vp9LevelTable[i].level) {
max_level = Vp9LevelTable[i].level;
level_index = i;
}
break;
}
}
for (int i = 0; i < kNumVp9Levels; ++i) {
if (average_bitrate <= Vp9LevelTable[i].average_bitrate) {
if (max_level < Vp9LevelTable[i].level) {
max_level = Vp9LevelTable[i].level;
level_index = i;
}
break;
}
}
for (int i = 0; i < kNumVp9Levels; ++i) {
// Only check CPB size for levels that are defined.
if (Vp9LevelTable[i].max_cpb_size > 0 &&
max_cpb_size <= Vp9LevelTable[i].max_cpb_size) {
if (max_level < Vp9LevelTable[i].level) {
max_level = Vp9LevelTable[i].level;
level_index = i;
}
break;
}
}
for (int i = 0; i < kNumVp9Levels; ++i) {
if (max_column_tiles <= Vp9LevelTable[i].max_tiles) {
if (max_level < Vp9LevelTable[i].level) {
max_level = Vp9LevelTable[i].level;
level_index = i;
}
break;
}
}
for (int i = 0; i < kNumVp9Levels; ++i) {
if (max_ref_frames <= Vp9LevelTable[i].max_ref_frames) {
if (max_level < Vp9LevelTable[i].level) {
max_level = Vp9LevelTable[i].level;
level_index = i;
}
break;
}
}
// Check if the current level meets the minimum altref distance requirement.
// If not, set to unknown level as we can't move up a level as the minimum
// altref distance get farther apart and we can't move down a level as we are
// already at the minimum level for all the other requirements.
if (min_altref_distance < Vp9LevelTable[level_index].min_altref_distance)
max_level = LEVEL_UNKNOWN;
// The minimum compression ratio has the same behavior as minimum altref
// distance.
if (compresion_ratio < Vp9LevelTable[level_index].compresion_ratio)
max_level = LEVEL_UNKNOWN;
return max_level;
}
int64_t Vp9LevelStats::GetMaxLumaSampleRate() const { return max_luma_size_; }
int64_t Vp9LevelStats::GetMaxLumaPictureSize() const {
return max_luma_picture_size_;
}
int64_t Vp9LevelStats::GetMaxLumaPictureBreadth() const {
return max_luma_picture_breadth_;
}
double Vp9LevelStats::GetAverageBitRate() const {
const int64_t frame_duration_ns = end_ns_ - start_ns_;
double duration_seconds =
((duration_ns_ == -1) ? frame_duration_ns : duration_ns_) /
libwebm::kNanosecondsPerSecond;
if (estimate_last_frame_duration_ &&
(duration_ns_ == -1 || duration_ns_ <= frame_duration_ns)) {
const double sec_per_frame = frame_duration_ns /
libwebm::kNanosecondsPerSecond /
(displayed_frames - 1);
duration_seconds += sec_per_frame;
}
return total_compressed_size_ / duration_seconds / 125.0;
}
double Vp9LevelStats::GetMaxCpbSize() const { return max_cpb_size_ / 125.0; }
double Vp9LevelStats::GetCompressionRatio() const {
return total_uncompressed_bits_ /
static_cast<double>(total_compressed_size_ * 8);
}
int Vp9LevelStats::GetMaxColumnTiles() const { return max_column_tiles_; }
int Vp9LevelStats::GetMinimumAltrefDistance() const {
if (minimum_altref_distance != std::numeric_limits<int>::max())
return minimum_altref_distance;
else
return -1;
}
int Vp9LevelStats::GetMaxReferenceFrames() const {
return max_frames_refreshed_;
}
} // namespace vp9_parser
@@ -0,0 +1,215 @@
// Copyright (c) 2016 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.
#ifndef LIBWEBM_COMMON_VP9_LEVEL_STATS_H_
#define LIBWEBM_COMMON_VP9_LEVEL_STATS_H_
#include <limits>
#include <queue>
#include <utility>
#include "common/vp9_header_parser.h"
namespace vp9_parser {
const int kMaxVp9RefFrames = 8;
// Defined VP9 levels. See http://www.webmproject.org/vp9/profiles/ for
// detailed information on VP9 levels.
const int kNumVp9Levels = 14;
enum Vp9Level {
LEVEL_UNKNOWN = 0,
LEVEL_1 = 10,
LEVEL_1_1 = 11,
LEVEL_2 = 20,
LEVEL_2_1 = 21,
LEVEL_3 = 30,
LEVEL_3_1 = 31,
LEVEL_4 = 40,
LEVEL_4_1 = 41,
LEVEL_5 = 50,
LEVEL_5_1 = 51,
LEVEL_5_2 = 52,
LEVEL_6 = 60,
LEVEL_6_1 = 61,
LEVEL_6_2 = 62
};
struct Vp9LevelRow {
Vp9LevelRow() = default;
~Vp9LevelRow() = default;
Vp9LevelRow(Vp9LevelRow&& other) = default;
Vp9LevelRow(const Vp9LevelRow& other) = default;
Vp9LevelRow& operator=(Vp9LevelRow&& other) = delete;
Vp9LevelRow& operator=(const Vp9LevelRow& other) = delete;
Vp9Level level;
int64_t max_luma_sample_rate;
int64_t max_luma_picture_size;
int64_t max_luma_picture_breadth;
double average_bitrate;
double max_cpb_size;
double compresion_ratio;
int max_tiles;
int min_altref_distance;
int max_ref_frames;
};
// Class to determine the VP9 level of a VP9 bitstream.
class Vp9LevelStats {
public:
static const Vp9LevelRow Vp9LevelTable[kNumVp9Levels];
Vp9LevelStats()
: frames(0),
displayed_frames(0),
start_ns_(-1),
end_ns_(-1),
duration_ns_(-1),
max_luma_picture_size_(0),
max_luma_picture_breadth_(0),
current_luma_size_(0),
max_luma_size_(0),
max_luma_end_ns_(0),
max_luma_sample_rate_grace_percent_(1.5),
first_altref(true),
frames_since_last_altref(0),
minimum_altref_distance(std::numeric_limits<int>::max()),
min_altref_end_ns(0),
max_cpb_window_size_(0),
max_cpb_window_end_ns_(0),
current_cpb_size_(0),
max_cpb_size_(0),
max_cpb_start_ns_(0),
max_cpb_end_ns_(0),
total_compressed_size_(0),
total_uncompressed_bits_(0),
frames_refreshed_(0),
max_frames_refreshed_(0),
max_column_tiles_(0),
estimate_last_frame_duration_(true) {}
~Vp9LevelStats() = default;
Vp9LevelStats(Vp9LevelStats&& other) = delete;
Vp9LevelStats(const Vp9LevelStats& other) = delete;
Vp9LevelStats& operator=(Vp9LevelStats&& other) = delete;
Vp9LevelStats& operator=(const Vp9LevelStats& other) = delete;
// Collects stats on a VP9 frame. The frame must already be parsed by
// |parser|. |time_ns| is the start time of the frame in nanoseconds.
void AddFrame(const Vp9HeaderParser& parser, int64_t time_ns);
// Returns the current VP9 level. All of the video frames should have been
// processed with AddFrame before calling this function.
Vp9Level GetLevel() const;
// Returns the maximum luma samples (pixels) per second. The Alt-Ref frames
// are taken into account, therefore this number may be larger than the
// display luma samples per second
int64_t GetMaxLumaSampleRate() const;
// The maximum frame size (width * height) in samples.
int64_t GetMaxLumaPictureSize() const;
// The maximum frame breadth (max of width and height) in samples.
int64_t GetMaxLumaPictureBreadth() const;
// The average bitrate of the video in kbps.
double GetAverageBitRate() const;
// The largest data size for any 4 consecutive frames in kilobits.
double GetMaxCpbSize() const;
// The ratio of total bytes decompressed over total bytes compressed.
double GetCompressionRatio() const;
// The maximum number of VP9 column tiles.
int GetMaxColumnTiles() const;
// The minimum distance in frames between two consecutive alternate reference
// frames.
int GetMinimumAltrefDistance() const;
// The maximum number of reference frames that had to be stored.
int GetMaxReferenceFrames() const;
// Sets the duration of the video stream in nanoseconds. If the duration is
// not explictly set by this function then this class will use end - start
// as the duration.
void set_duration(int64_t time_ns) { duration_ns_ = time_ns; }
double max_luma_sample_rate_grace_percent() const {
return max_luma_sample_rate_grace_percent_;
}
void set_max_luma_sample_rate_grace_percent(double percent) {
max_luma_sample_rate_grace_percent_ = percent;
}
bool estimate_last_frame_duration() const {
return estimate_last_frame_duration_;
}
// If true try to estimate the last frame's duration if the stream's duration
// is not set or the stream's duration equals the last frame's timestamp.
void set_estimate_last_frame_duration(bool flag) {
estimate_last_frame_duration_ = flag;
}
private:
int frames;
int displayed_frames;
int64_t start_ns_;
int64_t end_ns_;
int64_t duration_ns_;
int64_t max_luma_picture_size_;
int64_t max_luma_picture_breadth_;
// This is used to calculate the maximum number of luma samples per second.
// The first value is the luma picture size and the second value is the time
// in nanoseconds of one frame.
std::queue<std::pair<int64_t, int64_t>> luma_window_;
int64_t current_luma_size_;
int64_t max_luma_size_;
int64_t max_luma_end_ns_;
// MaxLumaSampleRate = (ExampleFrameRate + ExampleFrameRate /
// MinimumAltrefDistance) * MaxLumaPictureSize. For levels 1-4
// ExampleFrameRate / MinimumAltrefDistance is non-integer, so using a sliding
// window of one frame to calculate MaxLumaSampleRate may have frames >
// (ExampleFrameRate + ExampleFrameRate / MinimumAltrefDistance) in the
// window. In order to address this issue, a grace percent of 1.5 was added.
double max_luma_sample_rate_grace_percent_;
bool first_altref;
int frames_since_last_altref;
int minimum_altref_distance;
int64_t min_altref_end_ns;
// This is used to calculate the maximum number of compressed bytes for four
// consecutive frames. The first value is the compressed frame size and the
// second value is the time in nanoseconds of one frame.
std::queue<std::pair<int64_t, int64_t>> cpb_window_;
int64_t max_cpb_window_size_;
int64_t max_cpb_window_end_ns_;
int64_t current_cpb_size_;
int64_t max_cpb_size_;
int64_t max_cpb_start_ns_;
int64_t max_cpb_end_ns_;
int64_t total_compressed_size_;
int64_t total_uncompressed_bits_;
int frames_refreshed_;
int max_frames_refreshed_;
int max_column_tiles_;
bool estimate_last_frame_duration_;
};
} // namespace vp9_parser
#endif // LIBWEBM_COMMON_VP9_LEVEL_STATS_H_
@@ -0,0 +1,191 @@
// Copyright (c) 2016 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 "common/vp9_level_stats.h"
#include <memory>
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "common/hdr_util.h"
#include "common/vp9_header_parser.h"
#include "mkvparser/mkvparser.h"
#include "mkvparser/mkvreader.h"
#include "testing/test_util.h"
namespace {
// TODO(fgalligan): Refactor this test with other test files in this directory.
class Vp9LevelStatsTests : public ::testing::Test {
public:
Vp9LevelStatsTests() : is_reader_open_(false) {}
~Vp9LevelStatsTests() override { CloseReader(); }
void CloseReader() {
if (is_reader_open_) {
reader_.Close();
}
is_reader_open_ = false;
}
void CreateAndLoadSegment(const std::string& filename,
int expected_doc_type_ver) {
ASSERT_NE(0u, filename.length());
filename_ = test::GetTestFilePath(filename);
ASSERT_EQ(0, reader_.Open(filename_.c_str()));
is_reader_open_ = true;
pos_ = 0;
mkvparser::EBMLHeader ebml_header;
ebml_header.Parse(&reader_, pos_);
ASSERT_EQ(1, ebml_header.m_version);
ASSERT_EQ(1, ebml_header.m_readVersion);
ASSERT_STREQ("webm", ebml_header.m_docType);
ASSERT_EQ(expected_doc_type_ver, ebml_header.m_docTypeVersion);
ASSERT_EQ(2, ebml_header.m_docTypeReadVersion);
mkvparser::Segment* temp;
ASSERT_EQ(0, mkvparser::Segment::CreateInstance(&reader_, pos_, temp));
segment_.reset(temp);
ASSERT_FALSE(HasFailure());
ASSERT_GE(0, segment_->Load());
}
void CreateAndLoadSegment(const std::string& filename) {
CreateAndLoadSegment(filename, 4);
}
void ProcessTheFrames() {
std::vector<uint8_t> data;
size_t data_len = 0;
const mkvparser::Tracks* const parser_tracks = segment_->GetTracks();
ASSERT_TRUE(parser_tracks != NULL);
const mkvparser::Cluster* cluster = segment_->GetFirst();
ASSERT_TRUE(cluster);
while ((cluster != NULL) && !cluster->EOS()) {
const mkvparser::BlockEntry* block_entry;
long status = cluster->GetFirst(block_entry); // NOLINT
ASSERT_EQ(0, status);
while ((block_entry != NULL) && !block_entry->EOS()) {
const mkvparser::Block* const block = block_entry->GetBlock();
ASSERT_TRUE(block != NULL);
const long long trackNum = block->GetTrackNumber(); // NOLINT
const mkvparser::Track* const parser_track =
parser_tracks->GetTrackByNumber(
static_cast<unsigned long>(trackNum)); // NOLINT
ASSERT_TRUE(parser_track != NULL);
const long long track_type = parser_track->GetType(); // NOLINT
if (track_type == mkvparser::Track::kVideo) {
const int frame_count = block->GetFrameCount();
const long long time_ns = block->GetTime(cluster); // NOLINT
for (int i = 0; i < frame_count; ++i) {
const mkvparser::Block::Frame& frame = block->GetFrame(i);
if (static_cast<size_t>(frame.len) > data.size()) {
data.resize(frame.len);
data_len = static_cast<size_t>(frame.len);
}
ASSERT_FALSE(frame.Read(&reader_, &data[0]));
parser_.ParseUncompressedHeader(&data[0], data_len);
stats_.AddFrame(parser_, time_ns);
}
}
status = cluster->GetNext(block_entry, block_entry);
ASSERT_EQ(0, status);
}
cluster = segment_->GetNext(cluster);
}
}
protected:
mkvparser::MkvReader reader_;
bool is_reader_open_;
std::unique_ptr<mkvparser::Segment> segment_;
std::string filename_;
long long pos_; // NOLINT
vp9_parser::Vp9HeaderParser parser_;
vp9_parser::Vp9LevelStats stats_;
};
TEST_F(Vp9LevelStatsTests, VideoOnlyFile) {
CreateAndLoadSegment("test_stereo_left_right.webm");
ProcessTheFrames();
EXPECT_EQ(256, parser_.width());
EXPECT_EQ(144, parser_.height());
EXPECT_EQ(1, parser_.column_tiles());
EXPECT_EQ(0, parser_.frame_parallel_mode());
EXPECT_EQ(11, stats_.GetLevel());
EXPECT_EQ(479232, stats_.GetMaxLumaSampleRate());
EXPECT_EQ(36864, stats_.GetMaxLumaPictureSize());
EXPECT_DOUBLE_EQ(264.03233333333333, stats_.GetAverageBitRate());
EXPECT_DOUBLE_EQ(147.136, stats_.GetMaxCpbSize());
EXPECT_DOUBLE_EQ(19.267458404715583, stats_.GetCompressionRatio());
EXPECT_EQ(1, stats_.GetMaxColumnTiles());
EXPECT_EQ(11, stats_.GetMinimumAltrefDistance());
EXPECT_EQ(3, stats_.GetMaxReferenceFrames());
EXPECT_TRUE(stats_.estimate_last_frame_duration());
stats_.set_estimate_last_frame_duration(false);
EXPECT_DOUBLE_EQ(275.512, stats_.GetAverageBitRate());
}
TEST_F(Vp9LevelStatsTests, Muxed) {
CreateAndLoadSegment("bbb_480p_vp9_opus_1second.webm", 4);
ProcessTheFrames();
EXPECT_EQ(854, parser_.width());
EXPECT_EQ(480, parser_.height());
EXPECT_EQ(2, parser_.column_tiles());
EXPECT_EQ(1, parser_.frame_parallel_mode());
EXPECT_EQ(30, stats_.GetLevel());
EXPECT_EQ(9838080, stats_.GetMaxLumaSampleRate());
EXPECT_EQ(409920, stats_.GetMaxLumaPictureSize());
EXPECT_DOUBLE_EQ(447.09394572025053, stats_.GetAverageBitRate());
EXPECT_DOUBLE_EQ(118.464, stats_.GetMaxCpbSize());
EXPECT_DOUBLE_EQ(241.17670131398313, stats_.GetCompressionRatio());
EXPECT_EQ(2, stats_.GetMaxColumnTiles());
EXPECT_EQ(9, stats_.GetMinimumAltrefDistance());
EXPECT_EQ(3, stats_.GetMaxReferenceFrames());
stats_.set_estimate_last_frame_duration(false);
EXPECT_DOUBLE_EQ(468.38413361169108, stats_.GetAverageBitRate());
}
TEST_F(Vp9LevelStatsTests, SetDuration) {
CreateAndLoadSegment("test_stereo_left_right.webm");
ProcessTheFrames();
const int64_t kDurationNano = 2080000000; // 2.08 seconds
stats_.set_duration(kDurationNano);
EXPECT_EQ(256, parser_.width());
EXPECT_EQ(144, parser_.height());
EXPECT_EQ(1, parser_.column_tiles());
EXPECT_EQ(0, parser_.frame_parallel_mode());
EXPECT_EQ(11, stats_.GetLevel());
EXPECT_EQ(479232, stats_.GetMaxLumaSampleRate());
EXPECT_EQ(36864, stats_.GetMaxLumaPictureSize());
EXPECT_DOUBLE_EQ(264.9153846153846, stats_.GetAverageBitRate());
EXPECT_DOUBLE_EQ(147.136, stats_.GetMaxCpbSize());
EXPECT_DOUBLE_EQ(19.267458404715583, stats_.GetCompressionRatio());
EXPECT_EQ(1, stats_.GetMaxColumnTiles());
EXPECT_EQ(11, stats_.GetMinimumAltrefDistance());
EXPECT_EQ(3, stats_.GetMaxReferenceFrames());
}
} // namespace
int main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
@@ -0,0 +1,20 @@
// 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.
#ifndef LIBWEBM_COMMON_WEBM_CONSTANTS_H_
#define LIBWEBM_COMMON_WEBM_CONSTANTS_H_
namespace libwebm {
const double kNanosecondsPerSecond = 1000000000.0;
const int kNanosecondsPerSecondi = 1000000000;
const int kNanosecondsPerMillisecond = 1000000;
} // namespace libwebm
#endif // LIBWEBM_COMMON_WEBM_CONSTANTS_H_
@@ -0,0 +1,85 @@
// 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 "common/webm_endian.h"
#include <stdint.h>
namespace {
// Swaps unsigned 32 bit values to big endian if needed. Returns |value|
// unmodified if architecture is big endian. Returns swapped bytes of |value|
// if architecture is little endian. Returns 0 otherwise.
uint32_t swap32_check_little_endian(uint32_t value) {
// Check endianness.
union {
uint32_t val32;
uint8_t c[4];
} check;
check.val32 = 0x01234567U;
// Check for big endian.
if (check.c[3] == 0x67)
return value;
// Check for not little endian.
if (check.c[0] != 0x67)
return 0;
return value << 24 | ((value << 8) & 0x0000FF00U) |
((value >> 8) & 0x00FF0000U) | value >> 24;
}
// Swaps unsigned 64 bit values to big endian if needed. Returns |value|
// unmodified if architecture is big endian. Returns swapped bytes of |value|
// if architecture is little endian. Returns 0 otherwise.
uint64_t swap64_check_little_endian(uint64_t value) {
// Check endianness.
union {
uint64_t val64;
uint8_t c[8];
} check;
check.val64 = 0x0123456789ABCDEFULL;
// Check for big endian.
if (check.c[7] == 0xEF)
return value;
// Check for not little endian.
if (check.c[0] != 0xEF)
return 0;
return value << 56 | ((value << 40) & 0x00FF000000000000ULL) |
((value << 24) & 0x0000FF0000000000ULL) |
((value << 8) & 0x000000FF00000000ULL) |
((value >> 8) & 0x00000000FF000000ULL) |
((value >> 24) & 0x0000000000FF0000ULL) |
((value >> 40) & 0x000000000000FF00ULL) | value >> 56;
}
} // namespace
namespace libwebm {
uint32_t host_to_bigendian(uint32_t value) {
return swap32_check_little_endian(value);
}
uint32_t bigendian_to_host(uint32_t value) {
return swap32_check_little_endian(value);
}
uint64_t host_to_bigendian(uint64_t value) {
return swap64_check_little_endian(value);
}
uint64_t bigendian_to_host(uint64_t value) {
return swap64_check_little_endian(value);
}
} // namespace libwebm
@@ -0,0 +1,38 @@
// 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.
#ifndef LIBWEBM_COMMON_WEBM_ENDIAN_H_
#define LIBWEBM_COMMON_WEBM_ENDIAN_H_
#include <stdint.h>
namespace libwebm {
// Swaps unsigned 32 bit values to big endian if needed. Returns |value| if
// architecture is big endian. Returns big endian value if architecture is
// little endian. Returns 0 otherwise.
uint32_t host_to_bigendian(uint32_t value);
// Swaps unsigned 32 bit values to little endian if needed. Returns |value| if
// architecture is big endian. Returns little endian value if architecture is
// little endian. Returns 0 otherwise.
uint32_t bigendian_to_host(uint32_t value);
// Swaps unsigned 64 bit values to big endian if needed. Returns |value| if
// architecture is big endian. Returns big endian value if architecture is
// little endian. Returns 0 otherwise.
uint64_t host_to_bigendian(uint64_t value);
// Swaps unsigned 64 bit values to little endian if needed. Returns |value| if
// architecture is big endian. Returns little endian value if architecture is
// little endian. Returns 0 otherwise.
uint64_t bigendian_to_host(uint64_t value);
} // namespace libwebm
#endif // LIBWEBM_COMMON_WEBM_ENDIAN_H_
@@ -0,0 +1,193 @@
// 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.
#ifndef COMMON_WEBMIDS_H_
#define COMMON_WEBMIDS_H_
namespace libwebm {
enum MkvId {
kMkvEBML = 0x1A45DFA3,
kMkvEBMLVersion = 0x4286,
kMkvEBMLReadVersion = 0x42F7,
kMkvEBMLMaxIDLength = 0x42F2,
kMkvEBMLMaxSizeLength = 0x42F3,
kMkvDocType = 0x4282,
kMkvDocTypeVersion = 0x4287,
kMkvDocTypeReadVersion = 0x4285,
kMkvVoid = 0xEC,
kMkvSignatureSlot = 0x1B538667,
kMkvSignatureAlgo = 0x7E8A,
kMkvSignatureHash = 0x7E9A,
kMkvSignaturePublicKey = 0x7EA5,
kMkvSignature = 0x7EB5,
kMkvSignatureElements = 0x7E5B,
kMkvSignatureElementList = 0x7E7B,
kMkvSignedElement = 0x6532,
// segment
kMkvSegment = 0x18538067,
// Meta Seek Information
kMkvSeekHead = 0x114D9B74,
kMkvSeek = 0x4DBB,
kMkvSeekID = 0x53AB,
kMkvSeekPosition = 0x53AC,
// Segment Information
kMkvInfo = 0x1549A966,
kMkvTimecodeScale = 0x2AD7B1,
kMkvDuration = 0x4489,
kMkvDateUTC = 0x4461,
kMkvTitle = 0x7BA9,
kMkvMuxingApp = 0x4D80,
kMkvWritingApp = 0x5741,
// Cluster
kMkvCluster = 0x1F43B675,
kMkvTimecode = 0xE7,
kMkvPrevSize = 0xAB,
kMkvBlockGroup = 0xA0,
kMkvBlock = 0xA1,
kMkvBlockDuration = 0x9B,
kMkvReferenceBlock = 0xFB,
kMkvLaceNumber = 0xCC,
kMkvSimpleBlock = 0xA3,
kMkvBlockAdditions = 0x75A1,
kMkvBlockMore = 0xA6,
kMkvBlockAddID = 0xEE,
kMkvBlockAdditional = 0xA5,
kMkvDiscardPadding = 0x75A2,
// Track
kMkvTracks = 0x1654AE6B,
kMkvTrackEntry = 0xAE,
kMkvTrackNumber = 0xD7,
kMkvTrackUID = 0x73C5,
kMkvTrackType = 0x83,
kMkvFlagEnabled = 0xB9,
kMkvFlagDefault = 0x88,
kMkvFlagForced = 0x55AA,
kMkvFlagLacing = 0x9C,
kMkvDefaultDuration = 0x23E383,
kMkvMaxBlockAdditionID = 0x55EE,
kMkvName = 0x536E,
kMkvLanguage = 0x22B59C,
kMkvCodecID = 0x86,
kMkvCodecPrivate = 0x63A2,
kMkvCodecName = 0x258688,
kMkvCodecDelay = 0x56AA,
kMkvSeekPreRoll = 0x56BB,
// video
kMkvVideo = 0xE0,
kMkvFlagInterlaced = 0x9A,
kMkvStereoMode = 0x53B8,
kMkvAlphaMode = 0x53C0,
kMkvPixelWidth = 0xB0,
kMkvPixelHeight = 0xBA,
kMkvPixelCropBottom = 0x54AA,
kMkvPixelCropTop = 0x54BB,
kMkvPixelCropLeft = 0x54CC,
kMkvPixelCropRight = 0x54DD,
kMkvDisplayWidth = 0x54B0,
kMkvDisplayHeight = 0x54BA,
kMkvDisplayUnit = 0x54B2,
kMkvAspectRatioType = 0x54B3,
kMkvColourSpace = 0x2EB524,
kMkvFrameRate = 0x2383E3,
// end video
// colour
kMkvColour = 0x55B0,
kMkvMatrixCoefficients = 0x55B1,
kMkvBitsPerChannel = 0x55B2,
kMkvChromaSubsamplingHorz = 0x55B3,
kMkvChromaSubsamplingVert = 0x55B4,
kMkvCbSubsamplingHorz = 0x55B5,
kMkvCbSubsamplingVert = 0x55B6,
kMkvChromaSitingHorz = 0x55B7,
kMkvChromaSitingVert = 0x55B8,
kMkvRange = 0x55B9,
kMkvTransferCharacteristics = 0x55BA,
kMkvPrimaries = 0x55BB,
kMkvMaxCLL = 0x55BC,
kMkvMaxFALL = 0x55BD,
// mastering metadata
kMkvMasteringMetadata = 0x55D0,
kMkvPrimaryRChromaticityX = 0x55D1,
kMkvPrimaryRChromaticityY = 0x55D2,
kMkvPrimaryGChromaticityX = 0x55D3,
kMkvPrimaryGChromaticityY = 0x55D4,
kMkvPrimaryBChromaticityX = 0x55D5,
kMkvPrimaryBChromaticityY = 0x55D6,
kMkvWhitePointChromaticityX = 0x55D7,
kMkvWhitePointChromaticityY = 0x55D8,
kMkvLuminanceMax = 0x55D9,
kMkvLuminanceMin = 0x55DA,
// end mastering metadata
// end colour
// projection
kMkvProjection = 0x7670,
kMkvProjectionType = 0x7671,
kMkvProjectionPrivate = 0x7672,
kMkvProjectionPoseYaw = 0x7673,
kMkvProjectionPosePitch = 0x7674,
kMkvProjectionPoseRoll = 0x7675,
// end projection
// audio
kMkvAudio = 0xE1,
kMkvSamplingFrequency = 0xB5,
kMkvOutputSamplingFrequency = 0x78B5,
kMkvChannels = 0x9F,
kMkvBitDepth = 0x6264,
// end audio
// ContentEncodings
kMkvContentEncodings = 0x6D80,
kMkvContentEncoding = 0x6240,
kMkvContentEncodingOrder = 0x5031,
kMkvContentEncodingScope = 0x5032,
kMkvContentEncodingType = 0x5033,
kMkvContentCompression = 0x5034,
kMkvContentCompAlgo = 0x4254,
kMkvContentCompSettings = 0x4255,
kMkvContentEncryption = 0x5035,
kMkvContentEncAlgo = 0x47E1,
kMkvContentEncKeyID = 0x47E2,
kMkvContentSignature = 0x47E3,
kMkvContentSigKeyID = 0x47E4,
kMkvContentSigAlgo = 0x47E5,
kMkvContentSigHashAlgo = 0x47E6,
kMkvContentEncAESSettings = 0x47E7,
kMkvAESSettingsCipherMode = 0x47E8,
kMkvAESSettingsCipherInitData = 0x47E9,
// end ContentEncodings
// Cueing Data
kMkvCues = 0x1C53BB6B,
kMkvCuePoint = 0xBB,
kMkvCueTime = 0xB3,
kMkvCueTrackPositions = 0xB7,
kMkvCueTrack = 0xF7,
kMkvCueClusterPosition = 0xF1,
kMkvCueBlockNumber = 0x5378,
// Chapters
kMkvChapters = 0x1043A770,
kMkvEditionEntry = 0x45B9,
kMkvChapterAtom = 0xB6,
kMkvChapterUID = 0x73C4,
kMkvChapterStringUID = 0x5654,
kMkvChapterTimeStart = 0x91,
kMkvChapterTimeEnd = 0x92,
kMkvChapterDisplay = 0x80,
kMkvChapString = 0x85,
kMkvChapLanguage = 0x437C,
kMkvChapCountry = 0x437E,
// Tags
kMkvTags = 0x1254C367,
kMkvTag = 0x7373,
kMkvSimpleTag = 0x67C8,
kMkvTagName = 0x45A3,
kMkvTagString = 0x4487
};
} // namespace libwebm
#endif // COMMON_WEBMIDS_H_