Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
From: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
To: ffmpeg-devel@ffmpeg.org
Cc: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
Subject: [FFmpeg-devel] [PATCH 1/5] avformat/matroskaenc: Support rotations
Date: Mon,  7 Aug 2023 01:04:16 +0200
Message-ID: <AS8P250MB0744E8BE1C2515319ABA75A08F0FA@AS8P250MB0744.EURP250.PROD.OUTLOOK.COM> (raw)

Matroska supports orthogonal transformations (both pure rotations
as well as reflections) via its 3D-projection elements, namely
ProjectionPoseYaw (for a horizontal reflection) as well as
ProjectionPoseRoll (for rotations). This commit adds support
for this.

Support for this in the demuxer has been added in
937bb6bbc1e8654633737e69e403e95a37113058 and
the sample used in the matroska-dovi-write-config8 FATE-test
includes a displaymatrix indicating a rotation which is now
properly written and read, thereby providing coverage for
the relevant code in the muxer as well as the demuxer.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
---
Honestly, I am not really sure how to handle the floating-point
inaccuracies here (in atan2).

 libavformat/matroskaenc.c                  | 100 +++++++++++++++++----
 tests/ref/fate/matroska-dovi-write-config8 |  13 ++-
 2 files changed, 94 insertions(+), 19 deletions(-)

diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
index 41e13b273d..c1f40b26e6 100644
--- a/libavformat/matroskaenc.c
+++ b/libavformat/matroskaenc.c
@@ -1403,25 +1403,75 @@ static void mkv_write_video_color(EbmlWriter *writer, const AVStream *st,
 }
 
 #define MAX_VIDEO_PROJECTION_ELEMS 6
-static void mkv_write_video_projection(AVFormatContext *s, EbmlWriter *writer,
-                                       const AVStream *st, uint8_t private[])
+static void mkv_handle_rotation(void *logctx, const AVStream *st,
+                                double *yaw, double *roll)
+{
+    const int32_t *matrix =
+        (const int32_t*)av_stream_get_side_data(st, AV_PKT_DATA_DISPLAYMATRIX, NULL);
+
+    if (!matrix)
+        return;
+
+    /* Check whether this is an affine transformation */
+    if (matrix[2] || matrix[5])
+        goto ignore;
+
+    /* This together with the checks below test whether
+     * the upper-left 2x2 matrix is nonsingular. */
+    if (!matrix[0] && !matrix[1])
+        goto ignore;
+
+    /* We ignore the translation part of the matrix (matrix[6] and matrix[7])
+     * as well as any scaling, i.e. we only look at the upper left 2x2 matrix.
+     * We only accept matrices that are an exact multiple of an orthogonal one.
+     * Apart from the multiple, every such matrix can be obtained by
+     * potentially flipping in the x-direction (corresponding to yaw = 180)
+     * followed by a rotation of (say) an angle phi in the counterclockwise
+     * direction. The upper-left 2x2 matrix then looks like this:
+     *         | (+/-)cos(phi) (-/+)sin(phi) |
+     * scale * |                             |
+     *         |      sin(phi)      cos(phi) |
+     * The first set of signs in the first row apply in case of no flipping,
+     * the second set applies in case of flipping. */
+
+    /* The casts to int64_t are needed because -INT32_MIN doesn't fit
+     * in an int32_t. */
+    if (matrix[0] == matrix[4] && -(int64_t)matrix[1] == matrix[3]) {
+        /* No flipping case */
+        *yaw = 0;
+    } else if (-(int64_t)matrix[0] == matrix[4] && matrix[1] == matrix[3]) {
+        /* Horizontal flip */
+        *yaw = 180;
+    } else {
+ignore:
+        av_log(logctx, AV_LOG_INFO, "Ignoring display matrix indicating "
+               "non-orthogonal transformation.\n");
+        return;
+    }
+    *roll = 180 / M_PI * atan2(matrix[3], matrix[4]);
+
+    /* We do not write a ProjectionType element indicating "rectangular",
+     * because this is the default value. */
+}
+
+static int mkv_handle_spherical(void *logctx, EbmlWriter *writer,
+                                const AVStream *st, uint8_t private[],
+                                double *yaw, double *pitch, double *roll)
 {
     const AVSphericalMapping *spherical =
         (const AVSphericalMapping *)av_stream_get_side_data(st, AV_PKT_DATA_SPHERICAL,
                                                             NULL);
 
     if (!spherical)
-        return;
+        return 0;
 
     if (spherical->projection != AV_SPHERICAL_EQUIRECTANGULAR      &&
         spherical->projection != AV_SPHERICAL_EQUIRECTANGULAR_TILE &&
         spherical->projection != AV_SPHERICAL_CUBEMAP) {
-        av_log(s, AV_LOG_WARNING, "Unknown projection type\n");
-        return;
+        av_log(logctx, AV_LOG_WARNING, "Unknown projection type\n");
+        return 0;
     }
 
-    ebml_writer_open_master(writer, MATROSKA_ID_VIDEOPROJECTION);
-
     switch (spherical->projection) {
     case AV_SPHERICAL_EQUIRECTANGULAR:
     case AV_SPHERICAL_EQUIRECTANGULAR_TILE:
@@ -1455,17 +1505,33 @@ static void mkv_write_video_projection(AVFormatContext *s, EbmlWriter *writer,
         av_assert0(0);
     }
 
-    if (spherical->yaw)
-        ebml_writer_add_float(writer, MATROSKA_ID_VIDEOPROJECTIONPOSEYAW,
-                              (double) spherical->yaw   / (1 << 16));
-    if (spherical->pitch)
-        ebml_writer_add_float(writer, MATROSKA_ID_VIDEOPROJECTIONPOSEPITCH,
-                       (double) spherical->pitch / (1 << 16));
-    if (spherical->roll)
-        ebml_writer_add_float(writer, MATROSKA_ID_VIDEOPROJECTIONPOSEROLL,
-                       (double) spherical->roll  / (1 << 16));
+    *yaw   = (double) spherical->yaw   / (1 << 16);
+    *pitch = (double) spherical->pitch / (1 << 16);
+    *roll  = (double) spherical->roll  / (1 << 16);
 
-    ebml_writer_close_master(writer);
+    return 1; /* Projection included */
+}
+
+static void mkv_write_video_projection(void *logctx, EbmlWriter *wr,
+                                       const AVStream *st, uint8_t private[])
+{
+    double yaw = 0, pitch = 0, roll = 0;
+    int ret;
+
+    ebml_writer_open_master(wr, MATROSKA_ID_VIDEOPROJECTION);
+
+    ret = mkv_handle_spherical(logctx, wr, st, private, &yaw, &pitch, &roll);
+    if (!ret)
+        mkv_handle_rotation(logctx, st, &yaw, &roll);
+
+    if (yaw)
+        ebml_writer_add_float(wr, MATROSKA_ID_VIDEOPROJECTIONPOSEYAW, yaw);
+    if (pitch)
+        ebml_writer_add_float(wr, MATROSKA_ID_VIDEOPROJECTIONPOSEPITCH, pitch);
+    if (roll)
+        ebml_writer_add_float(wr, MATROSKA_ID_VIDEOPROJECTIONPOSEROLL, roll);
+
+    ebml_writer_close_or_discard_master(wr);
 }
 
 #define MAX_FIELD_ORDER_ELEMS 2
diff --git a/tests/ref/fate/matroska-dovi-write-config8 b/tests/ref/fate/matroska-dovi-write-config8
index bb22563eee..58eb454865 100644
--- a/tests/ref/fate/matroska-dovi-write-config8
+++ b/tests/ref/fate/matroska-dovi-write-config8
@@ -1,5 +1,5 @@
-09ff3c0a038eec0cdf4773929b24f41a *tests/data/fate/matroska-dovi-write-config8.matroska
-3600606 tests/data/fate/matroska-dovi-write-config8.matroska
+80d2b23a6f27ab28b02a907b37b9649c *tests/data/fate/matroska-dovi-write-config8.matroska
+3600620 tests/data/fate/matroska-dovi-write-config8.matroska
 #extradata 0:      551, 0xa18acf66
 #extradata 1:        2, 0x00340022
 #tb 0: 1/1000
@@ -46,6 +46,15 @@
 1,        395,        395,       23,      439, 0x7d85e4c9
 [STREAM]
 [SIDE_DATA]
+side_data_type=Display Matrix
+displaymatrix=
+00000000:            0       65536           0
+00000001:       -65536           0           0
+00000002:            0           0  1073741824
+
+rotation=-90
+[/SIDE_DATA]
+[SIDE_DATA]
 side_data_type=DOVI configuration record
 dv_version_major=1
 dv_version_minor=0
-- 
2.34.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

             reply	other threads:[~2023-08-06 23:03 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-08-06 23:04 Andreas Rheinhardt [this message]
2023-08-06 23:06 ` [FFmpeg-devel] [PATCH 2/5] avformat/matroskaenc: Don't reserve unnecessarily many EBML elements Andreas Rheinhardt
2023-08-06 23:06 ` [FFmpeg-devel] [PATCH 3/5] fate/matroska: Add ALAC remux test Andreas Rheinhardt
2023-08-06 23:06 ` [FFmpeg-devel] [PATCH 4/5] avformat/matroskaenc: Don't pretend to support unsupported codecs Andreas Rheinhardt
2023-08-06 23:06 ` [FFmpeg-devel] [PATCH 5/5] avformat/matroskaenc: Don't pretend to be able to mux RV30 Andreas Rheinhardt
2023-08-08 16:40 ` [FFmpeg-devel] [PATCH 06/15] avformat/matroskaenc: Hoist check out of loop Andreas Rheinhardt
2023-08-10  7:48   ` Andreas Rheinhardt
2023-08-08 16:40 ` [FFmpeg-devel] [PATCH 07/15] avformat/matroskaenc: Remove unnecessary check Andreas Rheinhardt
2023-08-08 16:40 ` [FFmpeg-devel] [PATCH 08/15] avformat/matroskaenc: Use proper AVIOContext Andreas Rheinhardt
2023-08-08 16:40 ` [FFmpeg-devel] [PATCH 09/15] avformat/matroskaenc: Use dedicated pointer for accesses Andreas Rheinhardt
2023-08-08 16:40 ` [FFmpeg-devel] [PATCH 10/15] avformat/matroskaenc: Avoid allocations when writing Dynamic HDR10+ Andreas Rheinhardt
2023-08-08 16:40 ` [FFmpeg-devel] [PATCH 11/15] avformat/dovi_isom: Don't use AVFormatContext* for logctx Andreas Rheinhardt
2023-08-08 16:40 ` [FFmpeg-devel] [PATCH 12/15] avformat/matroskaenc: Add const where appropriate Andreas Rheinhardt
2023-08-08 16:40 ` [FFmpeg-devel] [PATCH 13/15] avformat/matroskaenc: Don't reserve space for HDR10+ when unnecessary Andreas Rheinhardt
2023-08-08 16:40 ` [FFmpeg-devel] [PATCH 14/15] avformat/matroskaenc: Reindent after the previous commit Andreas Rheinhardt
2023-08-08 16:40 ` [FFmpeg-devel] [PATCH 15/15] avformat/matroskaenc: Don't write \0 unnecessarily Andreas Rheinhardt
2023-08-09  9:26 ` [FFmpeg-devel] [PATCH 1/5] avformat/matroskaenc: Support rotations Andreas Rheinhardt

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=AS8P250MB0744E8BE1C2515319ABA75A08F0FA@AS8P250MB0744.EURP250.PROD.OUTLOOK.COM \
    --to=andreas.rheinhardt@outlook.com \
    --cc=ffmpeg-devel@ffmpeg.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git