From a7c2899990cdb6537d8fb6d70d22cb033398f33b Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Mon, 20 Jan 2025 00:37:58 +0100 Subject: [PATCH] matroskamux: Implement rotation tag support Similar to qtmux, but for mkv and webm containers. Tested against other Gst elements and MPV. Note that the later apparently does not show correct results for flipped values. In particular the Yaw value seems to get ignored by many clients. Can be tested with: ``` gst-launch-1.0 \ videotestsrc num-buffers=90 ! \ taginject tags="image-orientation=rotate-270" ! \ capsfilter caps=video/x-raw,width=640,height=480,max-framerate=30/1 ! \ videoconvert ! \ queue ! \ vp8enc ! \ queue ! \ webmmux ! \ filesink location=./test.webm ``` Part-of: --- .../gst/matroska/matroska-mux.c | 81 ++++++++++++++++++- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/subprojects/gst-plugins-good/gst/matroska/matroska-mux.c b/subprojects/gst-plugins-good/gst/matroska/matroska-mux.c index 81e8d4d935..a3251c196d 100644 --- a/subprojects/gst-plugins-good/gst/matroska/matroska-mux.c +++ b/subprojects/gst-plugins-good/gst/matroska/matroska-mux.c @@ -2693,9 +2693,81 @@ gst_matroska_mux_write_colour (GstMatroskaMux * mux, } static void -gst_matroska_mux_track_header (GstMatroskaMux * mux, - GstMatroskaTrackContext * context) +gst_matroska_mux_write_projection (GstMatroskaMux * mux, + GstMatroskaMuxPad * pad) { + GstEbmlWrite *ebml = mux->ebml_write; + gchar *orientation; + guint64 master; + float yaw, roll; + + if (!pad->tags || + !gst_tag_list_get_string (pad->tags, GST_TAG_IMAGE_ORIENTATION, + &orientation)) { + const GstTagList *global_tags; + + global_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux)); + if (!global_tags || + !gst_tag_list_get_string (global_tags, GST_TAG_IMAGE_ORIENTATION, + &orientation)) + return; + } + + if (!g_strcmp0 ("rotate-0", orientation)) { + yaw = 0.0; + roll = 0.0; + } else if (!g_strcmp0 ("rotate-90", orientation)) { + yaw = 0.0; + roll = -90.0; + } else if (!g_strcmp0 ("rotate-180", orientation)) { + yaw = 0.0; + roll = 180.0; + } else if (!g_strcmp0 ("rotate-270", orientation)) { + yaw = 0.0; + roll = 90.0; + } else if (!g_strcmp0 ("flip-rotate-0", orientation)) { + yaw = 180.0; + roll = 0.0; + } else if (!g_strcmp0 ("flip-rotate-90", orientation)) { + yaw = 180.0; + roll = -90.0; + } else if (!g_strcmp0 ("flip-rotate-180", orientation)) { + yaw = 180.0; + roll = 180.0; + } else if (!g_strcmp0 ("flip-rotate-270", orientation)) { + yaw = 180.0; + roll = 90.0; + } else { + GST_FIXME_OBJECT (mux, "Unsupported orientation %s", orientation); + yaw = 0.0; + roll = 0.0; + } + + /* Default projection, skip writing */ + if (yaw == 0.0 && roll == 0.0) { + g_free (orientation); + return; + } + + master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_VIDEOPROJECTION); + + gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPROJECTIONTYPE, 0); + gst_ebml_write_float (ebml, GST_MATROSKA_ID_VIDEOPROJECTIONPOSEYAW, yaw); + gst_ebml_write_float (ebml, GST_MATROSKA_ID_VIDEOPROJECTIONPOSEPITCH, 0.0); + gst_ebml_write_float (ebml, GST_MATROSKA_ID_VIDEOPROJECTIONPOSEROLL, roll); + + gst_ebml_write_master_finish (ebml, master); + + GST_INFO_OBJECT (mux, + "Wrote projection type: 0 yaw: %f pitch: 0.0 row: %f from tag: %s", yaw, + roll, orientation); + g_free (orientation); +} + +static void +gst_matroska_mux_track_header (GstMatroskaMux * mux, GstMatroskaMuxPad * pad) +{ + GstMatroskaTrackContext *context = pad->track; GstEbmlWrite *ebml = mux->ebml_write; guint64 master; @@ -2757,7 +2829,10 @@ gst_matroska_mux_track_header (GstMatroskaMux * mux, gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE, (gpointer) & fcc_le, 4); } + gst_matroska_mux_write_colour (mux, videocontext); + gst_matroska_mux_write_projection (mux, pad); + if (videocontext->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) { guint64 stereo_mode = 0; @@ -3236,7 +3311,7 @@ gst_matroska_mux_start_file (GstMatroskaMux * mux) pad->track->num = tracknum++; child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY); - gst_matroska_mux_track_header (mux, pad->track); + gst_matroska_mux_track_header (mux, pad); gst_ebml_write_master_finish (ebml, child); /* some remaining pad/track setup */ pad->default_duration_scaled =