This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit 9e011abad729687f6e14c32fd90629e5aa820a3a
Author:     Jun Zhao <[email protected]>
AuthorDate: Fri Aug 28 10:31:44 2026 +0800
Commit:     Jun Zhao <[email protected]>
CommitDate: Fri Sep 11 22:31:03 2026 +0000

    fftools/ffmpeg: add -stereo3d per-stream output option
    
    Sources such as PNG sequences have no stereoscopic metadata, so there
    was no CLI way to write container layout tags without an external
    post-processor. Inject AV_PKT_DATA_STEREO3D onto the output stream
    after bitstream filters and before write_header.
    
    A bare -stereo3d applies to video and is ignored on other types; an
    explicit specifier on a non-video stream is rejected.
    
    Fixes: #24269
    Reported-by: optozorax
    
    Signed-off-by: Jun Zhao <[email protected]>
---
 fftools/ffmpeg.h          |  1 +
 fftools/ffmpeg_mux.c      | 35 ++++++++++++++++++
 fftools/ffmpeg_mux.h      |  3 ++
 fftools/ffmpeg_mux_init.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++-
 fftools/ffmpeg_opt.c      |  3 ++
 5 files changed, 133 insertions(+), 1 deletion(-)

diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index c403acdb59..d3aa191e23 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -203,6 +203,7 @@ typedef struct OptionsContext {
     SpecifierOptList fps_mode;
     SpecifierOptList force_fps;
     SpecifierOptList frame_aspect_ratios;
+    SpecifierOptList stereo3ds;
     SpecifierOptList display_rotations;
     SpecifierOptList display_hflips;
     SpecifierOptList display_vflips;
diff --git a/fftools/ffmpeg_mux.c b/fftools/ffmpeg_mux.c
index b408b02f2a..9e7ebd3016 100644
--- a/fftools/ffmpeg_mux.c
+++ b/fftools/ffmpeg_mux.c
@@ -30,6 +30,7 @@
 #include "libavutil/intreadwrite.h"
 #include "libavutil/log.h"
 #include "libavutil/mem.h"
+#include "libavutil/stereo3d.h"
 #include "libavutil/time.h"
 #include "libavutil/timestamp.h"
 
@@ -633,6 +634,40 @@ int of_stream_init(OutputFile *of, OutputStream *ost,
     if (ret < 0)
         return ret;
 
+    if (ms->stereo3d_set) {
+        AVCodecParameters *par = ost->st->codecpar;
+        AVStereo3D *stereo;
+        size_t size;
+
+        stereo = av_stereo3d_alloc_size(&size);
+        if (!stereo)
+            return AVERROR(ENOMEM);
+        stereo->type = ms->stereo3d_type;
+
+        if (av_packet_side_data_get(par->coded_side_data, 
par->nb_coded_side_data,
+                                    AV_PKT_DATA_STEREO3D))
+            av_log(ost, AV_LOG_INFO, "Overriding existing stereoscopic 3D side 
data\n");
+
+        if (!av_packet_side_data_add(&par->coded_side_data, 
&par->nb_coded_side_data,
+                                     AV_PKT_DATA_STEREO3D, stereo, size, 0)) {
+            av_freep(&stereo);
+            return AVERROR(ENOMEM);
+        }
+
+        /* Keep Matroska/WebM metadata consistent with the explicit layout,
+         * as their muxer consults the tag before the side data. */
+        if (!strcmp(mux->fc->oformat->name, "matroska") ||
+            !strcmp(mux->fc->oformat->name, "webm")) {
+            const char *stereo_mode =
+                ms->stereo3d_type == AV_STEREO3D_2D         ? "mono" :
+                ms->stereo3d_type == AV_STEREO3D_SIDEBYSIDE ? "left_right" :
+                                                              "top_bottom";
+            ret = av_dict_set(&ost->st->metadata, "stereo_mode", stereo_mode, 
0);
+            if (ret < 0)
+                return ret;
+        }
+    }
+
     if (ms->stream_duration) {
         ost->st->duration = av_rescale_q(ms->stream_duration, 
ms->stream_duration_tb,
                                          ost->st->time_base);
diff --git a/fftools/ffmpeg_mux.h b/fftools/ffmpeg_mux.h
index f458cc882c..3d6686da02 100644
--- a/fftools/ffmpeg_mux.h
+++ b/fftools/ffmpeg_mux.h
@@ -87,6 +87,9 @@ typedef struct MuxStream {
     int             force_fps;
 
     const char     *apad;
+
+    int             stereo3d_set;
+    int             stereo3d_type;
 } MuxStream;
 
 typedef struct Muxer {
diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c
index 6fd5ed367c..a01a6b756e 100644
--- a/fftools/ffmpeg_mux_init.c
+++ b/fftools/ffmpeg_mux_init.c
@@ -47,6 +47,7 @@
 #include "libavutil/opt.h"
 #include "libavutil/parseutils.h"
 #include "libavutil/pixdesc.h"
+#include "libavutil/stereo3d.h"
 
 #define DEFAULT_PASS_LOGFILENAME_PREFIX "ffmpeg2pass"
 
@@ -545,6 +546,86 @@ static enum AVPixelFormat pix_fmt_parse(OutputStream *ost, 
const char *name)
     return fmt;
 }
 
+static int parse_stereo3d_type(void *logctx, const char *arg, int *type)
+{
+    static const struct {
+        const char *name;
+        int type;
+    } aliases[] = {
+        { "2d",   AV_STEREO3D_2D },
+        { "mono", AV_STEREO3D_2D },
+        { "sbs",  AV_STEREO3D_SIDEBYSIDE },
+        { "sbsl", AV_STEREO3D_SIDEBYSIDE },
+        { "tb",   AV_STEREO3D_TOPBOTTOM },
+        { "tbl",  AV_STEREO3D_TOPBOTTOM },
+    };
+    static const enum AVStereo3DType v1_types[] = {
+        AV_STEREO3D_2D,
+        AV_STEREO3D_SIDEBYSIDE,
+        AV_STEREO3D_TOPBOTTOM,
+    };
+
+    for (int i = 0; i < FF_ARRAY_ELEMS(aliases); i++) {
+        if (!av_strcasecmp(arg, aliases[i].name)) {
+            *type = aliases[i].type;
+            return 0;
+        }
+    }
+
+    for (int i = 0; i < FF_ARRAY_ELEMS(v1_types); i++) {
+        const char *name = av_stereo3d_type_name(v1_types[i]);
+        if (!av_strcasecmp(arg, name)) {
+            *type = v1_types[i];
+            return 0;
+        }
+    }
+
+    av_log(logctx, AV_LOG_ERROR,
+           "Invalid stereoscopic 3D layout '%s'. "
+           "Valid values are: 2d, mono, sbs, sbsl, side by side, "
+           "tb, tbl, top and bottom.\n", arg);
+    return AVERROR(EINVAL);
+}
+
+static int check_stereo3d_leftovers(Muxer *mux, const OptionsContext *o)
+{
+    AVFormatContext *oc = mux->fc;
+
+    for (int i = 0; i < o->stereo3ds.nb_opt; i++) {
+        const SpecifierOpt *so = &o->stereo3ds.opt[i];
+        int matched_video = 0, matched_other = 0;
+
+        /* Empty specifier applies to video only; ignore other stream types. */
+        if (!so->specifier[0])
+            continue;
+
+        for (unsigned j = 0; j < oc->nb_streams; j++) {
+            AVStream *st = oc->streams[j];
+            if (!stream_specifier_match(&so->stream_spec, oc, st, mux))
+                continue;
+            if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+                matched_video++;
+            else
+                matched_other++;
+        }
+
+        if (matched_other) {
+            av_log(mux, AV_LOG_ERROR,
+                   "-stereo3d is only valid for video streams (specifier 
'%s').\n",
+                   so->specifier);
+            return AVERROR(EINVAL);
+        }
+        if (!matched_video) {
+            av_log(mux, AV_LOG_ERROR,
+                   "Stream specifier '%s' for -stereo3d matches no video 
streams.\n",
+                   so->specifier);
+            return AVERROR(EINVAL);
+        }
+    }
+
+    return 0;
+}
+
 static int new_stream_video(Muxer *mux, const OptionsContext *o,
                             OutputStream *ost, int *keep_pix_fmt,
                             enum VideoSyncMethod *vsync_method)
@@ -553,6 +634,7 @@ static int new_stream_video(Muxer *mux, const 
OptionsContext *o,
     AVFormatContext *oc = mux->fc;
     AVStream *st;
     const char *frame_rate = NULL, *max_frame_rate = NULL, *frame_aspect_ratio 
= NULL;
+    const char *stereo3d = NULL;
     int ret = 0;
 
     st  = ost->st;
@@ -585,6 +667,14 @@ static int new_stream_video(Muxer *mux, const 
OptionsContext *o,
         ost->frame_aspect_ratio = q;
     }
 
+    opt_match_per_stream_str(ost, &o->stereo3ds, oc, st, &stereo3d);
+    if (stereo3d) {
+        ret = parse_stereo3d_type(ost, stereo3d, &ms->stereo3d_type);
+        if (ret < 0)
+            return ret;
+        ms->stereo3d_set = 1;
+    }
+
     if (ost->enc) {
         AVCodecContext *video_enc = ost->enc->enc_ctx;
         const char *p = NULL, *fps_mode = NULL;
@@ -2067,7 +2157,7 @@ static int create_streams(Muxer *mux, const 
OptionsContext *o)
         return AVERROR(EINVAL);
     }
 
-    return 0;
+    return check_stereo3d_leftovers(mux, o);
 }
 
 static int setup_sync_queues(Muxer *mux, AVFormatContext *oc,
diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
index fdbc0fdc13..fd7260c3f7 100644
--- a/fftools/ffmpeg_opt.c
+++ b/fftools/ffmpeg_opt.c
@@ -1853,6 +1853,9 @@ const OptionDef options[] = {
     { "aspect",                     OPT_TYPE_STRING, OPT_VIDEO | OPT_PERSTREAM 
| OPT_OUTPUT,
         { .off = OFFSET(frame_aspect_ratios) },
         "set aspect ratio (4:3, 16:9 or 1.3333, 1.7777)", "aspect" },
+    { "stereo3d",                   OPT_TYPE_STRING, OPT_VIDEO | OPT_PERSTREAM 
| OPT_OUTPUT,
+        { .off = OFFSET(stereo3ds) },
+        "set stereoscopic 3D layout metadata (does not rearrange pixels)", 
"layout" },
     { "pix_fmt",                    OPT_TYPE_STRING, OPT_VIDEO | OPT_EXPERT | 
OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT,
         { .off = OFFSET(frame_pix_fmts) },
         "set pixel format", "format" },

-- 
To stop receiving notification emails like this one, please contact
[email protected].
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to