diff mbox series

[FFmpeg-devel,08/13] avformat/mux: Add flag for "not more than one stream of each type"

Message ID AS8P250MB074454B81C3F45502A6358D48F332@AS8P250MB0744.EURP250.PROD.OUTLOOK.COM
State New
Headers show
Series [FFmpeg-devel,01/13] avformat/mp3enc: Improve query_codec | expand

Checks

Context Check Description
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Andreas Rheinhardt March 20, 2024, 2:12 a.m. UTC
More exactly: Not more than one stream of each type for which
a default codec (i.e. AVOutputFormat.(audio|video|subtitle)_codec)
is set; for those types for which no such codec is set (or for
which no designated default codec in AVOutputFormat exists at all)
no streams are permitted.

Given that with this flag set the default codecs become more important,
they are now set explicitly to AV_CODEC_ID_NONE for "unset";
the earlier code relied on AV_CODEC_ID_NONE being equal to zero,
so that default static initialization set it accordingly;
but this is not how one is supposed to use an enum.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
---
 libavformat/ac4enc.c          |   4 +-
 libavformat/aeaenc.c          |  11 +--
 libavformat/alp.c             |   7 +-
 libavformat/apm.c             |  11 +--
 libavformat/apngenc.c         |   6 +-
 libavformat/argo_asf.c        |  11 +--
 libavformat/argo_cvg.c        |  11 +--
 libavformat/assenc.c          |   5 +-
 libavformat/astenc.c          |  11 +--
 libavformat/au.c              |   7 +-
 libavformat/cafenc.c          |   7 +-
 libavformat/chromaprint.c     |   8 +-
 libavformat/codec2.c          |   4 +-
 libavformat/gif.c             |   6 +-
 libavformat/ilbc.c            |  11 +--
 libavformat/ircamenc.c        |   7 +-
 libavformat/ivfenc.c          |  12 +--
 libavformat/kvag.c            |  11 +--
 libavformat/lrcenc.c          |   9 +--
 libavformat/microdvdenc.c     |   5 +-
 libavformat/mux.c             |  23 ++++++
 libavformat/mux.h             |  14 ++++
 libavformat/mux_utils.c       |  19 +++++
 libavformat/rawenc.c          | 135 ++++++++++++++++++++--------------
 libavformat/rcwtenc.c         |   9 ++-
 libavformat/sccenc.c          |   9 +--
 libavformat/segafilmenc.c     |  10 +--
 libavformat/srtenc.c          |   9 +--
 libavformat/supenc.c          |   9 +--
 libavformat/swfenc.c          |  12 +--
 libavformat/ttaenc.c          |  10 +--
 libavformat/ttmlenc.c         |   6 +-
 libavformat/wavenc.c          |  20 +----
 libavformat/webpenc.c         |   7 +-
 libavformat/webvttenc.c       |   5 +-
 libavformat/westwood_audenc.c |   7 +-
 libavformat/wvenc.c           |   5 +-
 libavformat/yuv4mpegenc.c     |   5 +-
 38 files changed, 241 insertions(+), 237 deletions(-)
diff mbox series

Patch

diff --git a/libavformat/ac4enc.c b/libavformat/ac4enc.c
index aefbfc2684..02b8d4d976 100644
--- a/libavformat/ac4enc.c
+++ b/libavformat/ac4enc.c
@@ -35,7 +35,7 @@  static int ac4_init(AVFormatContext *s)
 {
     AVCodecParameters *par = s->streams[0]->codecpar;
 
-    if (s->nb_streams != 1 || par->codec_id != AV_CODEC_ID_AC4) {
+    if (par->codec_id != AV_CODEC_ID_AC4) {
         av_log(s, AV_LOG_ERROR, "Only one AC-4 stream can be muxed by the AC-4 muxer\n");
         return AVERROR(EINVAL);
     }
@@ -95,6 +95,8 @@  const FFOutputFormat ff_ac4_muxer = {
     .priv_data_size    = sizeof(AC4Context),
     .p.audio_codec     = AV_CODEC_ID_AC4,
     .p.video_codec     = AV_CODEC_ID_NONE,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .init              = ac4_init,
     .write_packet      = ac4_write_packet,
     .p.priv_class      = &ac4_muxer_class,
diff --git a/libavformat/aeaenc.c b/libavformat/aeaenc.c
index 495e98c6a2..3303d108a5 100644
--- a/libavformat/aeaenc.c
+++ b/libavformat/aeaenc.c
@@ -29,14 +29,8 @@  static int aea_write_header(AVFormatContext *s)
 {
     const AVDictionaryEntry *title_entry;
     size_t title_length = 0;
-    AVStream *st;
-
-    if (s->nb_streams > 1) {
-        av_log(s, AV_LOG_ERROR, "Got more than one stream to encode. This is not supported.\n");
-        return AVERROR(EINVAL);
-    }
+    AVStream *st = s->streams[0];
 
-    st = s->streams[0];
     if (st->codecpar->ch_layout.nb_channels != 1 && st->codecpar->ch_layout.nb_channels != 2) {
         av_log(s, AV_LOG_ERROR, "Only maximum 2 channels are supported in the audio"
                " stream, %d channels were found.\n", st->codecpar->ch_layout.nb_channels);
@@ -108,7 +102,10 @@  const FFOutputFormat ff_aea_muxer = {
     .p.long_name      = NULL_IF_CONFIG_SMALL("MD STUDIO audio"),
     .p.extensions     = "aea",
     .p.audio_codec    = AV_CODEC_ID_ATRAC1,
+    .p.video_codec    = AV_CODEC_ID_NONE,
+    .p.subtitle_codec = AV_CODEC_ID_NONE,
 
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_header     = aea_write_header,
     .write_packet     = ff_raw_write_packet,
     .write_trailer    = aea_write_trailer,
diff --git a/libavformat/alp.c b/libavformat/alp.c
index 3db256cd05..9d507cb310 100644
--- a/libavformat/alp.c
+++ b/libavformat/alp.c
@@ -189,11 +189,6 @@  static int alp_write_init(AVFormatContext *s)
             alp->type = ALP_TYPE_TUN;
     }
 
-    if (s->nb_streams != 1) {
-        av_log(s, AV_LOG_ERROR, "Too many streams\n");
-        return AVERROR(EINVAL);
-    }
-
     par = s->streams[0]->codecpar;
 
     if (par->codec_id != AV_CODEC_ID_ADPCM_IMA_ALP) {
@@ -298,7 +293,9 @@  const FFOutputFormat ff_alp_muxer = {
     .p.extensions   = "tun,pcm",
     .p.audio_codec  = AV_CODEC_ID_ADPCM_IMA_ALP,
     .p.video_codec  = AV_CODEC_ID_NONE,
+    .p.subtitle_codec = AV_CODEC_ID_NONE,
     .p.priv_class   = &alp_muxer_class,
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .init           = alp_write_init,
     .write_header   = alp_write_header,
     .write_packet   = ff_raw_write_packet,
diff --git a/libavformat/apm.c b/libavformat/apm.c
index bcde82d958..89401dcf5b 100644
--- a/libavformat/apm.c
+++ b/libavformat/apm.c
@@ -215,14 +215,7 @@  const FFInputFormat ff_apm_demuxer = {
 #if CONFIG_APM_MUXER
 static int apm_write_init(AVFormatContext *s)
 {
-    AVCodecParameters *par;
-
-    if (s->nb_streams != 1) {
-        av_log(s, AV_LOG_ERROR, "APM files have exactly one stream\n");
-        return AVERROR(EINVAL);
-    }
-
-    par = s->streams[0]->codecpar;
+    AVCodecParameters *par = s->streams[0]->codecpar;
 
     if (par->codec_id != AV_CODEC_ID_ADPCM_IMA_APM) {
         av_log(s, AV_LOG_ERROR, "%s codec not supported\n",
@@ -311,6 +304,8 @@  const FFOutputFormat ff_apm_muxer = {
     .p.extensions   = "apm",
     .p.audio_codec  = AV_CODEC_ID_ADPCM_IMA_APM,
     .p.video_codec  = AV_CODEC_ID_NONE,
+    .p.subtitle_codec = AV_CODEC_ID_NONE,
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .init           = apm_write_init,
     .write_header   = apm_write_header,
     .write_packet   = ff_raw_write_packet,
diff --git a/libavformat/apngenc.c b/libavformat/apngenc.c
index a0af916f14..79b0dbf0f7 100644
--- a/libavformat/apngenc.c
+++ b/libavformat/apngenc.c
@@ -84,9 +84,7 @@  static int apng_write_header(AVFormatContext *format_context)
     APNGMuxContext *apng = format_context->priv_data;
     AVCodecParameters *par = format_context->streams[0]->codecpar;
 
-    if (format_context->nb_streams != 1 ||
-        format_context->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO ||
-        format_context->streams[0]->codecpar->codec_id   != AV_CODEC_ID_APNG) {
+    if (format_context->streams[0]->codecpar->codec_id   != AV_CODEC_ID_APNG) {
         av_log(format_context, AV_LOG_ERROR,
                "APNG muxer supports only a single video APNG stream.\n");
         return AVERROR(EINVAL);
@@ -315,6 +313,8 @@  const FFOutputFormat ff_apng_muxer = {
     .priv_data_size = sizeof(APNGMuxContext),
     .p.audio_codec  = AV_CODEC_ID_NONE,
     .p.video_codec  = AV_CODEC_ID_APNG,
+    .p.subtitle_codec = AV_CODEC_ID_NONE,
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_header   = apng_write_header,
     .write_packet   = apng_write_packet,
     .write_trailer  = apng_write_trailer,
diff --git a/libavformat/argo_asf.c b/libavformat/argo_asf.c
index 0e1eae24fb..28e3844394 100644
--- a/libavformat/argo_asf.c
+++ b/libavformat/argo_asf.c
@@ -288,14 +288,7 @@  const FFInputFormat ff_argo_asf_demuxer = {
 static int argo_asf_write_init(AVFormatContext *s)
 {
     ArgoASFMuxContext *ctx = s->priv_data;
-    const AVCodecParameters *par;
-
-    if (s->nb_streams != 1) {
-        av_log(s, AV_LOG_ERROR, "ASF files have exactly one stream\n");
-        return AVERROR(EINVAL);
-    }
-
-    par = s->streams[0]->codecpar;
+    const AVCodecParameters *par = s->streams[0]->codecpar;
 
     if (par->codec_id != AV_CODEC_ID_ADPCM_ARGO) {
         av_log(s, AV_LOG_ERROR, "%s codec not supported\n",
@@ -481,7 +474,9 @@  const FFOutputFormat ff_argo_asf_muxer = {
      */
     .p.audio_codec  = AV_CODEC_ID_ADPCM_ARGO,
     .p.video_codec  = AV_CODEC_ID_NONE,
+    .p.subtitle_codec = AV_CODEC_ID_NONE,
     .p.priv_class   = &argo_asf_muxer_class,
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .init           = argo_asf_write_init,
     .write_header   = argo_asf_write_header,
     .write_packet   = argo_asf_write_packet,
diff --git a/libavformat/argo_cvg.c b/libavformat/argo_cvg.c
index 0cf0bf3e9a..aacc34daeb 100644
--- a/libavformat/argo_cvg.c
+++ b/libavformat/argo_cvg.c
@@ -269,14 +269,7 @@  const FFInputFormat ff_argo_cvg_demuxer = {
 static int argo_cvg_write_init(AVFormatContext *s)
 {
     ArgoCVGMuxContext *ctx = s->priv_data;
-    const AVCodecParameters *par;
-
-    if (s->nb_streams != 1) {
-        av_log(s, AV_LOG_ERROR, "CVG files have exactly one stream\n");
-        return AVERROR(EINVAL);
-    }
-
-    par = s->streams[0]->codecpar;
+    const AVCodecParameters *par = s->streams[0]->codecpar;
 
     if (par->codec_id != AV_CODEC_ID_ADPCM_PSX) {
         av_log(s, AV_LOG_ERROR, "%s codec not supported\n",
@@ -408,7 +401,9 @@  const FFOutputFormat ff_argo_cvg_muxer = {
     .p.extensions   = "cvg",
     .p.audio_codec  = AV_CODEC_ID_ADPCM_PSX,
     .p.video_codec  = AV_CODEC_ID_NONE,
+    .p.subtitle_codec = AV_CODEC_ID_NONE,
     .p.priv_class   = &argo_cvg_muxer_class,
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .init           = argo_cvg_write_init,
     .write_header   = argo_cvg_write_header,
     .write_packet   = argo_cvg_write_packet,
diff --git a/libavformat/assenc.c b/libavformat/assenc.c
index dad0d81ca8..c23c77acda 100644
--- a/libavformat/assenc.c
+++ b/libavformat/assenc.c
@@ -50,7 +50,7 @@  static int write_header(AVFormatContext *s)
     ASSContext *ass = s->priv_data;
     AVCodecParameters *par = s->streams[0]->codecpar;
 
-    if (s->nb_streams != 1 || par->codec_id != AV_CODEC_ID_ASS) {
+    if (par->codec_id != AV_CODEC_ID_ASS) {
         av_log(s, AV_LOG_ERROR, "Exactly one ASS/SSA stream is needed.\n");
         return AVERROR(EINVAL);
     }
@@ -237,8 +237,11 @@  const FFOutputFormat ff_ass_muxer = {
     .p.long_name      = NULL_IF_CONFIG_SMALL("SSA (SubStation Alpha) subtitle"),
     .p.mime_type      = "text/x-ass",
     .p.extensions     = "ass,ssa",
+    .p.audio_codec    = AV_CODEC_ID_NONE,
+    .p.video_codec    = AV_CODEC_ID_NONE,
     .p.subtitle_codec = AV_CODEC_ID_ASS,
     .p.flags          = AVFMT_GLOBALHEADER | AVFMT_NOTIMESTAMPS | AVFMT_TS_NONSTRICT,
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .p.priv_class     = &ass_class,
     .priv_data_size = sizeof(ASSContext),
     .write_header   = write_header,
diff --git a/libavformat/astenc.c b/libavformat/astenc.c
index c6b02e3848..1fa06a2da8 100644
--- a/libavformat/astenc.c
+++ b/libavformat/astenc.c
@@ -49,16 +49,9 @@  static int ast_write_header(AVFormatContext *s)
 {
     ASTMuxContext *ast = s->priv_data;
     AVIOContext *pb = s->pb;
-    AVCodecParameters *par;
+    AVCodecParameters *par = s->streams[0]->codecpar;
     unsigned int codec_tag;
 
-    if (s->nb_streams == 1) {
-        par = s->streams[0]->codecpar;
-    } else {
-        av_log(s, AV_LOG_ERROR, "only one stream is supported\n");
-        return AVERROR(EINVAL);
-    }
-
     if (par->codec_id == AV_CODEC_ID_ADPCM_AFC) {
         av_log(s, AV_LOG_ERROR, "muxing ADPCM AFC is not implemented\n");
         return AVERROR_PATCHWELCOME;
@@ -204,6 +197,8 @@  const FFOutputFormat ff_ast_muxer = {
     .priv_data_size    = sizeof(ASTMuxContext),
     .p.audio_codec     = AV_CODEC_ID_PCM_S16BE_PLANAR,
     .p.video_codec     = AV_CODEC_ID_NONE,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_header      = ast_write_header,
     .write_packet      = ast_write_packet,
     .write_trailer     = ast_write_trailer,
diff --git a/libavformat/au.c b/libavformat/au.c
index da1fc79f0d..98f1a5b507 100644
--- a/libavformat/au.c
+++ b/libavformat/au.c
@@ -291,11 +291,6 @@  static int au_write_header(AVFormatContext *s)
     AVCodecParameters *par = s->streams[0]->codecpar;
     AVBPrint annotations;
 
-    if (s->nb_streams != 1) {
-        av_log(s, AV_LOG_ERROR, "only one stream is supported\n");
-        return AVERROR(EINVAL);
-    }
-
     par->codec_tag = ff_codec_get_tag(codec_au_tags, par->codec_id);
     if (!par->codec_tag) {
         av_log(s, AV_LOG_ERROR, "unsupported codec\n");
@@ -346,7 +341,9 @@  const FFOutputFormat ff_au_muxer = {
     .p.codec_tag    = au_codec_tags,
     .p.audio_codec  = AV_CODEC_ID_PCM_S16BE,
     .p.video_codec  = AV_CODEC_ID_NONE,
+    .p.subtitle_codec = AV_CODEC_ID_NONE,
     .p.flags        = AVFMT_NOTIMESTAMPS,
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .priv_data_size = sizeof(AUContext),
     .write_header  = au_write_header,
     .write_packet  = ff_raw_write_packet,
diff --git a/libavformat/cafenc.c b/libavformat/cafenc.c
index 67be59806c..426bc05209 100644
--- a/libavformat/cafenc.c
+++ b/libavformat/cafenc.c
@@ -118,11 +118,6 @@  static int caf_write_header(AVFormatContext *s)
     int64_t chunk_size = 0;
     int frame_size = par->frame_size, sample_rate = par->sample_rate;
 
-    if (s->nb_streams != 1) {
-        av_log(s, AV_LOG_ERROR, "CAF files have exactly one stream\n");
-        return AVERROR(EINVAL);
-    }
-
     switch (par->codec_id) {
     case AV_CODEC_ID_AAC:
         av_log(s, AV_LOG_ERROR, "muxing codec currently unsupported\n");
@@ -284,6 +279,8 @@  const FFOutputFormat ff_caf_muxer = {
     .priv_data_size = sizeof(CAFContext),
     .p.audio_codec  = AV_CODEC_ID_PCM_S16BE,
     .p.video_codec  = AV_CODEC_ID_NONE,
+    .p.subtitle_codec = AV_CODEC_ID_NONE,
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_header   = caf_write_header,
     .write_packet   = caf_write_packet,
     .write_trailer  = caf_write_trailer,
diff --git a/libavformat/chromaprint.c b/libavformat/chromaprint.c
index 3229e9160d..62264d6a4b 100644
--- a/libavformat/chromaprint.c
+++ b/libavformat/chromaprint.c
@@ -85,11 +85,6 @@  static int write_header(AVFormatContext *s)
 #endif
     }
 
-    if (s->nb_streams != 1) {
-        av_log(s, AV_LOG_ERROR, "Only one stream is supported\n");
-        return AVERROR(EINVAL);
-    }
-
     st = s->streams[0];
 
     if (st->codecpar->ch_layout.nb_channels > 2) {
@@ -182,6 +177,9 @@  const FFOutputFormat ff_chromaprint_muxer = {
     .p.long_name       = NULL_IF_CONFIG_SMALL("Chromaprint"),
     .priv_data_size    = sizeof(ChromaprintMuxContext),
     .p.audio_codec     = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE),
+    .p.video_codec     = AV_CODEC_ID_NONE,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_header      = write_header,
     .write_packet      = write_packet,
     .write_trailer     = write_trailer,
diff --git a/libavformat/codec2.c b/libavformat/codec2.c
index 9ed57af0a8..e49408e0fb 100644
--- a/libavformat/codec2.c
+++ b/libavformat/codec2.c
@@ -216,7 +216,7 @@  static int codec2_write_header(AVFormatContext *s)
 {
     AVStream *st;
 
-    if (s->nb_streams != 1 || s->streams[0]->codecpar->codec_id != AV_CODEC_ID_CODEC2) {
+    if (s->streams[0]->codecpar->codec_id != AV_CODEC_ID_CODEC2) {
         av_log(s, AV_LOG_ERROR, ".c2 files must have exactly one codec2 stream\n");
         return AVERROR(EINVAL);
     }
@@ -317,7 +317,9 @@  const FFOutputFormat ff_codec2_muxer = {
     .p.extensions   = "c2",
     .p.audio_codec  = AV_CODEC_ID_CODEC2,
     .p.video_codec  = AV_CODEC_ID_NONE,
+    .p.subtitle_codec = AV_CODEC_ID_NONE,
     .p.flags        = AVFMT_NOTIMESTAMPS,
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_header   = codec2_write_header,
     .write_packet   = ff_raw_write_packet,
 };
diff --git a/libavformat/gif.c b/libavformat/gif.c
index 568867cc5d..fe45637814 100644
--- a/libavformat/gif.c
+++ b/libavformat/gif.c
@@ -42,9 +42,7 @@  typedef struct GIFContext {
 
 static int gif_write_header(AVFormatContext *s)
 {
-    if (s->nb_streams != 1 ||
-        s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO ||
-        s->streams[0]->codecpar->codec_id   != AV_CODEC_ID_GIF) {
+    if (s->streams[0]->codecpar->codec_id   != AV_CODEC_ID_GIF) {
         av_log(s, AV_LOG_ERROR,
                "GIF muxer supports only a single video GIF stream.\n");
         return AVERROR(EINVAL);
@@ -213,6 +211,8 @@  const FFOutputFormat ff_gif_muxer = {
     .priv_data_size = sizeof(GIFContext),
     .p.audio_codec  = AV_CODEC_ID_NONE,
     .p.video_codec  = AV_CODEC_ID_GIF,
+    .p.subtitle_codec = AV_CODEC_ID_NONE,
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_header   = gif_write_header,
     .write_packet   = gif_write_packet,
     .write_trailer  = gif_write_trailer,
diff --git a/libavformat/ilbc.c b/libavformat/ilbc.c
index bb98d0e62a..2e102a4be7 100644
--- a/libavformat/ilbc.c
+++ b/libavformat/ilbc.c
@@ -33,13 +33,7 @@  static const char mode30_header[] = "#!iLBC30\n";
 static int ilbc_write_header(AVFormatContext *s)
 {
     AVIOContext *pb = s->pb;
-    AVCodecParameters *par;
-
-    if (s->nb_streams != 1) {
-        av_log(s, AV_LOG_ERROR, "Unsupported number of streams\n");
-        return AVERROR(EINVAL);
-    }
-    par = s->streams[0]->codecpar;
+    AVCodecParameters *par = s->streams[0]->codecpar;
 
     if (par->codec_id != AV_CODEC_ID_ILBC) {
         av_log(s, AV_LOG_ERROR, "Unsupported codec\n");
@@ -127,8 +121,11 @@  const FFOutputFormat ff_ilbc_muxer = {
     .p.long_name    = NULL_IF_CONFIG_SMALL("iLBC storage"),
     .p.mime_type    = "audio/iLBC",
     .p.extensions   = "lbc",
+    .p.video_codec    = AV_CODEC_ID_NONE,
     .p.audio_codec  = AV_CODEC_ID_ILBC,
+    .p.subtitle_codec = AV_CODEC_ID_NONE,
     .p.flags        = AVFMT_NOTIMESTAMPS,
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_header = ilbc_write_header,
     .write_packet = ff_raw_write_packet,
 };
diff --git a/libavformat/ircamenc.c b/libavformat/ircamenc.c
index ceeab2b305..7600d9a8a9 100644
--- a/libavformat/ircamenc.c
+++ b/libavformat/ircamenc.c
@@ -32,11 +32,6 @@  static int ircam_write_header(AVFormatContext *s)
     AVCodecParameters *par = s->streams[0]->codecpar;
     uint32_t tag;
 
-    if (s->nb_streams != 1) {
-        av_log(s, AV_LOG_ERROR, "only one stream is supported\n");
-        return AVERROR(EINVAL);
-    }
-
     tag = ff_codec_get_tag(ff_codec_ircam_le_tags, par->codec_id);
     if (!tag) {
         av_log(s, AV_LOG_ERROR, "unsupported codec\n");
@@ -57,6 +52,8 @@  const FFOutputFormat ff_ircam_muxer = {
     .p.long_name    = NULL_IF_CONFIG_SMALL("Berkeley/IRCAM/CARL Sound Format"),
     .p.audio_codec  = AV_CODEC_ID_PCM_S16LE,
     .p.video_codec  = AV_CODEC_ID_NONE,
+    .p.subtitle_codec = AV_CODEC_ID_NONE,
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_header   = ircam_write_header,
     .write_packet   = ff_raw_write_packet,
     .p.codec_tag    = (const AVCodecTag *const []){ ff_codec_ircam_le_tags, 0 },
diff --git a/libavformat/ivfenc.c b/libavformat/ivfenc.c
index 88399099d4..09782eecd6 100644
--- a/libavformat/ivfenc.c
+++ b/libavformat/ivfenc.c
@@ -29,15 +29,9 @@  typedef struct IVFEncContext {
 
 static int ivf_init(AVFormatContext *s)
 {
-    AVCodecParameters *par;
+    AVCodecParameters *par = s->streams[0]->codecpar;
 
-    if (s->nb_streams != 1) {
-        av_log(s, AV_LOG_ERROR, "Format supports only exactly one video stream\n");
-        return AVERROR(EINVAL);
-    }
-    par = s->streams[0]->codecpar;
-    if (par->codec_type != AVMEDIA_TYPE_VIDEO ||
-        !(par->codec_id == AV_CODEC_ID_AV1 ||
+    if (!(par->codec_id == AV_CODEC_ID_AV1 ||
           par->codec_id == AV_CODEC_ID_VP8 ||
           par->codec_id == AV_CODEC_ID_VP9)) {
         av_log(s, AV_LOG_ERROR, "Currently only VP8, VP9 and AV1 are supported!\n");
@@ -125,7 +119,9 @@  const FFOutputFormat ff_ivf_muxer = {
     .p.extensions   = "ivf",
     .p.audio_codec  = AV_CODEC_ID_NONE,
     .p.video_codec  = AV_CODEC_ID_VP8,
+    .p.subtitle_codec = AV_CODEC_ID_NONE,
     .p.codec_tag    = (const AVCodecTag* const []){ codec_ivf_tags, 0 },
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .priv_data_size = sizeof(IVFEncContext),
     .init         = ivf_init,
     .write_header = ivf_write_header,
diff --git a/libavformat/kvag.c b/libavformat/kvag.c
index bea1dda3e5..2053f4730d 100644
--- a/libavformat/kvag.c
+++ b/libavformat/kvag.c
@@ -129,14 +129,7 @@  const FFInputFormat ff_kvag_demuxer = {
 #if CONFIG_KVAG_MUXER
 static int kvag_write_init(AVFormatContext *s)
 {
-    AVCodecParameters *par;
-
-    if (s->nb_streams != 1) {
-        av_log(s, AV_LOG_ERROR, "KVAG files have exactly one stream\n");
-        return AVERROR(EINVAL);
-    }
-
-    par = s->streams[0]->codecpar;
+    AVCodecParameters *par = s->streams[0]->codecpar;
 
     if (par->codec_id != AV_CODEC_ID_ADPCM_IMA_SSI) {
         av_log(s, AV_LOG_ERROR, "%s codec not supported\n",
@@ -196,6 +189,8 @@  const FFOutputFormat ff_kvag_muxer = {
     .p.extensions   = "vag",
     .p.audio_codec  = AV_CODEC_ID_ADPCM_IMA_SSI,
     .p.video_codec  = AV_CODEC_ID_NONE,
+    .p.subtitle_codec = AV_CODEC_ID_NONE,
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .init           = kvag_write_init,
     .write_header   = kvag_write_header,
     .write_packet   = ff_raw_write_packet,
diff --git a/libavformat/lrcenc.c b/libavformat/lrcenc.c
index d66be9a8fa..15c31d33b3 100644
--- a/libavformat/lrcenc.c
+++ b/libavformat/lrcenc.c
@@ -37,12 +37,6 @@  static int lrc_write_header(AVFormatContext *s)
 {
     const AVDictionaryEntry *metadata_item;
 
-    if(s->nb_streams != 1 ||
-       s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
-        av_log(s, AV_LOG_ERROR,
-               "LRC supports only a single subtitle stream.\n");
-        return AVERROR(EINVAL);
-    }
     if(s->streams[0]->codecpar->codec_id != AV_CODEC_ID_SUBRIP &&
        s->streams[0]->codecpar->codec_id != AV_CODEC_ID_TEXT) {
         av_log(s, AV_LOG_ERROR, "Unsupported subtitle codec: %s\n",
@@ -131,7 +125,10 @@  const FFOutputFormat ff_lrc_muxer = {
     .p.extensions     = "lrc",
     .p.flags          = AVFMT_VARIABLE_FPS | AVFMT_GLOBALHEADER |
                         AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT,
+    .p.video_codec    = AV_CODEC_ID_NONE,
+    .p.audio_codec    = AV_CODEC_ID_NONE,
     .p.subtitle_codec = AV_CODEC_ID_SUBRIP,
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .priv_data_size = 0,
     .write_header   = lrc_write_header,
     .write_packet   = lrc_write_packet,
diff --git a/libavformat/microdvdenc.c b/libavformat/microdvdenc.c
index 950309981b..b9cadb8be4 100644
--- a/libavformat/microdvdenc.c
+++ b/libavformat/microdvdenc.c
@@ -29,7 +29,7 @@  static int microdvd_write_header(struct AVFormatContext *s)
     AVCodecParameters *par = s->streams[0]->codecpar;
     AVRational framerate = s->streams[0]->avg_frame_rate;
 
-    if (s->nb_streams != 1 || par->codec_id != AV_CODEC_ID_MICRODVD) {
+    if (par->codec_id != AV_CODEC_ID_MICRODVD) {
         av_log(s, AV_LOG_ERROR, "Exactly one MicroDVD stream is needed.\n");
         return -1;
     }
@@ -62,7 +62,10 @@  const FFOutputFormat ff_microdvd_muxer = {
     .p.mime_type      = "text/x-microdvd",
     .p.extensions     = "sub",
     .p.flags          = AVFMT_NOTIMESTAMPS,
+    .p.video_codec    = AV_CODEC_ID_NONE,
+    .p.audio_codec    = AV_CODEC_ID_NONE,
     .p.subtitle_codec = AV_CODEC_ID_MICRODVD,
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_header   = microdvd_write_header,
     .write_packet   = microdvd_write_packet,
 };
diff --git a/libavformat/mux.c b/libavformat/mux.c
index a2cae97397..134c2875b6 100644
--- a/libavformat/mux.c
+++ b/libavformat/mux.c
@@ -188,6 +188,12 @@  static int init_muxer(AVFormatContext *s, AVDictionary **options)
     AVDictionary *tmp = NULL;
     const FFOutputFormat *of = ffofmt(s->oformat);
     AVDictionaryEntry *e;
+    static const unsigned default_codec_offsets[] = {
+        [AVMEDIA_TYPE_VIDEO]    = offsetof(AVOutputFormat, video_codec),
+        [AVMEDIA_TYPE_AUDIO]    = offsetof(AVOutputFormat, audio_codec),
+        [AVMEDIA_TYPE_SUBTITLE] = offsetof(AVOutputFormat, subtitle_codec),
+    };
+    unsigned nb_type[FF_ARRAY_ELEMS(default_codec_offsets)] = { 0 };
     int ret = 0;
 
     if (options)
@@ -262,6 +268,23 @@  static int init_muxer(AVFormatContext *s, AVDictionary **options)
             }
             break;
         }
+        if (of->flags_internal & FF_OFMT_FLAG_MAX_ONE_OF_EACH) {
+            enum AVCodecID default_codec_id = AV_CODEC_ID_NONE;
+            unsigned nb;
+            if ((unsigned)par->codec_type < FF_ARRAY_ELEMS(default_codec_offsets)) {
+                nb = ++nb_type[par->codec_type];
+                if (default_codec_offsets[par->codec_type])
+                    default_codec_id = *(const enum AVCodecID*)((const char*)of + default_codec_offsets[par->codec_type]);
+            }
+            if (default_codec_id == AV_CODEC_ID_NONE || nb > 1) {
+                const char *type = av_get_media_type_string(par->codec_type);
+                av_log(s, AV_LOG_ERROR, "%s muxer does not support %s stream of type %s\n",
+                       of->p.name, default_codec_id == AV_CODEC_ID_NONE ? "any" : "more than one",
+                       type ? type : "unknown");
+                ret = AVERROR(EINVAL);
+                goto fail;
+            }
+        }
 
 #if FF_API_AVSTREAM_SIDE_DATA
 FF_DISABLE_DEPRECATION_WARNINGS
diff --git a/libavformat/mux.h b/libavformat/mux.h
index bd3eb53380..71ee5fd06b 100644
--- a/libavformat/mux.h
+++ b/libavformat/mux.h
@@ -36,6 +36,20 @@  struct AVDeviceInfoList;
  * of the return value in case of flushing.
  */
 #define FF_OFMT_FLAG_ALLOW_FLUSH                    (1 << 1)
+/**
+ * If this flag is set, it indicates that for each codec type
+ * whose corresponding default codec (i.e. AVOutputFormat.audio_codec,
+ * AVOutputFormat.video_codec and AVOutputFormat.subtitle_codec)
+ * is set (i.e. != AV_CODEC_ID_NONE) only one stream of this type
+ * can be muxed. It furthermore indicates that no stream with
+ * a codec type that has no default codec or whose default codec
+ * is AV_CODEC_ID_NONE can be muxed.
+ * Both of these restrictions are checked generically before
+ * the actual muxer's init/write_header callbacks.
+ *
+ * This flag is incompatible with FF_OFMT_FLAG_SUPPORT_ATTACHMENTS.
+ */
+#define FF_OFMT_FLAG_MAX_ONE_OF_EACH                (1 << 2)
 
 typedef struct FFOutputFormat {
     /**
diff --git a/libavformat/mux_utils.c b/libavformat/mux_utils.c
index 841a19a3a2..8a0ad4c9aa 100644
--- a/libavformat/mux_utils.c
+++ b/libavformat/mux_utils.c
@@ -44,6 +44,25 @@  int avformat_query_codec(const AVOutputFormat *ofmt, enum AVCodecID codec_id,
                   codec_id == ofmt->audio_codec ||
                   codec_id == ofmt->subtitle_codec))
             return 1;
+        else if (ffofmt(ofmt)->flags_internal & FF_OFMT_FLAG_MAX_ONE_OF_EACH) {
+            enum AVMediaType type = avcodec_get_type(codec_id);
+            switch (type) {
+            case AVMEDIA_TYPE_AUDIO:
+                if (ofmt->audio_codec == AV_CODEC_ID_NONE)
+                    return 0;
+                break;
+            case AVMEDIA_TYPE_VIDEO:
+                if (ofmt->video_codec == AV_CODEC_ID_NONE)
+                    return 0;
+                break;
+            case AVMEDIA_TYPE_SUBTITLE:
+                if (ofmt->subtitle_codec == AV_CODEC_ID_NONE)
+                    return 0;
+                break;
+            default:
+                return 0;
+            }
+        }
     }
     return AVERROR_PATCHWELCOME;
 }
diff --git a/libavformat/rawenc.c b/libavformat/rawenc.c
index f916db13a2..49dc0a9109 100644
--- a/libavformat/rawenc.c
+++ b/libavformat/rawenc.c
@@ -34,28 +34,6 @@  int ff_raw_write_packet(AVFormatContext *s, AVPacket *pkt)
     return 0;
 }
 
-static int force_one_stream(AVFormatContext *s)
-{
-    if (s->nb_streams != 1) {
-        av_log(s, AV_LOG_ERROR, "%s files have exactly one stream\n",
-               s->oformat->name);
-        return AVERROR(EINVAL);
-    }
-    if (   s->oformat->audio_codec != AV_CODEC_ID_NONE
-        && s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) {
-        av_log(s, AV_LOG_ERROR, "%s files have exactly one audio stream\n",
-               s->oformat->name);
-        return AVERROR(EINVAL);
-    }
-    if (   s->oformat->video_codec != AV_CODEC_ID_NONE
-        && s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) {
-        av_log(s, AV_LOG_ERROR, "%s files have exactly one video stream\n",
-               s->oformat->name);
-        return AVERROR(EINVAL);
-    }
-    return 0;
-}
-
 /* Note: Do not forget to add new entries to the Makefile as well. */
 
 #if CONFIG_AC3_MUXER
@@ -66,7 +44,8 @@  const FFOutputFormat ff_ac3_muxer = {
     .p.extensions      = "ac3",
     .p.audio_codec     = AV_CODEC_ID_AC3,
     .p.video_codec     = AV_CODEC_ID_NONE,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -98,7 +77,8 @@  const FFOutputFormat ff_adx_muxer = {
     .p.extensions      = "adx",
     .p.audio_codec     = AV_CODEC_ID_ADPCM_ADX,
     .p.video_codec     = AV_CODEC_ID_NONE,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .write_trailer     = adx_write_trailer,
     .p.flags           = AVFMT_NOTIMESTAMPS,
@@ -112,7 +92,8 @@  const FFOutputFormat ff_aptx_muxer = {
     .p.extensions      = "aptx",
     .p.audio_codec     = AV_CODEC_ID_APTX,
     .p.video_codec     = AV_CODEC_ID_NONE,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -125,7 +106,8 @@  const FFOutputFormat ff_aptx_hd_muxer = {
     .p.extensions      = "aptxhd",
     .p.audio_codec     = AV_CODEC_ID_APTX_HD,
     .p.video_codec     = AV_CODEC_ID_NONE,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -138,7 +120,8 @@  const FFOutputFormat ff_avs2_muxer = {
     .p.extensions      = "avs,avs2",
     .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.video_codec     = AV_CODEC_ID_AVS2,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -151,7 +134,8 @@  const FFOutputFormat ff_avs3_muxer = {
     .p.extensions      = "avs3",
     .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.video_codec     = AV_CODEC_ID_AVS3,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -165,7 +149,8 @@  const FFOutputFormat ff_cavsvideo_muxer = {
     .p.extensions      = "cavs",
     .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.video_codec     = AV_CODEC_ID_CAVS,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -177,7 +162,8 @@  const FFOutputFormat ff_codec2raw_muxer = {
     .p.long_name       = NULL_IF_CONFIG_SMALL("raw codec2 muxer"),
     .p.audio_codec     = AV_CODEC_ID_CODEC2,
     .p.video_codec     = AV_CODEC_ID_NONE,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -185,6 +171,15 @@  const FFOutputFormat ff_codec2raw_muxer = {
 
 
 #if CONFIG_DATA_MUXER
+static av_cold int force_one_stream(AVFormatContext *s)
+{
+    if (s->nb_streams != 1) {
+        av_log(s, AV_LOG_ERROR, "This muxer supports only one stream.\n");
+        return AVERROR(EINVAL);
+    }
+    return 0;
+}
+
 const FFOutputFormat ff_data_muxer = {
     .p.name            = "data",
     .p.long_name       = NULL_IF_CONFIG_SMALL("raw data"),
@@ -201,7 +196,8 @@  const FFOutputFormat ff_dfpwm_muxer = {
     .p.extensions      = "dfpwm",
     .p.audio_codec     = AV_CODEC_ID_DFPWM,
     .p.video_codec     = AV_CODEC_ID_NONE,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -214,7 +210,8 @@  const FFOutputFormat ff_dirac_muxer = {
     .p.extensions      = "drc,vc2",
     .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.video_codec     = AV_CODEC_ID_DIRAC,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -227,7 +224,8 @@  const FFOutputFormat ff_dnxhd_muxer = {
     .p.extensions      = "dnxhd,dnxhr",
     .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.video_codec     = AV_CODEC_ID_DNXHD,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -241,7 +239,8 @@  const FFOutputFormat ff_dts_muxer = {
     .p.extensions      = "dts",
     .p.audio_codec     = AV_CODEC_ID_DTS,
     .p.video_codec     = AV_CODEC_ID_NONE,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -255,7 +254,8 @@  const FFOutputFormat ff_eac3_muxer = {
     .p.extensions      = "eac3,ec3",
     .p.audio_codec     = AV_CODEC_ID_EAC3,
     .p.video_codec     = AV_CODEC_ID_NONE,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -269,7 +269,8 @@  const FFOutputFormat ff_g722_muxer = {
     .p.extensions      = "g722",
     .p.audio_codec     = AV_CODEC_ID_ADPCM_G722,
     .p.video_codec     = AV_CODEC_ID_NONE,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -283,7 +284,8 @@  const FFOutputFormat ff_g723_1_muxer = {
     .p.extensions      = "tco,rco",
     .p.audio_codec     = AV_CODEC_ID_G723_1,
     .p.video_codec     = AV_CODEC_ID_NONE,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -295,7 +297,8 @@  const FFOutputFormat ff_g726_muxer = {
     .p.long_name       = NULL_IF_CONFIG_SMALL("raw big-endian G.726 (\"left-justified\")"),
     .p.audio_codec     = AV_CODEC_ID_ADPCM_G726,
     .p.video_codec     = AV_CODEC_ID_NONE,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -307,7 +310,8 @@  const FFOutputFormat ff_g726le_muxer = {
     .p.long_name       = NULL_IF_CONFIG_SMALL("raw little-endian G.726 (\"right-justified\")"),
     .p.audio_codec     = AV_CODEC_ID_ADPCM_G726LE,
     .p.video_codec     = AV_CODEC_ID_NONE,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -321,7 +325,8 @@  const FFOutputFormat ff_gsm_muxer = {
     .p.extensions      = "gsm",
     .p.audio_codec     = AV_CODEC_ID_GSM,
     .p.video_codec     = AV_CODEC_ID_NONE,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -335,7 +340,8 @@  const FFOutputFormat ff_h261_muxer = {
     .p.extensions      = "h261",
     .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.video_codec     = AV_CODEC_ID_H261,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -349,7 +355,8 @@  const FFOutputFormat ff_h263_muxer = {
     .p.extensions      = "h263",
     .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.video_codec     = AV_CODEC_ID_H263,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -371,7 +378,8 @@  const FFOutputFormat ff_h264_muxer = {
     .p.extensions      = "h264,264",
     .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.video_codec     = AV_CODEC_ID_H264,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .check_bitstream   = h264_check_bitstream,
     .p.flags           = AVFMT_NOTIMESTAMPS,
@@ -394,7 +402,8 @@  const FFOutputFormat ff_vvc_muxer = {
     .p.extensions      = "vvc,h266,266",
     .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.video_codec     = AV_CODEC_ID_VVC,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .check_bitstream   = vvc_check_bitstream,
     .p.flags           = AVFMT_NOTIMESTAMPS,
@@ -417,7 +426,8 @@  const FFOutputFormat ff_hevc_muxer = {
     .p.extensions      = "hevc,h265,265",
     .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.video_codec     = AV_CODEC_ID_HEVC,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .check_bitstream   = hevc_check_bitstream,
     .p.flags           = AVFMT_NOTIMESTAMPS,
@@ -431,7 +441,8 @@  const FFOutputFormat ff_evc_muxer = {
     .p.extensions      = "evc",
     .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.video_codec     = AV_CODEC_ID_EVC,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -444,7 +455,8 @@  const FFOutputFormat ff_m4v_muxer = {
     .p.extensions      = "m4v",
     .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.video_codec     = AV_CODEC_ID_MPEG4,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -458,7 +470,8 @@  const FFOutputFormat ff_mjpeg_muxer = {
     .p.extensions      = "mjpg,mjpeg",
     .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.video_codec     = AV_CODEC_ID_MJPEG,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -471,7 +484,8 @@  const FFOutputFormat ff_mlp_muxer = {
     .p.extensions      = "mlp",
     .p.audio_codec     = AV_CODEC_ID_MLP,
     .p.video_codec     = AV_CODEC_ID_NONE,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -485,7 +499,8 @@  const FFOutputFormat ff_mp2_muxer = {
     .p.extensions      = "mp2,m2a,mpa",
     .p.audio_codec     = AV_CODEC_ID_MP2,
     .p.video_codec     = AV_CODEC_ID_NONE,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -499,7 +514,8 @@  const FFOutputFormat ff_mpeg1video_muxer = {
     .p.extensions      = "mpg,mpeg,m1v",
     .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.video_codec     = AV_CODEC_ID_MPEG1VIDEO,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -512,7 +528,8 @@  const FFOutputFormat ff_mpeg2video_muxer = {
     .p.extensions      = "m2v",
     .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.video_codec     = AV_CODEC_ID_MPEG2VIDEO,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -531,7 +548,8 @@  const FFOutputFormat ff_obu_muxer = {
     .p.extensions      = "obu",
     .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.video_codec     = AV_CODEC_ID_AV1,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .check_bitstream   = obu_check_bitstream,
     .p.flags           = AVFMT_NOTIMESTAMPS,
@@ -545,6 +563,7 @@  const FFOutputFormat ff_rawvideo_muxer = {
     .p.extensions      = "yuv,rgb",
     .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.video_codec     = AV_CODEC_ID_RAWVIDEO,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -557,7 +576,9 @@  const FFOutputFormat ff_sbc_muxer = {
     .p.mime_type       = "audio/x-sbc",
     .p.extensions      = "sbc,msbc",
     .p.audio_codec     = AV_CODEC_ID_SBC,
-    .init              = force_one_stream,
+    .p.video_codec     = AV_CODEC_ID_NONE,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -570,7 +591,8 @@  const FFOutputFormat ff_truehd_muxer = {
     .p.extensions      = "thd",
     .p.audio_codec     = AV_CODEC_ID_TRUEHD,
     .p.video_codec     = AV_CODEC_ID_NONE,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
@@ -583,7 +605,8 @@  const FFOutputFormat ff_vc1_muxer = {
     .p.extensions      = "vc1",
     .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.video_codec     = AV_CODEC_ID_VC1,
-    .init              = force_one_stream,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_packet      = ff_raw_write_packet,
     .p.flags           = AVFMT_NOTIMESTAMPS,
 };
diff --git a/libavformat/rcwtenc.c b/libavformat/rcwtenc.c
index ed72de81ef..4ccd7b93c8 100644
--- a/libavformat/rcwtenc.c
+++ b/libavformat/rcwtenc.c
@@ -84,10 +84,8 @@  static void rcwt_flush_cluster(AVFormatContext *avf)
 
 static int rcwt_write_header(AVFormatContext *avf)
 {
-    if (avf->nb_streams != 1 || avf->streams[0]->codecpar->codec_id != AV_CODEC_ID_EIA_608) {
-        av_log(avf, AV_LOG_ERROR,
-                "RCWT supports only one CC (608/708) stream, more than one stream was "
-                "provided or its codec type was not CC (608/708)\n");
+    if (avf->streams[0]->codecpar->codec_id != AV_CODEC_ID_EIA_608) {
+        av_log(avf, AV_LOG_ERROR, "RCWT supports only CC (608/708)\n");
         return AVERROR(EINVAL);
     }
 
@@ -168,7 +166,10 @@  const FFOutputFormat ff_rcwt_muxer = {
     .p.long_name        = NULL_IF_CONFIG_SMALL("RCWT (Raw Captions With Time)"),
     .p.extensions       = "bin",
     .p.flags            = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT,
+    .p.video_codec      = AV_CODEC_ID_NONE,
+    .p.audio_codec      = AV_CODEC_ID_NONE,
     .p.subtitle_codec   = AV_CODEC_ID_EIA_608,
+    .flags_internal     = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .priv_data_size     = sizeof(RCWTContext),
     .write_header       = rcwt_write_header,
     .write_packet       = rcwt_write_packet,
diff --git a/libavformat/sccenc.c b/libavformat/sccenc.c
index ea7865ecf2..040d1ac96c 100644
--- a/libavformat/sccenc.c
+++ b/libavformat/sccenc.c
@@ -35,12 +35,6 @@  static int scc_write_header(AVFormatContext *avf)
 {
     SCCContext *scc = avf->priv_data;
 
-    if (avf->nb_streams != 1 ||
-        avf->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
-        av_log(avf, AV_LOG_ERROR,
-               "SCC supports only a single subtitles stream.\n");
-        return AVERROR(EINVAL);
-    }
     if (avf->streams[0]->codecpar->codec_id != AV_CODEC_ID_EIA_608) {
         av_log(avf, AV_LOG_ERROR,
                "Unsupported subtitles codec: %s\n",
@@ -117,7 +111,10 @@  const FFOutputFormat ff_scc_muxer = {
     .p.long_name      = NULL_IF_CONFIG_SMALL("Scenarist Closed Captions"),
     .p.extensions     = "scc",
     .p.flags          = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT,
+    .p.video_codec    = AV_CODEC_ID_NONE,
+    .p.audio_codec    = AV_CODEC_ID_NONE,
     .p.subtitle_codec = AV_CODEC_ID_EIA_608,
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .priv_data_size = sizeof(SCCContext),
     .write_header   = scc_write_header,
     .write_packet   = scc_write_packet,
diff --git a/libavformat/segafilmenc.c b/libavformat/segafilmenc.c
index ba06dc655d..88a5b9f972 100644
--- a/libavformat/segafilmenc.c
+++ b/libavformat/segafilmenc.c
@@ -124,10 +124,6 @@  static int film_init(AVFormatContext *format_context)
     for (int i = 0; i < format_context->nb_streams; i++) {
         AVStream *st = format_context->streams[i];
         if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
-            if (film->audio_index > -1) {
-                av_log(format_context, AV_LOG_ERROR, "Sega FILM allows a maximum of one audio stream.\n");
-                return AVERROR(EINVAL);
-            }
             if (get_audio_codec_id(st->codecpar->codec_id) < 0) {
                 av_log(format_context, AV_LOG_ERROR,
                        "Incompatible audio stream format.\n");
@@ -137,10 +133,6 @@  static int film_init(AVFormatContext *format_context)
         }
 
         if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
-            if (film->video_index > -1) {
-                av_log(format_context, AV_LOG_ERROR, "Sega FILM allows a maximum of one video stream.\n");
-                return AVERROR(EINVAL);
-            }
             if (st->codecpar->codec_id != AV_CODEC_ID_CINEPAK &&
                 st->codecpar->codec_id != AV_CODEC_ID_RAWVIDEO) {
                 av_log(format_context, AV_LOG_ERROR,
@@ -287,6 +279,8 @@  const FFOutputFormat ff_segafilm_muxer = {
     .priv_data_size = sizeof(FILMOutputContext),
     .p.audio_codec  = AV_CODEC_ID_PCM_S16BE_PLANAR,
     .p.video_codec  = AV_CODEC_ID_CINEPAK,
+    .p.subtitle_codec = AV_CODEC_ID_NONE,
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .init           = film_init,
     .write_trailer  = film_write_header,
     .write_packet   = film_write_packet,
diff --git a/libavformat/srtenc.c b/libavformat/srtenc.c
index 30dc59e0f8..a8a16a7f50 100644
--- a/libavformat/srtenc.c
+++ b/libavformat/srtenc.c
@@ -39,12 +39,6 @@  static int srt_write_header(AVFormatContext *avf)
 {
     SRTContext *srt = avf->priv_data;
 
-    if (avf->nb_streams != 1 ||
-        avf->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
-        av_log(avf, AV_LOG_ERROR,
-               "SRT supports only a single subtitles stream.\n");
-        return AVERROR(EINVAL);
-    }
     if (avf->streams[0]->codecpar->codec_id != AV_CODEC_ID_TEXT &&
         avf->streams[0]->codecpar->codec_id != AV_CODEC_ID_SUBRIP) {
         av_log(avf, AV_LOG_ERROR,
@@ -103,7 +97,10 @@  const FFOutputFormat ff_srt_muxer = {
     .p.mime_type      = "application/x-subrip",
     .p.extensions     = "srt",
     .p.flags          = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT,
+    .p.video_codec    = AV_CODEC_ID_NONE,
+    .p.audio_codec    = AV_CODEC_ID_NONE,
     .p.subtitle_codec = AV_CODEC_ID_SUBRIP,
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .priv_data_size = sizeof(SRTContext),
     .write_header   = srt_write_header,
     .write_packet   = srt_write_packet,
diff --git a/libavformat/supenc.c b/libavformat/supenc.c
index 4af92fefa2..9d5ca51894 100644
--- a/libavformat/supenc.c
+++ b/libavformat/supenc.c
@@ -74,12 +74,6 @@  static int sup_write_packet(AVFormatContext *s, AVPacket *pkt)
 
 static int sup_write_header(AVFormatContext *s)
 {
-    if (s->nb_streams != 1) {
-        av_log(s, AV_LOG_ERROR, "%s files have exactly one stream\n",
-               s->oformat->name);
-        return AVERROR(EINVAL);
-    }
-
     avpriv_set_pts_info(s->streams[0], 32, 1, 90000);
 
     return 0;
@@ -90,8 +84,11 @@  const FFOutputFormat ff_sup_muxer = {
     .p.long_name      = NULL_IF_CONFIG_SMALL("raw HDMV Presentation Graphic Stream subtitles"),
     .p.extensions     = "sup",
     .p.mime_type      = "application/x-pgs",
+    .p.video_codec    = AV_CODEC_ID_NONE,
+    .p.audio_codec    = AV_CODEC_ID_NONE,
     .p.subtitle_codec = AV_CODEC_ID_HDMV_PGS_SUBTITLE,
     .p.flags          = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT,
+    .flags_internal   = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_header   = sup_write_header,
     .write_packet   = sup_write_packet,
 };
diff --git a/libavformat/swfenc.c b/libavformat/swfenc.c
index f9ac69539b..d106e16d19 100644
--- a/libavformat/swfenc.c
+++ b/libavformat/swfenc.c
@@ -208,10 +208,6 @@  static int swf_write_header(AVFormatContext *s)
     for(i=0;i<s->nb_streams;i++) {
         AVCodecParameters *par = s->streams[i]->codecpar;
         if (par->codec_type == AVMEDIA_TYPE_AUDIO) {
-            if (swf->audio_par) {
-                av_log(s, AV_LOG_ERROR, "SWF muxer only supports 1 audio stream\n");
-                return AVERROR_INVALIDDATA;
-            }
             if (par->codec_id == AV_CODEC_ID_MP3) {
                 swf->audio_par = par;
                 swf->audio_fifo = av_fifo_alloc2(AUDIO_FIFO_SIZE, 1, 0);
@@ -222,10 +218,6 @@  static int swf_write_header(AVFormatContext *s)
                 return -1;
             }
         } else {
-            if (swf->video_par) {
-                av_log(s, AV_LOG_ERROR, "SWF muxer only supports 1 video stream\n");
-                return AVERROR_INVALIDDATA;
-            }
             if (ff_codec_get_tag(ff_swf_codec_tags, par->codec_id) ||
                 par->codec_id == AV_CODEC_ID_PNG ||
                 par->codec_id == AV_CODEC_ID_MJPEG) {
@@ -556,11 +548,13 @@  const FFOutputFormat ff_swf_muxer = {
     .priv_data_size    = sizeof(SWFEncContext),
     .p.audio_codec     = AV_CODEC_ID_MP3,
     .p.video_codec     = AV_CODEC_ID_FLV1,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
     .write_header      = swf_write_header,
     .write_packet      = swf_write_packet,
     .write_trailer     = swf_write_trailer,
     .deinit            = swf_deinit,
     .p.flags           = AVFMT_TS_NONSTRICT,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
 };
 #endif
 #if CONFIG_AVM2_MUXER
@@ -571,10 +565,12 @@  const FFOutputFormat ff_avm2_muxer = {
     .priv_data_size    = sizeof(SWFEncContext),
     .p.audio_codec     = AV_CODEC_ID_MP3,
     .p.video_codec     = AV_CODEC_ID_FLV1,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
     .write_header      = swf_write_header,
     .write_packet      = swf_write_packet,
     .write_trailer     = swf_write_trailer,
     .deinit            = swf_deinit,
     .p.flags           = AVFMT_TS_NONSTRICT,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
 };
 #endif
diff --git a/libavformat/ttaenc.c b/libavformat/ttaenc.c
index c7f7e065dc..661aa8fc83 100644
--- a/libavformat/ttaenc.c
+++ b/libavformat/ttaenc.c
@@ -40,13 +40,7 @@  typedef struct TTAMuxContext {
 static int tta_init(AVFormatContext *s)
 {
     TTAMuxContext *tta = s->priv_data;
-    AVCodecParameters *par;
-
-    if (s->nb_streams != 1) {
-        av_log(s, AV_LOG_ERROR, "Only one stream is supported\n");
-        return AVERROR(EINVAL);
-    }
-    par = s->streams[0]->codecpar;
+    AVCodecParameters *par = s->streams[0]->codecpar;
 
     if (par->codec_id != AV_CODEC_ID_TTA) {
         av_log(s, AV_LOG_ERROR, "Unsupported codec\n");
@@ -174,6 +168,8 @@  const FFOutputFormat ff_tta_muxer = {
     .priv_data_size    = sizeof(TTAMuxContext),
     .p.audio_codec     = AV_CODEC_ID_TTA,
     .p.video_codec     = AV_CODEC_ID_NONE,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .init              = tta_init,
     .deinit            = tta_deinit,
     .write_header      = tta_write_header,
diff --git a/libavformat/ttmlenc.c b/libavformat/ttmlenc.c
index 212994be50..0af82d5753 100644
--- a/libavformat/ttmlenc.c
+++ b/libavformat/ttmlenc.c
@@ -126,8 +126,7 @@  static int ttml_write_header(AVFormatContext *ctx)
     TTMLMuxContext *ttml_ctx = ctx->priv_data;
     ttml_ctx->document_written = 0;
 
-    if (ctx->nb_streams != 1 ||
-        ctx->streams[0]->codecpar->codec_id != AV_CODEC_ID_TTML) {
+    if (ctx->streams[0]->codecpar->codec_id != AV_CODEC_ID_TTML) {
         av_log(ctx, AV_LOG_ERROR, "Exactly one TTML stream is required!\n");
         return AVERROR(EINVAL);
     }
@@ -224,7 +223,10 @@  const FFOutputFormat ff_ttml_muxer = {
     .priv_data_size    = sizeof(TTMLMuxContext),
     .p.flags           = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |
                          AVFMT_TS_NONSTRICT,
+    .p.video_codec     = AV_CODEC_ID_NONE,
+    .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.subtitle_codec  = AV_CODEC_ID_TTML,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
     .write_header      = ttml_write_header,
     .write_packet      = ttml_write_packet,
     .write_trailer     = ttml_write_trailer,
diff --git a/libavformat/wavenc.c b/libavformat/wavenc.c
index 19daa813a0..7e853ce2fb 100644
--- a/libavformat/wavenc.c
+++ b/libavformat/wavenc.c
@@ -304,11 +304,6 @@  static int wav_write_header(AVFormatContext *s)
     AVIOContext *pb = s->pb;
     int64_t fmt;
 
-    if (s->nb_streams != 1) {
-        av_log(s, AV_LOG_ERROR, "WAVE files have exactly one stream\n");
-        return AVERROR(EINVAL);
-    }
-
     if (wav->rf64 == RF64_ALWAYS) {
         ffio_wfourcc(pb, "RF64");
         avio_wl32(pb, -1); /* RF64 chunk size: use size in ds64 */
@@ -516,6 +511,7 @@  const FFOutputFormat ff_wav_muxer = {
     .priv_data_size    = sizeof(WAVMuxContext),
     .p.audio_codec     = AV_CODEC_ID_PCM_S16LE,
     .p.video_codec     = AV_CODEC_ID_NONE,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
     .write_header      = wav_write_header,
     .write_packet      = wav_write_packet,
     .write_trailer     = wav_write_trailer,
@@ -523,22 +519,13 @@  const FFOutputFormat ff_wav_muxer = {
     .p.flags           = AVFMT_TS_NONSTRICT,
     .p.codec_tag       = ff_wav_codec_tags_list,
     .p.priv_class      = &wav_muxer_class,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
 };
 #endif /* CONFIG_WAV_MUXER */
 
 #if CONFIG_W64_MUXER
 #include "w64.h"
 
-static av_cold int w64_init(AVFormatContext *ctx)
-{
-    if (ctx->nb_streams != 1) {
-        av_log(ctx, AV_LOG_ERROR, "This muxer only supports a single stream.\n");
-        return AVERROR(EINVAL);
-    }
-
-    return 0;
-}
-
 static void start_guid(AVIOContext *pb, const uint8_t *guid, int64_t *pos)
 {
     *pos = avio_tell(pb);
@@ -624,11 +611,12 @@  const FFOutputFormat ff_w64_muxer = {
     .priv_data_size    = sizeof(WAVMuxContext),
     .p.audio_codec     = AV_CODEC_ID_PCM_S16LE,
     .p.video_codec     = AV_CODEC_ID_NONE,
-    .init              = w64_init,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
     .write_header      = w64_write_header,
     .write_packet      = wav_write_packet,
     .write_trailer     = w64_write_trailer,
     .p.flags           = AVFMT_TS_NONSTRICT,
     .p.codec_tag       = ff_wav_codec_tags_list,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
 };
 #endif /* CONFIG_W64_MUXER */
diff --git a/libavformat/webpenc.c b/libavformat/webpenc.c
index ea7a321975..85074ae315 100644
--- a/libavformat/webpenc.c
+++ b/libavformat/webpenc.c
@@ -42,10 +42,6 @@  static int webp_init(AVFormatContext *s)
 
     w->last_pkt = ffformatcontext(s)->pkt;
 
-    if (s->nb_streams != 1) {
-        av_log(s, AV_LOG_ERROR, "Only exactly 1 stream is supported\n");
-        return AVERROR(EINVAL);
-    }
     st = s->streams[0];
     if (st->codecpar->codec_id != AV_CODEC_ID_WEBP) {
         av_log(s, AV_LOG_ERROR, "Only WebP is supported\n");
@@ -230,9 +226,12 @@  const FFOutputFormat ff_webp_muxer = {
     .p.extensions   = "webp",
     .priv_data_size = sizeof(WebpContext),
     .p.video_codec  = AV_CODEC_ID_WEBP,
+    .p.audio_codec    = AV_CODEC_ID_NONE,
+    .p.subtitle_codec = AV_CODEC_ID_NONE,
     .init           = webp_init,
     .write_packet   = webp_write_packet,
     .write_trailer  = webp_write_trailer,
     .p.priv_class   = &webp_muxer_class,
     .p.flags        = AVFMT_VARIABLE_FPS,
+    .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
 };
diff --git a/libavformat/webvttenc.c b/libavformat/webvttenc.c
index 1337045325..12a4b776dc 100644
--- a/libavformat/webvttenc.c
+++ b/libavformat/webvttenc.c
@@ -50,7 +50,7 @@  static int webvtt_write_header(AVFormatContext *ctx)
     AVCodecParameters *par = ctx->streams[0]->codecpar;
     AVIOContext *pb = ctx->pb;
 
-    if (ctx->nb_streams != 1 || par->codec_id != AV_CODEC_ID_WEBVTT) {
+    if (par->codec_id != AV_CODEC_ID_WEBVTT) {
         av_log(ctx, AV_LOG_ERROR, "Exactly one WebVTT stream is needed.\n");
         return AVERROR(EINVAL);
     }
@@ -109,7 +109,10 @@  const FFOutputFormat ff_webvtt_muxer = {
     .p.extensions      = "vtt",
     .p.mime_type       = "text/vtt",
     .p.flags           = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT,
+    .p.video_codec     = AV_CODEC_ID_NONE,
+    .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.subtitle_codec  = AV_CODEC_ID_WEBVTT,
     .write_header      = webvtt_write_header,
     .write_packet      = webvtt_write_packet,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
 };
diff --git a/libavformat/westwood_audenc.c b/libavformat/westwood_audenc.c
index 46456e2ecb..2240d130b6 100644
--- a/libavformat/westwood_audenc.c
+++ b/libavformat/westwood_audenc.c
@@ -59,11 +59,6 @@  static int wsaud_write_init(AVFormatContext *ctx)
         return AVERROR(EINVAL);
     }
 
-    if (ctx->nb_streams != 1) {
-        av_log(ctx, AV_LOG_ERROR, "AUD files have exactly one stream\n");
-        return AVERROR(EINVAL);
-    }
-
     return 0;
 }
 
@@ -133,8 +128,10 @@  const FFOutputFormat ff_wsaud_muxer = {
     .priv_data_size    = sizeof(AUDMuxContext),
     .p.audio_codec     = AV_CODEC_ID_ADPCM_IMA_WS,
     .p.video_codec     = AV_CODEC_ID_NONE,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
     .init              = wsaud_write_init,
     .write_header      = wsaud_write_header,
     .write_packet      = wsaud_write_packet,
     .write_trailer     = wsaud_write_trailer,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
 };
diff --git a/libavformat/wvenc.c b/libavformat/wvenc.c
index 9d9190f252..6d5fefe1bc 100644
--- a/libavformat/wvenc.c
+++ b/libavformat/wvenc.c
@@ -33,8 +33,7 @@  typedef struct WvMuxContext {
 
 static av_cold int wv_init(AVFormatContext *ctx)
 {
-    if (ctx->nb_streams > 1 ||
-        ctx->streams[0]->codecpar->codec_id != AV_CODEC_ID_WAVPACK) {
+    if (ctx->streams[0]->codecpar->codec_id != AV_CODEC_ID_WAVPACK) {
         av_log(ctx, AV_LOG_ERROR, "This muxer only supports a single WavPack stream.\n");
         return AVERROR(EINVAL);
     }
@@ -85,8 +84,10 @@  const FFOutputFormat ff_wv_muxer = {
     .priv_data_size    = sizeof(WvMuxContext),
     .p.audio_codec     = AV_CODEC_ID_WAVPACK,
     .p.video_codec     = AV_CODEC_ID_NONE,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
     .init              = wv_init,
     .write_packet      = wv_write_packet,
     .write_trailer     = wv_write_trailer,
     .p.flags           = AVFMT_NOTIMESTAMPS,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
 };
diff --git a/libavformat/yuv4mpegenc.c b/libavformat/yuv4mpegenc.c
index 1d641eb9e4..35397cbde0 100644
--- a/libavformat/yuv4mpegenc.c
+++ b/libavformat/yuv4mpegenc.c
@@ -221,9 +221,6 @@  static int yuv4_write_packet(AVFormatContext *s, AVPacket *pkt)
 
 static int yuv4_init(AVFormatContext *s)
 {
-    if (s->nb_streams != 1)
-        return AVERROR(EIO);
-
     if (s->streams[0]->codecpar->codec_id != AV_CODEC_ID_WRAPPED_AVFRAME &&
         s->streams[0]->codecpar->codec_id != AV_CODEC_ID_RAWVIDEO) {
         av_log(s, AV_LOG_ERROR, "ERROR: Codec not supported.\n");
@@ -297,7 +294,9 @@  const FFOutputFormat ff_yuv4mpegpipe_muxer = {
     .p.extensions      = "y4m",
     .p.audio_codec     = AV_CODEC_ID_NONE,
     .p.video_codec     = AV_CODEC_ID_WRAPPED_AVFRAME,
+    .p.subtitle_codec  = AV_CODEC_ID_NONE,
     .init              = yuv4_init,
     .write_header      = yuv4_write_header,
     .write_packet      = yuv4_write_packet,
+    .flags_internal    = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
 };