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]
