Last active
April 18, 2023 06:17
-
-
Save yzctzl/c51bdb1b186bbc050f93d811f192597b to your computer and use it in GitHub Desktop.
A patch for ffmpeg 6.0 to extend flv container to support hevc, av1 and opus
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/libavformat/flv.h b/libavformat/flv.h | |
index 3571b90..764746c 100644 | |
--- a/libavformat/flv.h | |
+++ b/libavformat/flv.h | |
@@ -99,6 +99,7 @@ enum { | |
FLV_CODECID_PCM_MULAW = 8 << FLV_AUDIO_CODECID_OFFSET, | |
FLV_CODECID_AAC = 10<< FLV_AUDIO_CODECID_OFFSET, | |
FLV_CODECID_SPEEX = 11<< FLV_AUDIO_CODECID_OFFSET, | |
+ FLV_CODECID_OPUS = 13<< FLV_AUDIO_CODECID_OFFSET, | |
}; | |
enum { | |
@@ -110,6 +111,8 @@ enum { | |
FLV_CODECID_H264 = 7, | |
FLV_CODECID_REALH263= 8, | |
FLV_CODECID_MPEG4 = 9, | |
+ FLV_CODECID_HEVC = 12, | |
+ FLV_CODECID_AV1 = 13, | |
}; | |
enum { | |
diff --git a/libavformat/flvdec.c b/libavformat/flvdec.c | |
index d83edff..d64b427 100644 | |
--- a/libavformat/flvdec.c | |
+++ b/libavformat/flvdec.c | |
@@ -237,6 +237,11 @@ static int flv_same_audio_codec(AVCodecParameters *apar, int flags) | |
case FLV_CODECID_PCM_ALAW: | |
return apar->sample_rate == 8000 && | |
apar->codec_id == AV_CODEC_ID_PCM_ALAW; | |
+ case FLV_CODECID_OPUS: | |
+ return apar->sample_rate == 48000 && | |
+ apar->channels == 2 && | |
+ apar->bits_per_coded_sample == 16 && | |
+ apar->codec_id == AV_CODEC_ID_OPUS; | |
default: | |
return apar->codec_tag == (flv_codecid >> FLV_AUDIO_CODECID_OFFSET); | |
} | |
@@ -295,6 +300,12 @@ static void flv_set_audio_codec(AVFormatContext *s, AVStream *astream, | |
apar->sample_rate = 8000; | |
apar->codec_id = AV_CODEC_ID_PCM_ALAW; | |
break; | |
+ case FLV_CODECID_OPUS: | |
+ apar->sample_rate = 48000; | |
+ apar->channels = 2; | |
+ apar->bits_per_coded_sample = 16; | |
+ apar->codec_id = AV_CODEC_ID_OPUS; | |
+ break; | |
default: | |
avpriv_request_sample(s, "Audio codec (%x)", | |
flv_codecid >> FLV_AUDIO_CODECID_OFFSET); | |
@@ -322,6 +333,10 @@ static int flv_same_video_codec(AVCodecParameters *vpar, int flags) | |
return vpar->codec_id == AV_CODEC_ID_VP6A; | |
case FLV_CODECID_H264: | |
return vpar->codec_id == AV_CODEC_ID_H264; | |
+ case FLV_CODECID_HEVC: | |
+ return vpar->codec_id == AV_CODEC_ID_HEVC; | |
+ case FLV_CODECID_AV1: | |
+ return vpar->codec_id == AV_CODEC_ID_AV1; | |
default: | |
return vpar->codec_tag == flv_codecid; | |
} | |
@@ -372,6 +387,16 @@ static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream, | |
par->codec_id = AV_CODEC_ID_MPEG4; | |
ret = 3; | |
break; | |
+ case FLV_CODECID_HEVC: | |
+ par->codec_id = AV_CODEC_ID_HEVC; | |
+ vstreami->need_parsing = AVSTREAM_PARSE_NONE; | |
+ ret = 3; // not 4, reading packet type will consume one byte | |
+ break; | |
+ case FLV_CODECID_AV1: | |
+ par->codec_id = AV_CODEC_ID_AV1; | |
+ vstreami->need_parsing = AVSTREAM_PARSE_NONE; | |
+ ret = 3; | |
+ break; | |
default: | |
avpriv_request_sample(s, "Video codec (%x)", flv_codecid); | |
par->codec_tag = flv_codecid; | |
@@ -1242,7 +1267,9 @@ retry_duration: | |
if (st->codecpar->codec_id == AV_CODEC_ID_AAC || | |
st->codecpar->codec_id == AV_CODEC_ID_H264 || | |
- st->codecpar->codec_id == AV_CODEC_ID_MPEG4) { | |
+ st->codecpar->codec_id == AV_CODEC_ID_MPEG4 || | |
+ st->codecpar->codec_id == AV_CODEC_ID_HEVC || | |
+ st->codecpar->codec_id == AV_CODEC_ID_AV1) { | |
int type = avio_r8(s->pb); | |
size--; | |
@@ -1251,7 +1278,10 @@ retry_duration: | |
goto leave; | |
} | |
- if (st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_MPEG4) { | |
+ if (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_AV1) { | |
// sign extension | |
int32_t cts = (avio_rb24(s->pb) + 0xff800000) ^ 0xff800000; | |
pts = av_sat_add64(dts, cts); | |
@@ -1267,7 +1297,8 @@ retry_duration: | |
} | |
} | |
if (type == 0 && (!st->codecpar->extradata || st->codecpar->codec_id == AV_CODEC_ID_AAC || | |
- st->codecpar->codec_id == AV_CODEC_ID_H264)) { | |
+ st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_HEVC || | |
+ st->codecpar->codec_id == AV_CODEC_ID_AV1)) { | |
AVDictionaryEntry *t; | |
if (st->codecpar->extradata) { | |
diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c | |
index 64ea554..199d986 100644 | |
--- a/libavformat/flvenc.c | |
+++ b/libavformat/flvenc.c | |
@@ -28,6 +28,8 @@ | |
#include "libavcodec/mpeg4audio.h" | |
#include "avio.h" | |
#include "avc.h" | |
+#include "hevc.h" | |
+#include "av1.h" | |
#include "avformat.h" | |
#include "flv.h" | |
#include "internal.h" | |
@@ -46,6 +48,8 @@ static const AVCodecTag flv_video_codec_ids[] = { | |
{ AV_CODEC_ID_VP6, FLV_CODECID_VP6 }, | |
{ AV_CODEC_ID_VP6A, FLV_CODECID_VP6A }, | |
{ AV_CODEC_ID_H264, FLV_CODECID_H264 }, | |
+ { AV_CODEC_ID_HEVC, FLV_CODECID_HEVC }, | |
+ { AV_CODEC_ID_AV1, FLV_CODECID_AV1 }, | |
{ AV_CODEC_ID_NONE, 0 } | |
}; | |
@@ -60,6 +64,7 @@ static const AVCodecTag flv_audio_codec_ids[] = { | |
{ AV_CODEC_ID_PCM_MULAW, FLV_CODECID_PCM_MULAW >> FLV_AUDIO_CODECID_OFFSET }, | |
{ AV_CODEC_ID_PCM_ALAW, FLV_CODECID_PCM_ALAW >> FLV_AUDIO_CODECID_OFFSET }, | |
{ AV_CODEC_ID_SPEEX, FLV_CODECID_SPEEX >> FLV_AUDIO_CODECID_OFFSET }, | |
+ { AV_CODEC_ID_OPUS, FLV_CODECID_OPUS >> FLV_AUDIO_CODECID_OFFSET }, | |
{ AV_CODEC_ID_NONE, 0 } | |
}; | |
@@ -131,6 +136,9 @@ static int get_audio_flags(AVFormatContext *s, AVCodecParameters *par) | |
if (par->codec_id == AV_CODEC_ID_AAC) // specs force these parameters | |
return FLV_CODECID_AAC | FLV_SAMPLERATE_44100HZ | | |
FLV_SAMPLESSIZE_16BIT | FLV_STEREO; | |
+ else if (par->codec_id == AV_CODEC_ID_OPUS) // specs force these parameters | |
+ return FLV_CODECID_OPUS | FLV_SAMPLERATE_44100HZ | | |
+ FLV_SAMPLESSIZE_16BIT | FLV_STEREO; | |
else if (par->codec_id == AV_CODEC_ID_SPEEX) { | |
if (par->sample_rate != 16000) { | |
av_log(s, AV_LOG_ERROR, | |
@@ -250,6 +258,30 @@ static void put_eos_tag(AVIOContext *pb, unsigned ts, enum AVCodecID codec_id) | |
avio_wb32(pb, 16); /* Size of FLV tag */ | |
} | |
+static void put_hevc_eos_tag(AVIOContext *pb, unsigned ts) | |
+{ | |
+ avio_w8(pb, FLV_TAG_TYPE_VIDEO); | |
+ avio_wb24(pb, 5); /* Tag Data Size */ | |
+ put_timestamp(pb, ts); | |
+ avio_wb24(pb, 0); /* StreamId = 0 */ | |
+ avio_w8(pb, 28); /* ub[4] FrameType = 1, ub[4] CodecId = 12 */ | |
+ avio_w8(pb, 2); /* HEVC end of sequence */ | |
+ avio_wb24(pb, 0); /* Always 0 for HEVC EOS. */ | |
+ avio_wb32(pb, 16); /* Size of FLV tag */ | |
+} | |
+ | |
+static void put_av1_eos_tag(AVIOContext *pb, unsigned ts) | |
+{ | |
+ avio_w8(pb, FLV_TAG_TYPE_VIDEO); | |
+ avio_wb24(pb, 5); /* Tag Data Size */ | |
+ put_timestamp(pb, ts); | |
+ avio_wb24(pb, 0); /* StreamId = 0 */ | |
+ avio_w8(pb, 29); /* ub[4] FrameType = 1, ub[4] CodecId = 13 */ | |
+ avio_w8(pb, 2); /* AV1 end of sequence */ | |
+ avio_wb24(pb, 0); /* Always 0 for AV1 EOS. */ | |
+ avio_wb32(pb, 16); /* Size of FLV tag */ | |
+} | |
+ | |
static void put_amf_double(AVIOContext *pb, double d) | |
{ | |
avio_w8(pb, AMF_DATA_TYPE_NUMBER); | |
@@ -492,7 +524,8 @@ static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, i | |
FLVContext *flv = s->priv_data; | |
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_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC | |
+ || par->codec_id == AV_CODEC_ID_AV1) { | |
int64_t pos; | |
avio_w8(pb, | |
par->codec_type == AVMEDIA_TYPE_VIDEO ? | |
@@ -538,7 +571,13 @@ static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, i | |
avio_w8(pb, par->codec_tag | FLV_FRAME_KEY); // flags | |
avio_w8(pb, 0); // AVC sequence header | |
avio_wb24(pb, 0); // composition time | |
- ff_isom_write_avcc(pb, par->extradata, par->extradata_size); | |
+ if (par->codec_id == AV_CODEC_ID_HEVC) { | |
+ ff_isom_write_hvcc(pb, par->extradata, par->extradata_size, 0); | |
+ } else if (par->codec_id == AV_CODEC_ID_AV1) { | |
+ ff_isom_write_av1c(pb, par->extradata, par->extradata_size, 1); | |
+ } else { | |
+ ff_isom_write_avcc(pb, par->extradata, par->extradata_size); | |
+ } | |
} | |
data_size = avio_tell(pb) - pos; | |
avio_seek(pb, -data_size - 10, SEEK_CUR); | |
@@ -787,6 +826,11 @@ end: | |
if (par->codec_type == AVMEDIA_TYPE_VIDEO && | |
(par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4)) | |
put_eos_tag(pb, sc->last_ts, par->codec_id); | |
+ else if (par->codec_id == AV_CODEC_ID_HEVC) { | |
+ put_hevc_eos_tag(pb, sc->last_ts); | |
+ } else if (par->codec_id == AV_CODEC_ID_AV1) { | |
+ put_av1_eos_tag(pb, sc->last_ts); | |
+ } | |
} | |
} | |
@@ -825,7 +869,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) | |
unsigned ts; | |
int size = pkt->size; | |
uint8_t *data = NULL; | |
- int flags = -1, flags_size, ret = 0; | |
+ int flags = -1, flags_size, ret = 0, offset = 0; | |
int64_t cur_offset = avio_tell(pb); | |
if (par->codec_type == AVMEDIA_TYPE_AUDIO && !pkt->size) { | |
@@ -836,13 +880,15 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) | |
if (par->codec_id == AV_CODEC_ID_VP6F || par->codec_id == AV_CODEC_ID_VP6A || | |
par->codec_id == AV_CODEC_ID_VP6 || par->codec_id == AV_CODEC_ID_AAC) | |
flags_size = 2; | |
- else if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4) | |
+ 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) | |
flags_size = 5; | |
else | |
flags_size = 1; | |
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_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC | |
+ || par->codec_id == AV_CODEC_ID_AV1) { | |
size_t side_size; | |
uint8_t *side = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size); | |
if (side && side_size > 0 && (side_size != par->extradata_size || memcmp(side, par->extradata, side_size))) { | |
@@ -851,6 +897,8 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) | |
return ret; | |
memcpy(par->extradata, side, side_size); | |
flv_write_codec_header(s, par, pkt->dts); | |
+ } else { | |
+ flv_write_codec_header(s, par, pkt->dts); | |
} | |
} | |
@@ -907,6 +955,14 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) | |
if (par->extradata_size > 0 && *(uint8_t*)par->extradata != 1) | |
if ((ret = ff_avc_parse_nal_units_buf(pkt->data, &data, &size)) < 0) | |
return ret; | |
+ } else if (par->codec_id == AV_CODEC_ID_HEVC) { | |
+ 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_AV1) { | |
+ if (par->extradata_size > 0 && *(uint8_t*)par->extradata != 1) | |
+ if ((ret = ff_av1_filter_obus_buf(pkt->data, &data, &size, &offset)) < 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) { | |
@@ -979,12 +1035,15 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) | |
(FFALIGN(par->height, 16) - par->height)); | |
} else if (par->codec_id == AV_CODEC_ID_AAC) | |
avio_w8(pb, 1); // AAC raw | |
- else if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4) { | |
+ 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) { | |
avio_w8(pb, 1); // AVC NALU | |
avio_wb24(pb, pkt->pts - pkt->dts); | |
} | |
- avio_write(pb, data ? data : pkt->data, size); | |
+ avio_write(pb, data ? data + offset : pkt->data, size); | |
avio_wb32(pb, size + flags_size + 11); // previous tag size | |
flv->duration = FFMAX(flv->duration, | |
@@ -1015,7 +1074,8 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) | |
} | |
} | |
fail: | |
- av_free(data); | |
+ if (pkt->data != data) | |
+ av_free(data); | |
return ret; | |
} | |
diff --git a/libavutil/parseutils.c b/libavutil/parseutils.c | |
index 94e88e0..008fc3e 100644 | |
--- a/libavutil/parseutils.c | |
+++ b/libavutil/parseutils.c | |
@@ -117,6 +117,7 @@ static const VideoSizeAbbr video_size_abbrs[] = { | |
{ "2kdci", 2048,1080 }, | |
{ "2kflat", 1998,1080 }, | |
{ "2kscope", 2048, 858 }, | |
+ { "2kqhd", 2560,1440 }, | |
{ "4k", 4096,2160 }, /* Digital Cinema System Specification */ | |
{ "4kdci", 4096,2160 }, | |
{ "4kflat", 3996,2160 }, |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment