From 483a0ff800c9293372dd2c64a5cab9b79c471131 Mon Sep 17 00:00:00 2001 From: Tom Finegan Date: Mon, 29 Aug 2016 10:07:45 -0700 Subject: [PATCH] mkvmuxer: Add Projection element support. Part of the Spherical Video V2 draft specification: https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md Change-Id: Ia935ba975a0f01c1fb2919325ab7f70254c2ed10 --- common/webmids.h | 8 +++ mkvmuxer/mkvmuxer.cc | 119 ++++++++++++++++++++++++++++++++++++++++++- mkvmuxer/mkvmuxer.h | 56 ++++++++++++++++++++ 3 files changed, 181 insertions(+), 2 deletions(-) diff --git a/common/webmids.h b/common/webmids.h index 32a0c5f..89d722a 100644 --- a/common/webmids.h +++ b/common/webmids.h @@ -124,6 +124,14 @@ enum MkvId { 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, diff --git a/mkvmuxer/mkvmuxer.cc b/mkvmuxer/mkvmuxer.cc index b2c4c4a..3a148f3 100644 --- a/mkvmuxer/mkvmuxer.cc +++ b/mkvmuxer/mkvmuxer.cc @@ -1228,6 +1228,89 @@ uint64_t Colour::PayloadSize() const { return size; } +/////////////////////////////////////////////////////////////// +// +// Projection element + +uint64_t Projection::ProjectionSize() const { + uint64_t size = PayloadSize(); + + if (size > 0) + size += EbmlMasterElementSize(libwebm::kMkvProjection, size); + + return size; +} + +bool Projection::Write(IMkvWriter* writer) const { + const uint64_t size = PayloadSize(); + + // Don't write an empty element. + if (size == 0) + return true; + + if (!WriteEbmlMasterElement(writer, libwebm::kMkvProjection, size)) + return false; + + if (!WriteEbmlElement(writer, libwebm::kMkvProjectionType, + static_cast(type_))) { + return false; + } + + if (private_data_length_ > 0 && private_data_ != NULL && + !WriteEbmlElement(writer, libwebm::kMkvProjectionPrivate, private_data_, + private_data_length_)) { + return false; + } + + if (!WriteEbmlElement(writer, libwebm::kMkvProjectionPoseYaw, pose_yaw_)) + return false; + + if (!WriteEbmlElement(writer, libwebm::kMkvProjectionPosePitch, pose_pitch_)) { + return false; + } + + if (!WriteEbmlElement(writer, libwebm::kMkvProjectionPoseRoll, pose_roll_)) { + return false; + } + + return true; +} + +bool Projection::SetProjectionPrivate(const uint8_t* data, + uint64_t data_length) { + if (data == NULL || data_length == 0) { + return false; + } + + uint8_t* new_private_data = new (std::nothrow) uint8_t[data_length]; + if (new_private_data == NULL) { + return false; + } + + delete[] private_data_; + private_data_ = new_private_data; + private_data_length_ = data_length; + memcpy(private_data_, data, static_cast(data_length)); + + return true; +} + +uint64_t Projection::PayloadSize() const { + uint64_t size = + EbmlElementSize(libwebm::kMkvProjection, static_cast(type_)); + + if (private_data_length_ > 0 && private_data_ != NULL) { + size += EbmlElementSize(libwebm::kMkvProjectionPrivate, private_data_, + private_data_length_); + } + + size += EbmlElementSize(libwebm::kMkvProjectionPoseYaw, pose_yaw_); + size += EbmlElementSize(libwebm::kMkvProjectionPosePitch, pose_pitch_); + size += EbmlElementSize(libwebm::kMkvProjectionPoseRoll, pose_roll_); + + return size; +} + /////////////////////////////////////////////////////////////// // // VideoTrack Class @@ -1245,9 +1328,13 @@ VideoTrack::VideoTrack(unsigned int* seed) stereo_mode_(0), alpha_mode_(0), width_(0), - colour_(NULL) {} + colour_(NULL), + projection_(NULL) {} -VideoTrack::~VideoTrack() { delete colour_; } +VideoTrack::~VideoTrack() { + delete colour_; + delete projection_; +} bool VideoTrack::SetStereoMode(uint64_t stereo_mode) { if (stereo_mode != kMono && stereo_mode != kSideBySideLeftIsFirst && @@ -1346,6 +1433,10 @@ bool VideoTrack::Write(IMkvWriter* writer) const { if (!colour_->Write(writer)) return false; } + if (projection_) { + if (!projection_->Write(writer)) + return false; + } const int64_t stop_position = writer->Position(); if (stop_position < 0 || @@ -1384,6 +1475,27 @@ bool VideoTrack::SetColour(const Colour& colour) { return true; } +bool VideoTrack::SetProjection(const Projection& projection) { + std::auto_ptr projection_ptr(new Projection()); + if (!projection_ptr.get()) + return false; + + if (projection.private_data()) { + if (!projection_ptr->SetProjectionPrivate( + projection.private_data(), projection.private_data_length())) { + return false; + } + } + + projection_ptr->set_type(projection.type()); + projection_ptr->set_pose_yaw(projection.pose_yaw()); + projection_ptr->set_pose_pitch(projection.pose_pitch()); + projection_ptr->set_pose_roll(projection.pose_roll()); + delete projection_; + projection_ = projection_ptr.release(); + return true; +} + uint64_t VideoTrack::VideoPayloadSize() const { uint64_t size = EbmlElementSize(libwebm::kMkvPixelWidth, static_cast(width_)); @@ -1419,6 +1531,9 @@ uint64_t VideoTrack::VideoPayloadSize() const { if (colour_) size += colour_->ColourSize(); + if (projection_) + size += projection_->ProjectionSize(); + return size; } diff --git a/mkvmuxer/mkvmuxer.h b/mkvmuxer/mkvmuxer.h index 7cb0636..80907a4 100644 --- a/mkvmuxer/mkvmuxer.h +++ b/mkvmuxer/mkvmuxer.h @@ -464,6 +464,56 @@ class Colour { MasteringMetadata* mastering_metadata_; }; +/////////////////////////////////////////////////////////////// +// Projection element. +class Projection { + public: + enum ProjectionType { + kTypeNotPresent = -1, + kRectangular = 0, + kEquirectangular = 1, + kCubeMap = 2, + kMesh = 3, + }; + static const uint64_t kValueNotPresent; + Projection() + : type_(kRectangular), + pose_yaw_(0.0), + pose_pitch_(0.0), + pose_roll_(0.0), + private_data_(NULL), + private_data_length_(0) {} + ~Projection() { delete[] private_data_; } + + uint64_t ProjectionSize() const; + bool Write(IMkvWriter* writer) const; + + bool SetProjectionPrivate(const uint8_t* private_data, + uint64_t private_data_length); + + ProjectionType type() const { return type_; } + void set_type(ProjectionType type) { type_ = type; } + float pose_yaw() const { return pose_yaw_; } + void set_pose_yaw(float pose_yaw) { pose_yaw_ = pose_yaw; } + float pose_pitch() const { return pose_pitch_; } + void set_pose_pitch(float pose_pitch) { pose_pitch_ = pose_pitch; } + float pose_roll() const { return pose_roll_; } + void set_pose_roll(float pose_roll) { pose_roll_ = pose_roll; } + uint8_t* private_data() const { return private_data_; } + uint64_t private_data_length() const { return private_data_length_; } + + private: + // Returns size of VideoProjection child elements. + uint64_t PayloadSize() const; + + ProjectionType type_; + float pose_yaw_; + float pose_pitch_; + float pose_roll_; + uint8_t* private_data_; + uint64_t private_data_length_; +}; + /////////////////////////////////////////////////////////////// // Track element. class Track { @@ -611,6 +661,11 @@ class VideoTrack : public Track { // Deep copies |colour|. bool SetColour(const Colour& colour); + Projection* projection() { return projection_; }; + + // Deep copies |projection|. + bool SetProjection(const Projection& projection); + private: // Returns the size in bytes of the Video element. uint64_t VideoPayloadSize() const; @@ -629,6 +684,7 @@ class VideoTrack : public Track { uint64_t width_; Colour* colour_; + Projection* projection_; LIBWEBM_DISALLOW_COPY_AND_ASSIGN(VideoTrack); };