The transformation operations that can be described by a display matrix
are not limited to pure rotation, but include horizontal and vertical
flip, as well as transpose and antitranspose. Unfortunately the current
API can only return a rotation angle in degrees, and is not designed to
detect flip operations or a combination of rotation and flip.

So implement an additional API to analyze the display matrix and return
the most common rotation operations (multiples of 90º) as well flips or
a combination thereof. This function returns a bitfield mask composed of
AVDisplayOrientation elements that describe which rendering operations
should be performed on the frame. The existing API is still available
and useful in case of custom rotations.

Signed-off-by: Vittorio Giovara <vittorio.giov...@gmail.com>
---
Note: the new operations describe a clockwise rotation, while the
old API provided a counterclockwise rotation. I always felt this was
a mistake as it's counterintuitive and suprising to new users, so I
didn't want to cargo-cult it to a new API. What do people think about it?

See also https://github.com/FFMS/ffms2/issues/317 for flipped samples, code,
and additional discussion.

Missing changelog entry and version bump.
Vittorio

 libavutil/display.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 libavutil/display.h | 53 ++++++++++++++++++++++++++++++
 2 files changed, 145 insertions(+)

diff --git a/libavutil/display.c b/libavutil/display.c
index f7500948ff..839961ec20 100644
--- a/libavutil/display.c
+++ b/libavutil/display.c
@@ -22,6 +22,7 @@
 #include <string.h>
 #include <math.h>
 
+#include "avstring.h"
 #include "display.h"
 #include "mathematics.h"
 
@@ -73,3 +74,94 @@ void av_display_matrix_flip(int32_t matrix[9], int hflip, 
int vflip)
         for (i = 0; i < 9; i++)
             matrix[i] *= flip[i % 3];
 }
+
+uint32_t av_display_orientation_get(int32_t matrix_src[9])
+{
+    int32_t matrix[9];
+    uint32_t orientation = 0;
+    int64_t det = (int64_t)matrix_src[0] * matrix_src[4] - 
(int64_t)matrix_src[1] * matrix_src[3];
+
+    /* Duplicate matrix so that the input one is not modified in case of flip. 
*/
+    memcpy(matrix, matrix_src, sizeof(*matrix_src) * 9);
+
+    if (det < 0) {
+        /* Always assume an horizontal flip for simplicity, it can be
+         * changed later if rotation is 180º. */
+        orientation = AV_FLIP_HORIZONTAL;
+        av_display_matrix_flip(matrix, 1, 0);
+    }
+
+    if (matrix[1] == (1 << 16) && matrix[3] == -(1 << 16)) {
+        orientation |= AV_ROTATION_90;
+    } else if (matrix[0] == -(1 << 16) && matrix[4] == -(1 << 16)) {
+        if (det < 0)
+            orientation = AV_FLIP_VERTICAL;
+        else
+            orientation |= AV_ROTATION_180;
+    } else if (matrix[1] == -(1 << 16) && matrix[3] == (1 << 16)) {
+        orientation |= AV_ROTATION_270;
+    } else if (matrix[0] == (1 << 16) && matrix[4] == (1 << 16)) {
+        orientation |= AV_IDENTITY;
+    } else {
+        orientation |= AV_ROTATION_CUSTOM;
+    }
+
+    return orientation;
+}
+
+void av_display_orientation_set(int32_t matrix[9], uint32_t orientation, 
double angle)
+{
+    int hflip = !!(orientation & AV_FLIP_HORIZONTAL);
+    int vflip = !!(orientation & AV_FLIP_VERTICAL);
+
+    memset(matrix, 0, sizeof(*matrix) * 9);
+    matrix[8] = 1 << 30;
+
+    if (orientation & AV_IDENTITY) {
+        matrix[0] = 1 << 16;
+        matrix[4] = 1 << 16;
+    } else if (orientation & AV_ROTATION_90) {
+        matrix[1] = 1 << 16;
+        matrix[3] = -(1 << 16);
+    } else if (orientation & AV_ROTATION_180) {
+        matrix[0] = -(1 << 16);
+        matrix[4] = -(1 << 16);
+    } else if (orientation & AV_ROTATION_270) {
+        matrix[1] = -(1 << 16);
+        matrix[3] = 1 << 16;
+    } else if (orientation & AV_ROTATION_CUSTOM) {
+        av_display_rotation_set(matrix, angle);
+    }
+
+    av_display_matrix_flip(matrix, hflip, vflip);
+}
+
+void av_display_orientation_name(uint32_t orientation, char *buf, size_t 
buf_size)
+{
+    if (orientation == 0) {
+        av_strlcpy(buf, "identity", buf_size);
+        return;
+    }
+
+    if (orientation & AV_ROTATION_90)
+        av_strlcpy(buf, "rotation_90", buf_size);
+    else if (orientation & AV_ROTATION_180)
+        av_strlcpy(buf, "rotation_180", buf_size);
+    else if (orientation & AV_ROTATION_270)
+        av_strlcpy(buf, "rotation_270", buf_size);
+    else if (orientation & AV_ROTATION_CUSTOM)
+        av_strlcpy(buf, "rotation_custom", buf_size);
+    else
+        buf[0] = '\0';
+
+    if (orientation & AV_FLIP_HORIZONTAL) {
+        if (buf[0] != '\0')
+            av_strlcat(buf, "+", buf_size);
+        av_strlcat(buf, "hflip", buf_size);
+    }
+    if (orientation & AV_FLIP_VERTICAL) {
+        if (buf[0] != '\0')
+            av_strlcat(buf, "+", buf_size);
+        av_strlcat(buf, "vflip", buf_size);
+    }
+}
diff --git a/libavutil/display.h b/libavutil/display.h
index 2d869fcd16..a057453b20 100644
--- a/libavutil/display.h
+++ b/libavutil/display.h
@@ -27,6 +27,7 @@
 #define AVUTIL_DISPLAY_H
 
 #include <stdint.h>
+#include <stddef.h>
 
 /**
  * @addtogroup lavu_video
@@ -105,6 +106,58 @@ void av_display_rotation_set(int32_t matrix[9], double 
angle);
  */
 void av_display_matrix_flip(int32_t matrix[9], int hflip, int vflip);
 
+enum AVDisplayOrientation {
+    /** There is no orientation operation to be performed on the frame. */
+    AV_IDENTITY         = 0,
+    /** Apply a 90º clockwise rotation on the frame. */
+    AV_ROTATION_90      = 1 << 0,
+    /** Apply a 180º clockwise rotation on the frame. */
+    AV_ROTATION_180     = 1 << 1,
+    /** Apply a 270º clockwise rotation on the frame. */
+    AV_ROTATION_270     = 1 << 2,
+    /**
+     * Apply a custom rotation on the frame. Users may inspect the input matrix
+     * with av_display_rotation_get() to know the degree amount.
+     *
+     * @note av_display_rotation_get() returns a counterclockwise angle.
+     */
+    AV_ROTATION_CUSTOM  = 1 << 3,
+    /** Apply a horizontal flip on the frame. */
+    AV_FLIP_HORIZONTAL  = 1 << 4,
+    /** Apply a vertical flip on the frame. */
+    AV_FLIP_VERTICAL    = 1 << 5,
+};
+
+/**
+ * Return a mask composed of AVDisplayOrientation elements describing the list
+ * of operations to be performed on the rendered video frame from a given
+ * transformation matrix.
+ *
+ * @param matrix an allocated transformation matrix
+ * @return a set of AVDisplayOrientation operations
+ */
+uint32_t av_display_orientation_get(int32_t matrix[9]);
+
+/**
+ * Initialize a transformation matrix describing a set of AVDisplayOrientation
+ * operations. If a custom rotation is desired, it is possible to set the 
rotation
+ * angle as optional parameter.
+ *
+ * @param matrix an allocated transformation matrix (will be fully overwritten
+ *               by this function)
+ * @param orientation a set of AVDisplayOrientation operations
+ * @param angle counterclockwise rotation angle in degrees
+ */
+void av_display_orientation_set(int32_t matrix[9], uint32_t orientation, 
double angle);
+
+/**
+ * Return a human readable description of the input AVDisplayOrientation set.
+ *
+ * @param orientation a set of AVDisplayOrientation operations
+ * @param buf a user-allocated buffer that will contain the returned string
+ * @param buf_size size in bytes of the buffer
+ */
+void av_display_orientation_name(uint32_t orientation, char *buf, size_t size);
 /**
  * @}
  * @}
-- 
2.16.2

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

Reply via email to