From e69050224b95f1f0546009a5f9d470416dedcc0d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 24 Nov 2022 03:15:45 +0100 Subject: [PATCH] Implement MP4 transformation matrixes with support for basic rotations --- lib/mp4_generic.cpp | 70 +++++++++++++++++++++++++++++++++++++-------- lib/mp4_generic.h | 6 ++-- 2 files changed, 62 insertions(+), 14 deletions(-) diff --git a/lib/mp4_generic.cpp b/lib/mp4_generic.cpp index 97e902e2..dc9bfd04 100644 --- a/lib/mp4_generic.cpp +++ b/lib/mp4_generic.cpp @@ -1604,9 +1604,10 @@ namespace MP4{ setDuration(duration); // in ms setRate(0x00010000); // playback rate 1.0X setVolume(0x0100); // volume 1.0X - setMatrix(0x00010000, 0); - setMatrix(0x00010000, 4); - setMatrix(0x40000000, 8); + //Identity Transformation Matrix + setMatrix(1, 0); + setMatrix(1, 4); + setMatrix(1, 8); setTrackID(0xFFFFFFFF); // empty track numbers is unknown } @@ -1708,24 +1709,45 @@ namespace MP4{ // 10 bytes reserved in between uint32_t MVHD::getMatrixCount(){return 9;} - void MVHD::setMatrix(int32_t newMatrix, size_t index){ + void MVHD::setMatrix(double newMatrix, size_t index){ int offset = 0; if (getVersion() == 0){ offset = 24 + 2 + 10; }else{ offset = 36 + 2 + 10; } - setInt32(newMatrix, offset + index * 4); + //Indexes 2, 6 and 8 are 2.30 fixed point, the rest is 16.16 fixed point. + if (index == 2 || index == 5 || index == 8){ + setInt32((int32_t)(newMatrix * 1073741824.0), offset + index * 4); + }else{ + setInt32((int32_t)(newMatrix * 65536.0), offset + index * 4); + } } - int32_t MVHD::getMatrix(size_t index){ + double MVHD::getMatrix(size_t index){ int offset = 0; if (getVersion() == 0){ offset = 36; }else{ offset = 48; } - return getInt32(offset + index * 4); + //Indexes 2, 6 and 8 are 2.30 fixed point, the rest is 16.16 fixed point. + if (index == 2 || index == 5 || index == 8){ + return (int32_t)getInt32(offset + index * 4) / 1073741824.0; + }else{ + return (int32_t)getInt32(offset + index * 4) / 65536.0; + } + } + + uint16_t MVHD::getRotation(){ + //These are oversimplifications that ignore scaling and translation + //We also only handle the 90-degree-increment rotations, nothing else + //That should cover practically all videos we'll encounter though... + if (getMatrix(1) > 0.0 && getMatrix(3) < 0.0){return 90;} + if (getMatrix(1) < 0.0 && getMatrix(3) > 0.0){return 270;} + if (getMatrix(0) > 0.0 && getMatrix(4) > 0.0){return 0;} + if (getMatrix(0) < 0.0 && getMatrix(4) < 0.0){return 180;} + return 0; //Unknown rotation, assume none } // 24 bytes of pre-defined in between @@ -1761,6 +1783,7 @@ namespace MP4{ if (i != getMatrixCount() - 1){r << ", ";} } r << std::endl; + r << std::string(indent + 1, ' ') << "Rotation (calculated from Matrix): " << getRotation() << std::endl; r << std::string(indent + 1, ' ') << "next_track_ID: " << getTrackID() << std::endl; return r.str(); } @@ -1937,9 +1960,10 @@ namespace MP4{ } memset(data + payloadOffset, 0, 84); // set all bytes (92 - 8) to zeroes setFlags(3); // ENABLED | IN_MOVIE - setMatrix(0x00010000, 0); - setMatrix(0x00010000, 4); - setMatrix(0x40000000, 8); + //Identity transformation Matrix + setMatrix(1, 0); + setMatrix(1, 4); + setMatrix(1, 8); setVolume(0x0100); } @@ -2064,7 +2088,12 @@ namespace MP4{ }else{ offset = 52; } - setInt32(newMatrix, offset + index * 4); + //Indexes 2, 6 and 8 are 2.30 fixed point, the rest is 16.16 fixed point. + if (index == 2 || index == 5 || index == 8){ + setInt32((int32_t)(newMatrix * 1073741824.0), offset + index * 4); + }else{ + setInt32((int32_t)(newMatrix * 65536.0), offset + index * 4); + } } int32_t TKHD::getMatrix(size_t index){ @@ -2074,7 +2103,23 @@ namespace MP4{ }else{ offset = 52; } - return getInt32(offset + index * 4); + //Indexes 2, 6 and 8 are 2.30 fixed point, the rest is 16.16 fixed point. + if (index == 2 || index == 5 || index == 8){ + return (int32_t)getInt32(offset + index * 4) / 1073741824.0; + }else{ + return (int32_t)getInt32(offset + index * 4) / 65536.0; + } + } + + uint16_t TKHD::getRotation(){ + //These are oversimplifications that ignore scaling and translation + //We also only handle the 90-degree-increment rotations, nothing else + //That should cover practically all videos we'll encounter though... + if (getMatrix(1) > 0.0 && getMatrix(3) < 0.0){return 90;} + if (getMatrix(1) < 0.0 && getMatrix(3) > 0.0){return 270;} + if (getMatrix(0) > 0.0 && getMatrix(4) > 0.0){return 0;} + if (getMatrix(0) < 0.0 && getMatrix(4) < 0.0){return 180;} + return 0; //Unknown rotation, assume none } void TKHD::setWidth(double newWidth){ @@ -2126,6 +2171,7 @@ namespace MP4{ if (i != getMatrixCount() - 1){r << ", ";} } r << std::endl; + r << std::string(indent + 1, ' ') << "Rotation (calculated from Matrix): " << getRotation() << std::endl; r << std::string(indent + 1, ' ') << "Width: " << getWidth() << std::endl; r << std::string(indent + 1, ' ') << "Height: " << getHeight() << std::endl; return r.str(); diff --git a/lib/mp4_generic.h b/lib/mp4_generic.h index 3f509f17..b24854ea 100644 --- a/lib/mp4_generic.h +++ b/lib/mp4_generic.h @@ -470,8 +470,9 @@ namespace MP4{ void setVolume(uint16_t newVolume); uint16_t getVolume(); uint32_t getMatrixCount(); - void setMatrix(int32_t newMatrix, size_t index); - int32_t getMatrix(size_t index); + void setMatrix(double newMatrix, size_t index); + double getMatrix(size_t index); + uint16_t getRotation(); void setTrackID(uint32_t newTrackID); uint32_t getTrackID(); std::string toPrettyString(uint32_t indent = 0); @@ -529,6 +530,7 @@ namespace MP4{ uint32_t getMatrixCount(); void setMatrix(int32_t newMatrix, size_t index); int32_t getMatrix(size_t index); + uint16_t getRotation(); void setWidth(double newWidth); double getWidth();