From a281a22955000ad5c1bdb49c4761b39bcf5ea5cd Mon Sep 17 00:00:00 2001 From: Tom Finegan Date: Mon, 8 Feb 2016 15:21:45 -0800 Subject: [PATCH] mkvmuxer: Add support for the Colour element and its children. Change-Id: I8026a2ba6c664cbf0ce0a6602a79942283b194b1 --- mkvmuxer.cpp | 283 ++++++++++++++++++++++++++++++++++++++++++++++++++- mkvmuxer.hpp | 104 +++++++++++++++++++ 2 files changed, 382 insertions(+), 5 deletions(-) diff --git a/mkvmuxer.cpp b/mkvmuxer.cpp index ed6b194..c58553e 100644 --- a/mkvmuxer.cpp +++ b/mkvmuxer.cpp @@ -8,11 +8,13 @@ #include "mkvmuxer.hpp" +#include #include #include #include #include #include +#include #include #include "mkvmuxerutil.hpp" @@ -27,6 +29,9 @@ namespace mkvmuxer { +const float MasteringMetadata::kUnspecifiedMmValue = FLT_MAX; +const uint64 Colour::kUnspecifiedColourValue = UINT64_MAX; + namespace { // Deallocate the string designated by |dst|, and then copy the |src| // string to |dst|. The caller owns both the |src| string and the @@ -55,6 +60,20 @@ bool StrCpy(const char* src, char** dst_ptr) { strcpy(dst, src); // NOLINT return true; } + +typedef std::auto_ptr PrimaryChromaticityPtr; +bool CopyChromaticity(const PrimaryChromaticity* src, + PrimaryChromaticityPtr* dst) { + if (!dst) + return false; + + dst->reset(new (std::nothrow) PrimaryChromaticity(src->x, src->y)); + if (!dst->get()) + return false; + + return true; +} + } // namespace /////////////////////////////////////////////////////////////// @@ -842,6 +861,231 @@ void Track::set_name(const char* name) { } } +/////////////////////////////////////////////////////////////// +// +// Colour and its child elements + +uint64 PrimaryChromaticity::PrimaryChromaticityPayloadSize(MkvId x_id, + MkvId y_id) const { + return EbmlElementSize(x_id, x) + EbmlElementSize(y_id, y); +} + +bool PrimaryChromaticity::Write(IMkvWriter* writer, MkvId x_id, + MkvId y_id) const { + return WriteEbmlElement(writer, x_id, x) && WriteEbmlElement(writer, y_id, y); +} + +uint64 MasteringMetadata::MasteringMetadataPayloadSize() const { + uint64 size = 0; + + if (luminance_max != kUnspecifiedMmValue) + size += EbmlElementSize(kMkvLuminanceMax, luminance_max); + if (luminance_min != kUnspecifiedMmValue) + size += EbmlElementSize(kMkvLuminanceMin, luminance_min); + + if (r_) { + size += r_->PrimaryChromaticityPayloadSize(kMkvPrimaryRChromaticityX, + kMkvPrimaryRChromaticityY); + } + if (g_) { + size += g_->PrimaryChromaticityPayloadSize(kMkvPrimaryGChromaticityX, + kMkvPrimaryGChromaticityY); + } + if (b_) { + size += b_->PrimaryChromaticityPayloadSize(kMkvPrimaryBChromaticityX, + kMkvPrimaryBChromaticityY); + } + if (white_point_) { + size += white_point_->PrimaryChromaticityPayloadSize( + kMkvWhitePointChromaticityX, kMkvWhitePointChromaticityY); + } + + if (size > 0) + size += EbmlMasterElementSize(kMkvMasteringMetadata, size); + + return size; +} + +bool MasteringMetadata::Write(IMkvWriter* writer) const { + const uint64 size = MasteringMetadataPayloadSize(); + + // Don't write an empty element. + if (size == 0) + return true; + + if (!WriteEbmlMasterElement(writer, kMkvMasteringMetadata, size)) + return false; + if (luminance_max != kUnspecifiedMmValue && + !WriteEbmlElement(writer, kMkvLuminanceMax, luminance_max)) { + return false; + } + if (luminance_min != kUnspecifiedMmValue && + !WriteEbmlElement(writer, kMkvLuminanceMin, luminance_min)) { + return false; + } + if (r_ && + !r_->Write(writer, kMkvPrimaryRChromaticityX, + kMkvPrimaryRChromaticityY)) { + return false; + } + if (g_ && + !g_->Write(writer, kMkvPrimaryGChromaticityX, + kMkvPrimaryGChromaticityY)) { + return false; + } + if (b_ && + !b_->Write(writer, kMkvPrimaryBChromaticityX, + kMkvPrimaryBChromaticityY)) { + return false; + } + if (white_point_ && + !white_point_->Write(writer, kMkvWhitePointChromaticityX, + kMkvWhitePointChromaticityY)) { + return false; + } + + return true; +} + +bool MasteringMetadata::SetChromaticity( + const PrimaryChromaticity* r, const PrimaryChromaticity* g, + const PrimaryChromaticity* b, const PrimaryChromaticity* white_point) { + PrimaryChromaticityPtr r_ptr(NULL); + if (r) { + if (!CopyChromaticity(r, &r_ptr)) + return false; + } + PrimaryChromaticityPtr g_ptr(NULL); + if (g) { + if (!CopyChromaticity(g, &g_ptr)) + return false; + } + PrimaryChromaticityPtr b_ptr(NULL); + if (b) { + if (!CopyChromaticity(b, &b_ptr)) + return false; + } + PrimaryChromaticityPtr wp_ptr(NULL); + if (white_point) { + if (!CopyChromaticity(white_point, &wp_ptr)) + return false; + } + + r_ = r_ptr.release(); + g_ = g_ptr.release(); + b_ = b_ptr.release(); + white_point_ = wp_ptr.release(); + return true; +} + +uint64 Colour::ColourPayloadSize() const { + uint64 size = 0; + + if (matrix != kUnspecifiedColourValue) + size += EbmlElementSize(kMkvMatrix, matrix); + if (bits_per_channel != kUnspecifiedColourValue) + size += EbmlElementSize(kMkvBitsPerChannel, bits_per_channel); + if (chroma_subsampling != kUnspecifiedColourValue) + size += EbmlElementSize(kMkvChromaSubsampling, chroma_subsampling); + if (chroma_siting_horz != kUnspecifiedColourValue) + size += EbmlElementSize(kMkvChromaSitingHorz, chroma_siting_horz); + if (chroma_siting_vert != kUnspecifiedColourValue) + size += EbmlElementSize(kMkvChromaSitingVert, chroma_siting_vert); + if (range != kUnspecifiedColourValue) + size += EbmlElementSize(kMkvRange, range); + if (transfer_function != kUnspecifiedColourValue) + size += EbmlElementSize(kMkvTransferFunction, transfer_function); + if (primaries != kUnspecifiedColourValue) + size += EbmlElementSize(kMkvPrimaries, primaries); + if (max_cll != kUnspecifiedColourValue) + size += EbmlElementSize(kMkvMaxCLL, max_cll); + if (max_fall != kUnspecifiedColourValue) + size += EbmlElementSize(kMkvMaxFALL, max_fall); + + if (mastering_metadata_) + size += mastering_metadata_->MasteringMetadataPayloadSize(); + + if (size > 0) + size += EbmlMasterElementSize(kMkvColour, size); + + return size; +} + +bool Colour::Write(IMkvWriter* writer) const { + const uint64 size = ColourPayloadSize(); + + // Don't write an empty element. + if (size == 0) + return true; + + if (!WriteEbmlMasterElement(writer, kMkvColour, size)) + return false; + + if (matrix != kUnspecifiedColourValue && + !WriteEbmlElement(writer, kMkvMatrix, matrix)) { + return false; + } + if (bits_per_channel != kUnspecifiedColourValue && + !WriteEbmlElement(writer, kMkvBitsPerChannel, bits_per_channel)) { + return false; + } + if (chroma_subsampling != kUnspecifiedColourValue && + !WriteEbmlElement(writer, kMkvChromaSubsampling, chroma_subsampling)) { + return false; + } + if (chroma_siting_horz != kUnspecifiedColourValue && + !WriteEbmlElement(writer, kMkvChromaSitingHorz, chroma_siting_horz)) { + return false; + } + if (chroma_siting_vert != kUnspecifiedColourValue && + !WriteEbmlElement(writer, kMkvChromaSitingVert, chroma_siting_vert)) { + return false; + } + if (range != kUnspecifiedColourValue && + !WriteEbmlElement(writer, kMkvRange, range)) { + return false; + } + if (transfer_function != kUnspecifiedColourValue && + !WriteEbmlElement(writer, kMkvTransferFunction, transfer_function)) { + return false; + } + if (primaries != kUnspecifiedColourValue && + !WriteEbmlElement(writer, kMkvPrimaries, primaries)) { + return false; + } + if (max_cll != kUnspecifiedColourValue && + !WriteEbmlElement(writer, kMkvMaxCLL, max_cll)) { + return false; + } + if (max_fall != kUnspecifiedColourValue && + !WriteEbmlElement(writer, kMkvMaxFALL, max_fall)) { + return false; + } + + if (mastering_metadata_ && !mastering_metadata_->Write(writer)) + return false; + + return true; +} + +bool Colour::SetMasteringMetadata(const MasteringMetadata& mastering_metadata) { + std::auto_ptr mm_ptr(new MasteringMetadata()); + if (!mm_ptr.get()) + return false; + + mm_ptr->luminance_max = mastering_metadata.luminance_max; + mm_ptr->luminance_min = mastering_metadata.luminance_min; + + if (!mm_ptr->SetChromaticity(mastering_metadata.r(), mastering_metadata.g(), + mastering_metadata.b(), + mastering_metadata.white_point())) { + return false; + } + + delete mastering_metadata_; + mastering_metadata_ = mm_ptr.release(); + return true; +} /////////////////////////////////////////////////////////////// // // VideoTrack Class @@ -858,9 +1102,10 @@ VideoTrack::VideoTrack(unsigned int* seed) height_(0), stereo_mode_(0), alpha_mode_(0), - width_(0) {} + width_(0), + colour_(NULL) {} -VideoTrack::~VideoTrack() {} +VideoTrack::~VideoTrack() { delete colour_; } bool VideoTrack::SetStereoMode(uint64 stereo_mode) { if (stereo_mode != kMono && stereo_mode != kSideBySideLeftIsFirst && @@ -945,6 +1190,10 @@ bool VideoTrack::Write(IMkvWriter* writer) const { return false; } } + if (colour_) { + if (!colour_->Write(writer)) + return false; + } const int64 stop_position = writer->Position(); if (stop_position < 0 || @@ -955,6 +1204,30 @@ bool VideoTrack::Write(IMkvWriter* writer) const { return true; } +bool VideoTrack::SetColour(const Colour& colour) { + std::auto_ptr colour_ptr(new Colour()); + if (!colour_ptr.get()) + return false; + + if (colour.mastering_metadata()) { + if (!colour_ptr->SetMasteringMetadata(*colour.mastering_metadata())) + return false; + } + + colour_ptr->matrix = colour.matrix; + colour_ptr->primaries = colour.bits_per_channel; + colour_ptr->chroma_subsampling = colour.chroma_subsampling; + colour_ptr->chroma_siting_horz = colour.chroma_siting_horz; + colour_ptr->chroma_siting_vert = colour.chroma_siting_vert; + colour_ptr->range = colour.range; + colour_ptr->transfer_function = colour.transfer_function; + colour_ptr->primaries = colour.primaries; + colour_ptr->max_cll = colour.max_cll; + colour_ptr->max_fall = colour.max_fall; + colour_ = colour_ptr.release(); + return true; +} + uint64 VideoTrack::VideoPayloadSize() const { uint64 size = EbmlElementSize(kMkvPixelWidth, width_); size += EbmlElementSize(kMkvPixelHeight, height_); @@ -976,6 +1249,8 @@ uint64 VideoTrack::VideoPayloadSize() const { size += EbmlElementSize(kMkvAlphaMode, alpha_mode_); if (frame_rate_ > 0.0) size += EbmlElementSize(kMkvFrameRate, static_cast(frame_rate_)); + if (colour_) + size += colour_->ColourPayloadSize(); return size; } @@ -1048,9 +1323,7 @@ const char Tracks::kVp9CodecId[] = "V_VP9"; const char Tracks::kVp10CodecId[] = "V_VP10"; Tracks::Tracks() - : track_entries_(NULL), - track_entries_size_(0), - wrote_tracks_(false) {} + : track_entries_(NULL), track_entries_size_(0), wrote_tracks_(false) {} Tracks::~Tracks() { if (track_entries_) { diff --git a/mkvmuxer.hpp b/mkvmuxer.hpp index da00c8f..818c66e 100644 --- a/mkvmuxer.hpp +++ b/mkvmuxer.hpp @@ -9,7 +9,10 @@ #ifndef MKVMUXER_HPP #define MKVMUXER_HPP +#include + #include "mkvmuxertypes.hpp" +#include "webmids.hpp" // For a description of the WebM elements see // http://www.webmproject.org/code/specs/container/. @@ -329,6 +332,100 @@ class ContentEncoding { LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncoding); }; +/////////////////////////////////////////////////////////////// +// Colour element. +struct PrimaryChromaticity { + PrimaryChromaticity(float x_val, float y_val) : x(x_val), y(y_val) {} + PrimaryChromaticity() : x(0), y(0) {} + ~PrimaryChromaticity() {} + uint64 PrimaryChromaticityPayloadSize(MkvId x_id, MkvId y_id) const; + bool Write(IMkvWriter* writer, MkvId x_id, MkvId y_id) const; + + float x; + float y; +}; + +class MasteringMetadata { + public: + static const float kUnspecifiedMmValue; + + MasteringMetadata() + : luminance_max(kUnspecifiedMmValue), + luminance_min(kUnspecifiedMmValue), + r_(NULL), + g_(NULL), + b_(NULL), + white_point_(NULL) {} + ~MasteringMetadata() { + delete r_; + delete g_; + delete b_; + delete white_point_; + } + uint64 MasteringMetadataPayloadSize() const; + bool Write(IMkvWriter* writer) const; + + // Copies non-null chromaticity. + bool SetChromaticity(const PrimaryChromaticity* r, + const PrimaryChromaticity* g, + const PrimaryChromaticity* b, + const PrimaryChromaticity* white_point); + const PrimaryChromaticity* r() const { return r_; } + const PrimaryChromaticity* g() const { return g_; } + const PrimaryChromaticity* b() const { return b_; } + const PrimaryChromaticity* white_point() const { return white_point_; } + + float luminance_max; + float luminance_min; + + private: + PrimaryChromaticity* r_; + PrimaryChromaticity* g_; + PrimaryChromaticity* b_; + PrimaryChromaticity* white_point_; +}; + +class Colour { + public: + static const uint64 kUnspecifiedColourValue; + Colour() + : matrix(kUnspecifiedColourValue), + bits_per_channel(kUnspecifiedColourValue), + chroma_subsampling(kUnspecifiedColourValue), + chroma_siting_horz(kUnspecifiedColourValue), + chroma_siting_vert(kUnspecifiedColourValue), + range(kUnspecifiedColourValue), + transfer_function(kUnspecifiedColourValue), + primaries(kUnspecifiedColourValue), + max_cll(kUnspecifiedColourValue), + max_fall(kUnspecifiedColourValue), + mastering_metadata_(NULL) {} + ~Colour() { delete mastering_metadata_; } + uint64 ColourPayloadSize() const; + bool Write(IMkvWriter* writer) const; + + // Deep copies |mastering_metadata|. + bool SetMasteringMetadata(const MasteringMetadata& mastering_metadata); + + const MasteringMetadata* mastering_metadata() const { + return mastering_metadata_; + } + + uint64 matrix; + uint64 bits_per_channel; + uint64 chroma_subsampling; + uint64 chroma_siting_horz; + uint64 chroma_siting_vert; + uint64 range; + uint64 transfer_function; + uint64 primaries; + uint64 max_cll; + uint64 max_fall; + + private: + MasteringMetadata* mastering_metadata_; +}; + /////////////////////////////////////////////////////////////// // Track element. class Track { @@ -471,6 +568,11 @@ class VideoTrack : public Track { void set_width(uint64 width) { width_ = width; } uint64 width() const { return width_; } + Colour* colour() { return colour_; } + + // Deep copies |colour|. + bool SetColour(const Colour& colour); + private: // Returns the size in bytes of the Video element. uint64 VideoPayloadSize() const; @@ -488,6 +590,8 @@ class VideoTrack : public Track { uint64 alpha_mode_; uint64 width_; + Colour* colour_; + LIBWEBM_DISALLOW_COPY_AND_ASSIGN(VideoTrack); };