qtdemux: Use mvhd transform matrix and support for flipping

The mvhd matrix is now combined with the tkhd matrix. The combined
matrix is then checked if it matches one of the standard values for
GST_TAG_IMAGE_ORIENTATION.
This check now includes matrices with flipping.

Fixes #4064

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8127>
This commit is contained in:
Jochen Henneberg 2024-12-10 21:34:48 +01:00 committed by GStreamer Marge Bot
parent f0a8938133
commit c4d0f4bbd9

View file

@ -11430,6 +11430,23 @@ qtdemux_parse_transformation_matrix (GstQTDemux * qtdemux,
return TRUE; return TRUE;
} }
static void
qtdemux_mul_transformation_matrix (GstQTDemux * qtdemux,
guint32 * a, guint32 * b, guint32 * c)
{
#define QTMUL_MATRIX(_a,_b) (((_a) == 0 || (_b) == 0) ? 0 : \
((_a) == (_b) ? 1 : -1))
#define QTADD_MATRIX(_a,_b) ((_a) + (_b) > 0 ? (1U << 16) : \
((_a) + (_b) < 0) ? (G_MAXUINT16 << 16) : 0u)
c[2] = c[5] = c[6] = c[7] = 0;
c[0] = QTADD_MATRIX (QTMUL_MATRIX (a[0], b[0]), QTMUL_MATRIX (a[1], b[3]));
c[1] = QTADD_MATRIX (QTMUL_MATRIX (a[0], b[1]), QTMUL_MATRIX (a[1], b[4]));
c[3] = QTADD_MATRIX (QTMUL_MATRIX (a[3], b[0]), QTMUL_MATRIX (a[4], b[3]));
c[4] = QTADD_MATRIX (QTMUL_MATRIX (a[3], b[1]), QTMUL_MATRIX (a[4], b[4]));
c[8] = a[8];
}
static void static void
qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux, qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist) QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist)
@ -11459,6 +11476,14 @@ qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
rotation_tag = "rotate-180"; rotation_tag = "rotate-180";
} else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) { } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) {
rotation_tag = "rotate-270"; rotation_tag = "rotate-270";
} else if (QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, 1)) {
rotation_tag = "flip-rotate-0";
} else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) {
rotation_tag = "flip-rotate-90";
} else if (QTCHECK_MATRIX (matrix, 1, 0, 0, G_MAXUINT16)) {
rotation_tag = "flip-rotate-180";
} else if (QTCHECK_MATRIX (matrix, 0, 1, 1, 0)) {
rotation_tag = "flip-rotate-270";
} else { } else {
GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values"); GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
} }
@ -11745,7 +11770,7 @@ qtdemux_parse_stereo_svmi_atom (GstQTDemux * qtdemux, QtDemuxStream * stream,
* traks that do not decode to something (like strm traks) will not have a pad. * traks that do not decode to something (like strm traks) will not have a pad.
*/ */
static gboolean static gboolean
qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak, guint32 * mvhd_matrix)
{ {
GstByteReader tkhd; GstByteReader tkhd;
int offset; int offset;
@ -11917,15 +11942,21 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
/* parse rest of tkhd */ /* parse rest of tkhd */
if (stream->subtype == FOURCC_vide) { if (stream->subtype == FOURCC_vide) {
guint32 tkhd_matrix[9];
guint32 matrix[9]; guint32 matrix[9];
/* version 1 uses some 64-bit ints */ /* version 1 uses some 64-bit ints */
if (!gst_byte_reader_skip (&tkhd, 20 + value_size)) if (!gst_byte_reader_skip (&tkhd, 20 + value_size))
goto corrupt_file; goto corrupt_file;
if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd")) if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, tkhd_matrix,
"tkhd"))
goto corrupt_file; goto corrupt_file;
/* calculate the final matrix from the mvhd_matrix and the tkhd matrix */
qtdemux_mul_transformation_matrix (qtdemux, mvhd_matrix, tkhd_matrix,
matrix);
if (!gst_byte_reader_get_uint32_be (&tkhd, &w) if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
|| !gst_byte_reader_get_uint32_be (&tkhd, &h)) || !gst_byte_reader_get_uint32_be (&tkhd, &h))
goto corrupt_file; goto corrupt_file;
@ -14808,11 +14839,14 @@ qtdemux_parse_tree (GstQTDemux * qtdemux)
guint64 creation_time; guint64 creation_time;
GstDateTime *datetime = NULL; GstDateTime *datetime = NULL;
gint version; gint version;
GstByteReader mvhd_reader;
guint32 matrix[9];
/* make sure we have a usable taglist */ /* make sure we have a usable taglist */
qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list); qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd); mvhd = qtdemux_tree_get_child_by_type_full (qtdemux->moov_node,
FOURCC_mvhd, &mvhd_reader);
if (mvhd == NULL) { if (mvhd == NULL) {
GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects."); GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
return qtdemux_parse_redirects (qtdemux); return qtdemux_parse_redirects (qtdemux);
@ -14823,15 +14857,26 @@ qtdemux_parse_tree (GstQTDemux * qtdemux)
creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12); creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28); qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32); qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
if (!gst_byte_reader_skip (&mvhd_reader, 4 + 8 + 8 + 4 + 8))
return FALSE;
} else if (version == 0) { } else if (version == 0) {
creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12); creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20); qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24); qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
if (!gst_byte_reader_skip (&mvhd_reader, 4 + 4 + 4 + 4 + 4))
return FALSE;
} else { } else {
GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version); GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
return FALSE; return FALSE;
} }
if (!gst_byte_reader_skip (&mvhd_reader, 4 + 2 + 2 + 2 * 4))
return FALSE;
if (!qtdemux_parse_transformation_matrix (qtdemux, &mvhd_reader, matrix,
"mvhd"))
return FALSE;
/* Moving qt creation time (secs since 1904) to unix time */ /* Moving qt creation time (secs since 1904) to unix time */
if (creation_time != 0) { if (creation_time != 0) {
/* Try to use epoch first as it should be faster and more commonly found */ /* Try to use epoch first as it should be faster and more commonly found */
@ -14900,7 +14945,7 @@ qtdemux_parse_tree (GstQTDemux * qtdemux)
/* parse all traks */ /* parse all traks */
trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak); trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
while (trak) { while (trak) {
qtdemux_parse_trak (qtdemux, trak); qtdemux_parse_trak (qtdemux, trak, matrix);
/* iterate all siblings */ /* iterate all siblings */
trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak); trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
} }