PR #21658 opened by stevenliu
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21658
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21658.patch

reference document:
https://github.com/veovera/enhanced-rtmp/blob/docs/vvc/docs/enhanced/enhanced-rtmp-v2.md


>From 62ad91205e82c16fc0c4a49c6a85d1307930c196 Mon Sep 17 00:00:00 2001
From: Steven Liu <[email protected]>
Date: Wed, 4 Feb 2026 18:09:06 +0800
Subject: [PATCH 1/3] avformat/flvenc: support mux vvc in enhanced flv

---
 libavformat/flvenc.c | 38 ++++++++++++++++++++++++++++----------
 1 file changed, 28 insertions(+), 10 deletions(-)

diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c
index a0503c1799..37d0ae6528 100644
--- a/libavformat/flvenc.c
+++ b/libavformat/flvenc.c
@@ -33,6 +33,7 @@
 #include "av1.h"
 #include "vpcc.h"
 #include "hevc.h"
+#include "vvc.h"
 #include "avformat.h"
 #include "flv.h"
 #include "internal.h"
@@ -53,6 +54,7 @@ static const AVCodecTag flv_video_codec_ids[] = {
     { AV_CODEC_ID_VP6A,     FLV_CODECID_VP6A },
     { AV_CODEC_ID_H264,     FLV_CODECID_H264 },
     { AV_CODEC_ID_HEVC,     MKBETAG('h', 'v', 'c', '1') },
+    { AV_CODEC_ID_VVC,      MKBETAG('v', 'v', 'c', '1') },
     { AV_CODEC_ID_AV1,      MKBETAG('a', 'v', '0', '1') },
     { AV_CODEC_ID_VP9,      MKBETAG('v', 'p', '0', '9') },
     { AV_CODEC_ID_NONE,     0 }
@@ -520,6 +522,9 @@ static void write_codec_fourcc(AVIOContext *pb, enum 
AVCodecID codec_id)
     case AV_CODEC_ID_HEVC:
         avio_write(pb, "hvc1", 4);
         return;
+    case AV_CODEC_ID_VVC:
+        avio_write(pb, "vvc1", 4);
+        return;
     case AV_CODEC_ID_AV1:
         avio_write(pb, "av01", 4);
         return;
@@ -546,7 +551,7 @@ static void flv_write_metadata_packet(AVFormatContext *s, 
AVCodecParameters *par
         return;
 
     if (par->codec_id == AV_CODEC_ID_HEVC || par->codec_id == AV_CODEC_ID_AV1 
||
-        par->codec_id == AV_CODEC_ID_VP9) {
+        par->codec_id == AV_CODEC_ID_VP9  || par->codec_id == AV_CODEC_ID_VVC) 
{
         int flags_size = 5;
         side_data = av_packet_side_data_get(par->coded_side_data, 
par->nb_coded_side_data,
                                             AV_PKT_DATA_CONTENT_LIGHT_LEVEL);
@@ -807,6 +812,7 @@ static void flv_write_codec_header(AVFormatContext* s, 
AVCodecParameters* par, i
     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
             || par->codec_id == AV_CODEC_ID_AV1 || par->codec_id == 
AV_CODEC_ID_VP9
+            || par->codec_id == AV_CODEC_ID_VVC
             || (par->codec_id == AV_CODEC_ID_MP3 && track_idx)
             || 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) {
@@ -855,6 +861,7 @@ static void flv_write_codec_header(AVFormatContext* s, 
AVCodecParameters* par, i
             // If video stream has track_idx > 0 we need to send H.264 as 
extended video packet
             extended_flv = (par->codec_id == AV_CODEC_ID_H264 && track_idx) ||
                             par->codec_id == AV_CODEC_ID_HEVC ||
+                            par->codec_id == AV_CODEC_ID_VVC ||
                             par->codec_id == AV_CODEC_ID_AV1 ||
                             par->codec_id == AV_CODEC_ID_VP9;
 
@@ -878,6 +885,8 @@ static void flv_write_codec_header(AVFormatContext* s, 
AVCodecParameters* par, i
 
             if (par->codec_id == AV_CODEC_ID_HEVC)
                 ff_isom_write_hvcc(pb, par->extradata, par->extradata_size, 0, 
s);
+            else if (par->codec_id == AV_CODEC_ID_VVC)
+                ff_isom_write_vvcc(pb, par->extradata, par->extradata_size, 1);
             else if (par->codec_id == AV_CODEC_ID_AV1)
                 ff_isom_write_av1c(pb, par->extradata, par->extradata_size, 1);
             else if (par->codec_id == AV_CODEC_ID_VP9)
@@ -980,7 +989,8 @@ static int flv_init(struct AVFormatContext *s)
                 par->codec_id != AV_CODEC_ID_VP9 &&
                 par->codec_id != AV_CODEC_ID_AV1 &&
                 par->codec_id != AV_CODEC_ID_H264 &&
-                par->codec_id != AV_CODEC_ID_HEVC) {
+                par->codec_id != AV_CODEC_ID_HEVC &&
+                par->codec_id != AV_CODEC_ID_VVC) {
                 av_log(s, AV_LOG_ERROR, "Unsupported multi-track video 
codec.\n");
                 return AVERROR(EINVAL);
             }
@@ -1222,7 +1232,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket 
*pkt)
         flags_size = 2;
     else if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == 
AV_CODEC_ID_MPEG4 ||
              par->codec_id == AV_CODEC_ID_HEVC || par->codec_id == 
AV_CODEC_ID_AV1 ||
-             par->codec_id == AV_CODEC_ID_VP9)
+             par->codec_id == AV_CODEC_ID_VP9  || par->codec_id == 
AV_CODEC_ID_VVC)
         flags_size = 5;
     else
         flags_size = 1;
@@ -1230,13 +1240,14 @@ static int flv_write_packet(AVFormatContext *s, 
AVPacket *pkt)
     if ((par->codec_type == AVMEDIA_TYPE_VIDEO || par->codec_type == 
AVMEDIA_TYPE_AUDIO) && track_idx)
         flags_size += 2; // additional header bytes for multi-track flv
 
-    if ((par->codec_id == AV_CODEC_ID_HEVC ||
+    if ((par->codec_id == AV_CODEC_ID_HEVC || par->codec_id == AV_CODEC_ID_VVC 
||
         (par->codec_id == AV_CODEC_ID_H264 && track_idx))
             && pkt->pts != pkt->dts)
         flags_size += 3;
 
     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
+            || par->codec_id == AV_CODEC_ID_VVC
             || par->codec_id == AV_CODEC_ID_AV1 || par->codec_id == 
AV_CODEC_ID_VP9
             || par->codec_id == AV_CODEC_ID_OPUS || par->codec_id == 
AV_CODEC_ID_FLAC) {
         size_t side_size;
@@ -1260,8 +1271,8 @@ static int flv_write_packet(AVFormatContext *s, AVPacket 
*pkt)
         return AVERROR(EINVAL);
     }
     if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == 
AV_CODEC_ID_MPEG4 ||
-        par->codec_id == AV_CODEC_ID_HEVC ||  par->codec_id == AV_CODEC_ID_AV1 
||
-        par->codec_id == AV_CODEC_ID_VP9) {
+        par->codec_id == AV_CODEC_ID_HEVC || par->codec_id == AV_CODEC_ID_AV1 
||
+        par->codec_id == AV_CODEC_ID_VP9  || par->codec_id == AV_CODEC_ID_VVC) 
{
         if (pkt->pts == AV_NOPTS_VALUE) {
             av_log(s, AV_LOG_ERROR, "Packet is missing PTS\n");
             return AVERROR(EINVAL);
@@ -1308,6 +1319,10 @@ static int flv_write_packet(AVFormatContext *s, AVPacket 
*pkt)
         if (par->extradata_size > 0 && *(uint8_t*)par->extradata != 1)
             if ((ret = ff_hevc_annexb2mp4_buf(pkt->data, &data, &size, 0, 
NULL)) < 0)
                 return ret;
+    } else if (par->codec_id == AV_CODEC_ID_VVC) {
+        if (par->extradata_size > 0 && *(uint8_t*)par->extradata != 1)
+            if ((ret = ff_vvc_annexb2mp4_buf(pkt->data, &data, &size, 0, 
NULL)) < 0)
+                return ret;
     } else if (par->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 &&
                (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) {
         if (!s->streams[pkt->stream_index]->nb_frames) {
@@ -1370,15 +1385,17 @@ static int flv_write_packet(AVFormatContext *s, 
AVPacket *pkt)
     } else {
         int extended_video = (par->codec_id == AV_CODEC_ID_H264 && track_idx) 
||
                               par->codec_id == AV_CODEC_ID_HEVC ||
+                              par->codec_id == AV_CODEC_ID_VVC ||
                               par->codec_id == AV_CODEC_ID_AV1 ||
                               par->codec_id == AV_CODEC_ID_VP9;
 
         if (extended_video) {
-            int h2645 = par->codec_id == AV_CODEC_ID_H264 ||
+            int h26456 = par->codec_id == AV_CODEC_ID_H264 ||
+                        par->codec_id == AV_CODEC_ID_VVC ||
                         par->codec_id == AV_CODEC_ID_HEVC;
             int pkttype = PacketTypeCodedFrames;
-            // Optimisation for HEVC/H264: Do not send composition time if DTS 
== PTS
-            if (h2645 && pkt->pts == pkt->dts)
+            // Optimisation for VVC/HEVC/H264: Do not send composition time if 
DTS == PTS
+            if (h26456 && pkt->pts == pkt->dts)
                 pkttype = PacketTypeCodedFramesX;
 
             if (track_idx) {
@@ -1392,7 +1409,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket 
*pkt)
 
             if (track_idx)
                 avio_w8(pb, track_idx);
-            if (h2645 && pkttype == PacketTypeCodedFrames)
+            if (h26456 && pkttype == PacketTypeCodedFrames)
                 avio_wb24(pb, pkt->pts - pkt->dts);
         } else if (extended_audio) {
             if (track_idx) {
@@ -1476,6 +1493,7 @@ static int flv_check_bitstream(AVFormatContext *s, 
AVStream *st,
     if (!st->codecpar->extradata_size &&
             (st->codecpar->codec_id == AV_CODEC_ID_H264 ||
              st->codecpar->codec_id == AV_CODEC_ID_HEVC ||
+             st->codecpar->codec_id == AV_CODEC_ID_VVC ||
              st->codecpar->codec_id == AV_CODEC_ID_AV1 ||
              st->codecpar->codec_id == AV_CODEC_ID_MPEG4))
         return ff_stream_add_bitstream_filter(st, "extract_extradata", NULL);
-- 
2.52.0


>From 24ba0578ba5d0722d659afc9591076ddb7cda705 Mon Sep 17 00:00:00 2001
From: Steven Liu <[email protected]>
Date: Wed, 4 Feb 2026 18:09:51 +0800
Subject: [PATCH 2/3] avformat/flvdec: support demux vvc in enhanced flv

---
 libavformat/flvdec.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/libavformat/flvdec.c b/libavformat/flvdec.c
index c75345d882..81374d6d7f 100644
--- a/libavformat/flvdec.c
+++ b/libavformat/flvdec.c
@@ -386,6 +386,8 @@ static int flv_same_video_codec(AVCodecParameters *vpar, 
uint32_t flv_codecid)
         return 1;
 
     switch (flv_codecid) {
+    case MKBETAG('v', 'v', 'c', '1'):
+        return vpar->codec_id == AV_CODEC_ID_VVC;
     case FLV_CODECID_X_HEVC:
     case MKBETAG('h', 'v', 'c', '1'):
         return vpar->codec_id == AV_CODEC_ID_HEVC;
@@ -420,6 +422,10 @@ static int flv_set_video_codec(AVFormatContext *s, 
AVStream *vstream,
     enum AVCodecID old_codec_id = vstream->codecpar->codec_id;
 
     switch (flv_codecid) {
+    case MKBETAG('v', 'v', 'c', '1'):
+        par->codec_id = AV_CODEC_ID_VVC;
+        vstreami->need_parsing = AVSTREAM_PARSE_HEADERS;
+        break;
     case FLV_CODECID_X_HEVC:
     case MKBETAG('h', 'v', 'c', '1'):
         par->codec_id = AV_CODEC_ID_HEVC;
@@ -1731,6 +1737,7 @@ retry_duration:
             st->codecpar->codec_id == AV_CODEC_ID_H264 ||
             st->codecpar->codec_id == AV_CODEC_ID_MPEG4 ||
             st->codecpar->codec_id == AV_CODEC_ID_HEVC ||
+            st->codecpar->codec_id == AV_CODEC_ID_VVC ||
             st->codecpar->codec_id == AV_CODEC_ID_AV1 ||
             st->codecpar->codec_id == AV_CODEC_ID_VP9) {
             int type = 0;
@@ -1754,7 +1761,9 @@ retry_duration:
             }
 
             if (st->codecpar->codec_id == AV_CODEC_ID_MPEG4 ||
-                ((st->codecpar->codec_id == AV_CODEC_ID_H264 || 
st->codecpar->codec_id == AV_CODEC_ID_HEVC) &&
+                ((st->codecpar->codec_id == AV_CODEC_ID_H264 ||
+                  st->codecpar->codec_id == AV_CODEC_ID_VVC ||
+                  st->codecpar->codec_id == AV_CODEC_ID_HEVC) &&
                  (!enhanced_flv || type == PacketTypeCodedFrames))) {
                 // sign extension
                 int32_t cts = (avio_rb24(s->pb) + 0xff800000) ^ 0xff800000;
@@ -1775,6 +1784,7 @@ retry_duration:
             if (type == 0 && (!st->codecpar->extradata || 
st->codecpar->codec_id == AV_CODEC_ID_AAC ||
                 st->codecpar->codec_id == AV_CODEC_ID_OPUS || 
st->codecpar->codec_id == AV_CODEC_ID_FLAC ||
                 st->codecpar->codec_id == AV_CODEC_ID_H264 || 
st->codecpar->codec_id == AV_CODEC_ID_HEVC ||
+                st->codecpar->codec_id == AV_CODEC_ID_VVC ||
                 st->codecpar->codec_id == AV_CODEC_ID_AV1 || 
st->codecpar->codec_id == AV_CODEC_ID_VP9)) {
                 AVDictionaryEntry *t;
 
-- 
2.52.0


>From 604e89b7576a45bb3afd9df3085979126398093f Mon Sep 17 00:00:00 2001
From: Steven Liu <[email protected]>
Date: Wed, 4 Feb 2026 18:14:53 +0800
Subject: [PATCH 3/3] avformat/rtmpproto: add vvc1 string into enhanced_codecs
 list

---
 libavformat/rtmpproto.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c
index b029c57621..d4c9047266 100644
--- a/libavformat/rtmpproto.c
+++ b/libavformat/rtmpproto.c
@@ -362,7 +362,7 @@ static int gen_connect(URLContext *s, RTMPContext *rt)
         // check the string, fourcc + ',' + ...  + end fourcc correct length 
should be (4+1)*n+4
         if ((fourcc_str_len + 1) % 5 != 0) {
             av_log(s, AV_LOG_ERROR, "Malformed rtmp_enhanched_codecs, "
-                   "should be of the form hvc1[,av01][,vp09][,...]\n");
+                   "should be of the form hvc1[,av01][,vp09][,vvc1][,...]\n");
             ff_rtmp_packet_destroy(&pkt);
             return AVERROR(EINVAL);
         }
@@ -379,6 +379,7 @@ static int gen_connect(URLContext *s, RTMPContext *rt)
                 !strncmp(fourcc_data, "ec-3", 4) ||
                 !strncmp(fourcc_data, "fLaC", 4) ||
                 !strncmp(fourcc_data, "hvc1", 4) ||
+                !strncmp(fourcc_data, "vvc1", 4) ||
                 !strncmp(fourcc_data, ".mp3", 4) ||
                 !strncmp(fourcc_data, "mp4a", 4) ||
                 !strncmp(fourcc_data, "Opus", 4) ||
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to