From d423b9f63ebdb0988af9b975d419b354175cf215 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Sat, 24 May 2014 11:16:35 -0300 Subject: [PATCH] qtdemux: parse tkhd transformation matrix and add tags if appropriate Handle the transformation matrix cases where there are only simple rotations (90, 180 or 270 degrees) and use a tag for those cases. This is a common scenario when recording with mobile devices https://bugzilla.gnome.org/show_bug.cgi?id=679522 --- gst/isomp4/qtdemux.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 4 deletions(-) diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c index f736345..02e840b 100644 --- a/gst/isomp4/qtdemux.c +++ b/gst/isomp4/qtdemux.c @@ -7170,6 +7170,93 @@ bad_data: return 0; } +static gboolean +qtdemux_parse_transformation_matrix (GstQTDemux * qtdemux, + GstByteReader * reader, guint32 * matrix, const gchar * atom) +{ + /* + * 9 values of 32 bits (fixed point 16.16, except 2 5 and 8 that are 2.30) + * [0 1 2] + * [3 4 5] + * [6 7 8] + */ + + if (gst_byte_reader_get_remaining (reader) < 36) + return FALSE; + + matrix[0] = gst_byte_reader_get_uint32_be_unchecked (reader); + matrix[1] = gst_byte_reader_get_uint32_be_unchecked (reader); + matrix[2] = gst_byte_reader_get_uint32_be_unchecked (reader); + matrix[3] = gst_byte_reader_get_uint32_be_unchecked (reader); + matrix[4] = gst_byte_reader_get_uint32_be_unchecked (reader); + matrix[5] = gst_byte_reader_get_uint32_be_unchecked (reader); + matrix[6] = gst_byte_reader_get_uint32_be_unchecked (reader); + matrix[7] = gst_byte_reader_get_uint32_be_unchecked (reader); + matrix[8] = gst_byte_reader_get_uint32_be_unchecked (reader); + + GST_DEBUG_OBJECT (qtdemux, "Transformation matrix from atom %s", atom); + GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[0] >> 16, + matrix[0] & 0xFFFF, matrix[1] >> 16, matrix[1] & 0xFF, matrix[2] >> 16, + matrix[2] & 0xFF); + GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[3] >> 16, + matrix[3] & 0xFFFF, matrix[4] >> 16, matrix[4] & 0xFF, matrix[5] >> 16, + matrix[5] & 0xFF); + GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[6] >> 16, + matrix[6] & 0xFFFF, matrix[7] >> 16, matrix[7] & 0xFF, matrix[8] >> 16, + matrix[8] & 0xFF); + + return TRUE; +} + +static void +qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux, + QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist) +{ + +/* [a b c] + * [d e f] + * [g h i] + * + * This macro will only compare value abdegh, it expects cfi to have already + * been checked + */ +#define QTCHECK_MATRIX(m,a,b,d,e,g,h) ((m)[0] == (a << 16) && (m)[1] == (b << 16) && \ + (m)[3] == (d << 16) && (m)[4] == (e << 16) && \ + (m)[6] == (g << 16) && (m)[7] == (h << 16)) + + /* only handle the cases where the last column has standard values */ + if (matrix[2] == 0 && matrix[5] == 0 && matrix[8] == 1 << 30) { + const gchar *rotation_tag = NULL; + + /* no rotation needed */ + if (QTCHECK_MATRIX (matrix, 1, 0, 0, 1, 0, 0)) { + /* NOP */ + } else if (QTCHECK_MATRIX (matrix, 0, 1, G_MAXUINT16, 0, + stream->display_height, 0)) { + rotation_tag = "rotate-90"; + } else if (QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, G_MAXUINT16, + stream->display_width, stream->display_height)) { + rotation_tag = "rotate-180"; + } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0, 0, + stream->display_width)) { + rotation_tag = "rotate-270"; + } else { + GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values"); + } + + GST_DEBUG_OBJECT (qtdemux, "Transformation matrix rotation %s", + rotation_tag); + if (rotation_tag != NULL) { + if (*taglist == NULL) + *taglist = gst_tag_list_new_empty (); + gst_tag_list_add (*taglist, GST_TAG_MERGE_REPLACE, + GST_TAG_IMAGE_ORIENTATION, rotation_tag, NULL); + } + } else { + GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values"); + } +} + /* parse the traks. * With each track we associate a new QtDemuxStream that contains all the info * about the trak. @@ -7380,19 +7467,27 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) guint32 w = 0, h = 0; gboolean gray; gint depth, palette_size, palette_count; + guint32 matrix[9]; guint32 *palette_data = NULL; stream->sampled = TRUE; /* version 1 uses some 64-bit ints */ - if (!gst_byte_reader_skip (&tkhd, 56 + value_size) - || !gst_byte_reader_get_uint32_be (&tkhd, &w) + if (!gst_byte_reader_skip (&tkhd, 20 + value_size)) + goto corrupt_file; + + if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd")) + goto corrupt_file; + + if (!gst_byte_reader_get_uint32_be (&tkhd, &w) || !gst_byte_reader_get_uint32_be (&tkhd, &h)) goto corrupt_file; stream->display_width = w >> 16; stream->display_height = h >> 16; + qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix, &list); + offset = 16; if (len < 86) goto corrupt_file; @@ -7496,7 +7591,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) } if (codec) { - list = gst_tag_list_new_empty (); + if (list == NULL) + list = gst_tag_list_new_empty (); gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_VIDEO_CODEC, codec, NULL); g_free (codec); @@ -8276,7 +8372,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) GstStructure *s; gint bitrate = 0; - list = gst_tag_list_new_empty (); + if (list == NULL) + list = gst_tag_list_new_empty (); gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_AUDIO_CODEC, codec, NULL); g_free (codec); -- 2.7.4