[FFmpeg-devel] [PATCH 07/14] avformat/flvenc: write enhanced rtmp multichannel info for audio with more than two channels

Timo Rothenpieler timo at rothenpieler.org
Thu Dec 12 21:55:32 EET 2024


---
 libavformat/flvenc.c | 93 ++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 85 insertions(+), 8 deletions(-)

diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c
index fbe5416353..8b16f464c8 100644
--- a/libavformat/flvenc.c
+++ b/libavformat/flvenc.c
@@ -712,10 +712,82 @@ static void flv_write_aac_header(AVFormatContext* s, AVCodecParameters* par)
     avio_write(pb, par->extradata, par->extradata_size);
 }
 
+static void flv_write_multichannel_body(AVFormatContext* s, AVCodecParameters* par)
+{
+    AVIOContext *pb = s->pb;
+
+    switch (par->ch_layout.order) {
+    case AV_CHANNEL_ORDER_NATIVE:
+        avio_w8(pb, AudioChannelOrderNative);
+        break;
+    case AV_CHANNEL_ORDER_CUSTOM:
+        avio_w8(pb, AudioChannelOrderCustom);
+        break;
+    default:
+        avio_w8(pb, AudioChannelOrderUnspecified);
+        break;
+    }
+
+    avio_w8(pb, par->ch_layout.nb_channels);
+
+    if (par->ch_layout.order == AV_CHANNEL_ORDER_NATIVE) {
+        // The first 18 entries are identical between FFmpeg and flv
+        uint32_t mask = par->ch_layout.u.mask & 0x03FFFF;
+        // The remaining 6 flv entries are in the right order, but start at AV_CHAN_LOW_FREQUENCY_2
+        mask |= (par->ch_layout.u.mask >> (AV_CHAN_LOW_FREQUENCY_2 - 18)) & 0xFC0000;
+
+        avio_wb32(pb, mask);
+    } else if (par->ch_layout.order == AV_CHANNEL_ORDER_CUSTOM) {
+        for (int i = 0; i < par->ch_layout.nb_channels; i++) {
+            enum AVChannel id = par->ch_layout.u.map[i].id;
+            if (id >= AV_CHAN_FRONT_LEFT && id <= AV_CHAN_TOP_BACK_RIGHT) {
+                avio_w8(pb, id - AV_CHAN_FRONT_LEFT + 0);
+            } else if (id >= AV_CHAN_LOW_FREQUENCY_2 && id <= AV_CHAN_BOTTOM_FRONT_RIGHT) {
+                avio_w8(pb, id - AV_CHAN_LOW_FREQUENCY_2 + 18);
+            } else if (id == AV_CHAN_UNUSED) {
+                avio_w8(pb, 0xFE);
+            } else {
+                avio_w8(pb, 0xFF); // unknown
+            }
+        }
+    }
+}
+
+static int flv_get_multichannel_body_size(AVCodecParameters* par)
+{
+    int res = 2;
+
+    if (par->ch_layout.order == AV_CHANNEL_ORDER_NATIVE)
+        res += 4;
+    else if (par->ch_layout.order == AV_CHANNEL_ORDER_CUSTOM)
+        res += par->ch_layout.nb_channels;
+
+    return res;
+}
+
+static void flv_write_multichannel_header(AVFormatContext* s, AVCodecParameters* par, int64_t ts)
+{
+    AVIOContext *pb = s->pb;
+    int data_size = flv_get_multichannel_body_size(par);
+
+    avio_w8(pb, FLV_TAG_TYPE_AUDIO);
+    avio_wb24(pb, data_size + 5); // size
+    put_timestamp(pb, ts);
+    avio_wb24(pb, 0); // streamid
+
+    avio_w8(pb, FLV_CODECID_EX_HEADER | AudioPacketTypeMultichannelConfig);
+    write_codec_fourcc(pb, par->codec_id);
+
+    flv_write_multichannel_body(s, par);
+
+    avio_wb32(pb, data_size + 5 + 11); // previous tag size
+}
+
 static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, int64_t ts, int stream_index) {
     int64_t data_size;
     AVIOContext *pb = s->pb;
     FLVContext *flv = s->priv_data;
+    int extended_flv = 0;
 
     if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264
             || par->codec_id == AV_CODEC_ID_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC
@@ -731,10 +803,10 @@ static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, i
         avio_wb24(pb, 0); // streamid
         pos = avio_tell(pb);
         if (par->codec_type == AVMEDIA_TYPE_AUDIO) {
-            int extended_flv = par->codec_id == AV_CODEC_ID_OPUS
-                                    || par->codec_id == AV_CODEC_ID_FLAC
-                                    || par->codec_id == AV_CODEC_ID_AC3
-                                    || par->codec_id == AV_CODEC_ID_EAC3;
+            extended_flv = par->codec_id == AV_CODEC_ID_OPUS
+                                || par->codec_id == AV_CODEC_ID_FLAC
+                                || par->codec_id == AV_CODEC_ID_AC3
+                                || par->codec_id == AV_CODEC_ID_EAC3;
 
             if (extended_flv) {
                 avio_w8(pb, FLV_CODECID_EX_HEADER | AudioPacketTypeSequenceStart);
@@ -755,10 +827,10 @@ static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, i
         } else {
             int track_idx = flv->video_track_idx_map[stream_index];
             // If video stream has track_idx > 0 we need to send H.264 as extended video packet
-            int extended_flv = (par->codec_id == AV_CODEC_ID_H264 && track_idx)
-                                    || par->codec_id == AV_CODEC_ID_HEVC
-                                    || par->codec_id == AV_CODEC_ID_AV1
-                                    || par->codec_id == AV_CODEC_ID_VP9;
+            extended_flv = (par->codec_id == AV_CODEC_ID_H264 && track_idx)
+                                || par->codec_id == AV_CODEC_ID_HEVC
+                                || par->codec_id == AV_CODEC_ID_AV1
+                                || par->codec_id == AV_CODEC_ID_VP9;
 
             if (extended_flv) {
                 if (track_idx) {
@@ -793,6 +865,11 @@ static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, i
         avio_skip(pb, data_size + 10 - 3);
         avio_wb32(pb, data_size + 11); // previous tag size
     }
+
+    if (par->codec_type == AVMEDIA_TYPE_AUDIO && (extended_flv ||
+        (av_channel_layout_compare(&par->ch_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO) == 1 &&
+         av_channel_layout_compare(&par->ch_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_MONO) == 1)))
+        flv_write_multichannel_header(s, par, ts);
 }
 
 static int flv_append_keyframe_info(AVFormatContext *s, FLVContext *flv, double ts, int64_t pos)
-- 
2.45.2



More information about the ffmpeg-devel mailing list