You've already forked FFmpeg
mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-08-10 06:10:52 +02:00
avformat/flvdec: add support for demuxing multi-track FLV
Based on enhanced-rtmp v2 spec published by Veovera: https://veovera.github.io/enhanced-rtmp/docs/enhanced/enhanced-rtmp-v2 Signed-off-by: Dennis Sädtler <dennis@obsproject.com> Signed-off-by: Timo Rothenpieler <timo@rothenpieler.org>
This commit is contained in:
committed by
Timo Rothenpieler
parent
d8d0175d3a
commit
466a400b94
@@ -103,6 +103,7 @@ enum {
|
|||||||
FLV_CODECID_NELLYMOSER = 6 << FLV_AUDIO_CODECID_OFFSET,
|
FLV_CODECID_NELLYMOSER = 6 << FLV_AUDIO_CODECID_OFFSET,
|
||||||
FLV_CODECID_PCM_ALAW = 7 << FLV_AUDIO_CODECID_OFFSET,
|
FLV_CODECID_PCM_ALAW = 7 << FLV_AUDIO_CODECID_OFFSET,
|
||||||
FLV_CODECID_PCM_MULAW = 8 << FLV_AUDIO_CODECID_OFFSET,
|
FLV_CODECID_PCM_MULAW = 8 << FLV_AUDIO_CODECID_OFFSET,
|
||||||
|
FLV_CODECID_EX_HEADER = 9 << FLV_AUDIO_CODECID_OFFSET,
|
||||||
FLV_CODECID_AAC = 10<< FLV_AUDIO_CODECID_OFFSET,
|
FLV_CODECID_AAC = 10<< FLV_AUDIO_CODECID_OFFSET,
|
||||||
FLV_CODECID_SPEEX = 11<< FLV_AUDIO_CODECID_OFFSET,
|
FLV_CODECID_SPEEX = 11<< FLV_AUDIO_CODECID_OFFSET,
|
||||||
};
|
};
|
||||||
@@ -128,6 +129,11 @@ enum {
|
|||||||
PacketTypeMultitrack = 6,
|
PacketTypeMultitrack = 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
AudioPacketTypeSequenceStart = 0,
|
||||||
|
AudioPacketTypeCodedFrames = 1,
|
||||||
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
MultitrackTypeOneTrack = 0x00,
|
MultitrackTypeOneTrack = 0x00,
|
||||||
MultitrackTypeManyTracks = 0x10,
|
MultitrackTypeManyTracks = 0x10,
|
||||||
|
@@ -104,6 +104,10 @@ typedef struct FLVContext {
|
|||||||
|
|
||||||
FLVMetaVideoColor *metaVideoColor;
|
FLVMetaVideoColor *metaVideoColor;
|
||||||
int meta_color_info_flag;
|
int meta_color_info_flag;
|
||||||
|
|
||||||
|
uint8_t **mt_extradata;
|
||||||
|
int *mt_extradata_sz;
|
||||||
|
int mt_extradata_cnt;
|
||||||
} FLVContext;
|
} FLVContext;
|
||||||
|
|
||||||
/* AMF date type */
|
/* AMF date type */
|
||||||
@@ -186,7 +190,7 @@ static void add_keyframes_index(AVFormatContext *s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static AVStream *create_stream(AVFormatContext *s, int codec_type)
|
static AVStream *create_stream(AVFormatContext *s, int codec_type, int track_idx)
|
||||||
{
|
{
|
||||||
FFFormatContext *const si = ffformatcontext(s);
|
FFFormatContext *const si = ffformatcontext(s);
|
||||||
FLVContext *flv = s->priv_data;
|
FLVContext *flv = s->priv_data;
|
||||||
@@ -194,6 +198,11 @@ static AVStream *create_stream(AVFormatContext *s, int codec_type)
|
|||||||
if (!st)
|
if (!st)
|
||||||
return NULL;
|
return NULL;
|
||||||
st->codecpar->codec_type = codec_type;
|
st->codecpar->codec_type = codec_type;
|
||||||
|
st->id = track_idx;
|
||||||
|
avpriv_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */
|
||||||
|
if (track_idx)
|
||||||
|
return st;
|
||||||
|
|
||||||
if (s->nb_streams>=3 ||( s->nb_streams==2
|
if (s->nb_streams>=3 ||( s->nb_streams==2
|
||||||
&& s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE
|
&& s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE
|
||||||
&& s->streams[1]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE
|
&& s->streams[1]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE
|
||||||
@@ -210,8 +219,6 @@ static AVStream *create_stream(AVFormatContext *s, int codec_type)
|
|||||||
st->avg_frame_rate = flv->framerate;
|
st->avg_frame_rate = flv->framerate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
avpriv_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */
|
|
||||||
flv->last_keyframe_stream_index = s->nb_streams - 1;
|
flv->last_keyframe_stream_index = s->nb_streams - 1;
|
||||||
add_keyframes_index(s);
|
add_keyframes_index(s);
|
||||||
return st;
|
return st;
|
||||||
@@ -351,6 +358,7 @@ static int flv_same_video_codec(AVCodecParameters *vpar, uint32_t flv_codecid)
|
|||||||
case FLV_CODECID_VP6A:
|
case FLV_CODECID_VP6A:
|
||||||
return vpar->codec_id == AV_CODEC_ID_VP6A;
|
return vpar->codec_id == AV_CODEC_ID_VP6A;
|
||||||
case FLV_CODECID_H264:
|
case FLV_CODECID_H264:
|
||||||
|
case MKBETAG('a', 'v', 'c', '1'):
|
||||||
return vpar->codec_id == AV_CODEC_ID_H264;
|
return vpar->codec_id == AV_CODEC_ID_H264;
|
||||||
default:
|
default:
|
||||||
return vpar->codec_tag == flv_codecid;
|
return vpar->codec_tag == flv_codecid;
|
||||||
@@ -407,6 +415,7 @@ static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream,
|
|||||||
ret = 1; // 1 byte body size adjustment for flv_read_packet()
|
ret = 1; // 1 byte body size adjustment for flv_read_packet()
|
||||||
break;
|
break;
|
||||||
case FLV_CODECID_H264:
|
case FLV_CODECID_H264:
|
||||||
|
case MKBETAG('a', 'v', 'c', '1'):
|
||||||
par->codec_id = AV_CODEC_ID_H264;
|
par->codec_id = AV_CODEC_ID_H264;
|
||||||
vstreami->need_parsing = AVSTREAM_PARSE_HEADERS;
|
vstreami->need_parsing = AVSTREAM_PARSE_HEADERS;
|
||||||
break;
|
break;
|
||||||
@@ -676,7 +685,7 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream,
|
|||||||
} else if (!strcmp(key, "height") && vpar) {
|
} else if (!strcmp(key, "height") && vpar) {
|
||||||
vpar->height = num_val;
|
vpar->height = num_val;
|
||||||
} else if (!strcmp(key, "datastream")) {
|
} else if (!strcmp(key, "datastream")) {
|
||||||
AVStream *st = create_stream(s, AVMEDIA_TYPE_SUBTITLE);
|
AVStream *st = create_stream(s, AVMEDIA_TYPE_SUBTITLE, 0);
|
||||||
if (!st)
|
if (!st)
|
||||||
return AVERROR(ENOMEM);
|
return AVERROR(ENOMEM);
|
||||||
st->codecpar->codec_id = AV_CODEC_ID_TEXT;
|
st->codecpar->codec_id = AV_CODEC_ID_TEXT;
|
||||||
@@ -884,8 +893,11 @@ static int flv_read_close(AVFormatContext *s)
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
FLVContext *flv = s->priv_data;
|
FLVContext *flv = s->priv_data;
|
||||||
for (i=0; i<FLV_STREAM_TYPE_NB; i++)
|
for (i = 0; i < FLV_STREAM_TYPE_NB; i++)
|
||||||
av_freep(&flv->new_extradata[i]);
|
av_freep(&flv->new_extradata[i]);
|
||||||
|
for (i = 0; i < flv->mt_extradata_cnt; i++)
|
||||||
|
av_freep(&flv->mt_extradata[i]);
|
||||||
|
av_freep(&flv->mt_extradata_sz);
|
||||||
av_freep(&flv->keyframe_times);
|
av_freep(&flv->keyframe_times);
|
||||||
av_freep(&flv->keyframe_filepositions);
|
av_freep(&flv->keyframe_filepositions);
|
||||||
av_freep(&flv->metaVideoColor);
|
av_freep(&flv->metaVideoColor);
|
||||||
@@ -905,18 +917,47 @@ static int flv_get_extradata(AVFormatContext *s, AVStream *st, int size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int flv_queue_extradata(FLVContext *flv, AVIOContext *pb, int stream,
|
static int flv_queue_extradata(FLVContext *flv, AVIOContext *pb, int stream,
|
||||||
int size)
|
int size, int multitrack)
|
||||||
{
|
{
|
||||||
if (!size)
|
if (!size)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
av_free(flv->new_extradata[stream]);
|
if (!multitrack) {
|
||||||
flv->new_extradata[stream] = av_mallocz(size +
|
av_free(flv->new_extradata[stream]);
|
||||||
AV_INPUT_BUFFER_PADDING_SIZE);
|
flv->new_extradata[stream] = av_mallocz(size +
|
||||||
if (!flv->new_extradata[stream])
|
AV_INPUT_BUFFER_PADDING_SIZE);
|
||||||
return AVERROR(ENOMEM);
|
if (!flv->new_extradata[stream])
|
||||||
flv->new_extradata_size[stream] = size;
|
return AVERROR(ENOMEM);
|
||||||
avio_read(pb, flv->new_extradata[stream], size);
|
flv->new_extradata_size[stream] = size;
|
||||||
|
avio_read(pb, flv->new_extradata[stream], size);
|
||||||
|
} else {
|
||||||
|
int new_count = stream + 1;
|
||||||
|
|
||||||
|
if (flv->mt_extradata_cnt < new_count) {
|
||||||
|
flv->mt_extradata = av_realloc(flv->mt_extradata,
|
||||||
|
sizeof(*flv->mt_extradata) *
|
||||||
|
new_count);
|
||||||
|
flv->mt_extradata_sz = av_realloc(flv->mt_extradata_sz,
|
||||||
|
sizeof(*flv->mt_extradata_sz) *
|
||||||
|
new_count);
|
||||||
|
if (!flv->mt_extradata || !flv->mt_extradata_sz)
|
||||||
|
return AVERROR(ENOMEM);
|
||||||
|
// Set newly allocated pointers/sizes to 0
|
||||||
|
for (int i = flv->mt_extradata_cnt; i < new_count; i++) {
|
||||||
|
flv->mt_extradata[i] = NULL;
|
||||||
|
flv->mt_extradata_sz[i] = 0;
|
||||||
|
}
|
||||||
|
flv->mt_extradata_cnt = new_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
av_free(flv->mt_extradata[stream]);
|
||||||
|
flv->mt_extradata[stream] = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE);
|
||||||
|
if (!flv->mt_extradata[stream])
|
||||||
|
return AVERROR(ENOMEM);
|
||||||
|
flv->mt_extradata_sz[stream] = size;
|
||||||
|
avio_read(pb, flv->mt_extradata[stream], size);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1032,7 +1073,7 @@ static int flv_data_packet(AVFormatContext *s, AVPacket *pkt,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (i == s->nb_streams) {
|
if (i == s->nb_streams) {
|
||||||
st = create_stream(s, AVMEDIA_TYPE_SUBTITLE);
|
st = create_stream(s, AVMEDIA_TYPE_SUBTITLE, 0);
|
||||||
if (!st)
|
if (!st)
|
||||||
return AVERROR(ENOMEM);
|
return AVERROR(ENOMEM);
|
||||||
st->codecpar->codec_id = AV_CODEC_ID_TEXT;
|
st->codecpar->codec_id = AV_CODEC_ID_TEXT;
|
||||||
@@ -1205,6 +1246,9 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
int last = -1;
|
int last = -1;
|
||||||
int orig_size;
|
int orig_size;
|
||||||
int enhanced_flv = 0;
|
int enhanced_flv = 0;
|
||||||
|
int multitrack = 0;
|
||||||
|
int pkt_type = 0;
|
||||||
|
uint8_t track_idx = 0;
|
||||||
uint32_t video_codec_id = 0;
|
uint32_t video_codec_id = 0;
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
@@ -1258,14 +1302,33 @@ retry:
|
|||||||
* https://github.com/veovera/enhanced-rtmp/blob/main/enhanced-rtmp-v1.pdf
|
* https://github.com/veovera/enhanced-rtmp/blob/main/enhanced-rtmp-v1.pdf
|
||||||
* */
|
* */
|
||||||
enhanced_flv = (flags >> 7) & 1;
|
enhanced_flv = (flags >> 7) & 1;
|
||||||
|
pkt_type = enhanced_flv ? video_codec_id : 0;
|
||||||
size--;
|
size--;
|
||||||
|
|
||||||
|
if (pkt_type == PacketTypeMultitrack) {
|
||||||
|
uint8_t types = avio_r8(s->pb);
|
||||||
|
int multitrack_type = types >> 4;
|
||||||
|
pkt_type = types & 0xF;
|
||||||
|
|
||||||
|
if (multitrack_type != MultitrackTypeOneTrack) {
|
||||||
|
av_log(s, AV_LOG_ERROR, "Multitrack types other than MultitrackTypeOneTrack are unsupported!\n");
|
||||||
|
return AVERROR_PATCHWELCOME;
|
||||||
|
}
|
||||||
|
|
||||||
|
multitrack = 1;
|
||||||
|
size--;
|
||||||
|
}
|
||||||
|
|
||||||
if (enhanced_flv) {
|
if (enhanced_flv) {
|
||||||
video_codec_id = avio_rb32(s->pb);
|
video_codec_id = avio_rb32(s->pb);
|
||||||
size -= 4;
|
size -= 4;
|
||||||
}
|
}
|
||||||
|
if (multitrack) {
|
||||||
|
track_idx = avio_r8(s->pb);
|
||||||
|
size--;
|
||||||
|
}
|
||||||
|
|
||||||
if (enhanced_flv && stream_type == FLV_STREAM_TYPE_VIDEO && (flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_VIDEO_INFO_CMD) {
|
if (enhanced_flv && (flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_VIDEO_INFO_CMD) {
|
||||||
int pkt_type = flags & 0x0F;
|
|
||||||
if (pkt_type == PacketTypeMetadata) {
|
if (pkt_type == PacketTypeMetadata) {
|
||||||
int ret = flv_parse_video_color_info(s, st, next);
|
int ret = flv_parse_video_color_info(s, st, next);
|
||||||
av_log(s, AV_LOG_DEBUG, "enhanced flv parse metadata ret %d and skip\n", ret);
|
av_log(s, AV_LOG_DEBUG, "enhanced flv parse metadata ret %d and skip\n", ret);
|
||||||
@@ -1329,7 +1392,8 @@ skip:
|
|||||||
break;
|
break;
|
||||||
} else if (stream_type == FLV_STREAM_TYPE_VIDEO) {
|
} else if (stream_type == FLV_STREAM_TYPE_VIDEO) {
|
||||||
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
|
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
|
||||||
(s->video_codec_id || flv_same_video_codec(st->codecpar, video_codec_id)))
|
(s->video_codec_id || flv_same_video_codec(st->codecpar, video_codec_id)) &&
|
||||||
|
st->id == track_idx)
|
||||||
break;
|
break;
|
||||||
} else if (stream_type == FLV_STREAM_TYPE_SUBTITLE) {
|
} else if (stream_type == FLV_STREAM_TYPE_SUBTITLE) {
|
||||||
if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
|
if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
|
||||||
@@ -1341,7 +1405,7 @@ skip:
|
|||||||
}
|
}
|
||||||
if (i == s->nb_streams) {
|
if (i == s->nb_streams) {
|
||||||
static const enum AVMediaType stream_types[] = {AVMEDIA_TYPE_VIDEO, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_SUBTITLE, AVMEDIA_TYPE_DATA};
|
static const enum AVMediaType stream_types[] = {AVMEDIA_TYPE_VIDEO, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_SUBTITLE, AVMEDIA_TYPE_DATA};
|
||||||
st = create_stream(s, stream_types[stream_type]);
|
st = create_stream(s, stream_types[stream_type], track_idx);
|
||||||
if (!st)
|
if (!st)
|
||||||
return AVERROR(ENOMEM);
|
return AVERROR(ENOMEM);
|
||||||
}
|
}
|
||||||
@@ -1448,7 +1512,7 @@ retry_duration:
|
|||||||
st->codecpar->codec_id == AV_CODEC_ID_VP9) {
|
st->codecpar->codec_id == AV_CODEC_ID_VP9) {
|
||||||
int type = 0;
|
int type = 0;
|
||||||
if (enhanced_flv && stream_type == FLV_STREAM_TYPE_VIDEO) {
|
if (enhanced_flv && stream_type == FLV_STREAM_TYPE_VIDEO) {
|
||||||
type = flags & 0x0F;
|
type = pkt_type;
|
||||||
} else {
|
} else {
|
||||||
type = avio_r8(s->pb);
|
type = avio_r8(s->pb);
|
||||||
size--;
|
size--;
|
||||||
@@ -1464,7 +1528,8 @@ retry_duration:
|
|||||||
flv->meta_color_info_flag = 0;
|
flv->meta_color_info_flag = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
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_MPEG4 ||
|
||||||
|
(st->codecpar->codec_id == AV_CODEC_ID_H264 && (!enhanced_flv || type == PacketTypeCodedFrames)) ||
|
||||||
(st->codecpar->codec_id == AV_CODEC_ID_HEVC && type == PacketTypeCodedFrames)) {
|
(st->codecpar->codec_id == AV_CODEC_ID_HEVC && type == PacketTypeCodedFrames)) {
|
||||||
// sign extension
|
// sign extension
|
||||||
int32_t cts = (avio_rb24(s->pb) + 0xff800000) ^ 0xff800000;
|
int32_t cts = (avio_rb24(s->pb) + 0xff800000) ^ 0xff800000;
|
||||||
@@ -1487,7 +1552,7 @@ retry_duration:
|
|||||||
AVDictionaryEntry *t;
|
AVDictionaryEntry *t;
|
||||||
|
|
||||||
if (st->codecpar->extradata) {
|
if (st->codecpar->extradata) {
|
||||||
if ((ret = flv_queue_extradata(flv, s->pb, stream_type, size)) < 0)
|
if ((ret = flv_queue_extradata(flv, s->pb, multitrack ? track_idx : stream_type, size, multitrack)) < 0)
|
||||||
return ret;
|
return ret;
|
||||||
ret = FFERROR_REDO;
|
ret = FFERROR_REDO;
|
||||||
goto leave;
|
goto leave;
|
||||||
@@ -1518,7 +1583,7 @@ retry_duration:
|
|||||||
pkt->pts = pts == AV_NOPTS_VALUE ? dts : pts;
|
pkt->pts = pts == AV_NOPTS_VALUE ? dts : pts;
|
||||||
pkt->stream_index = st->index;
|
pkt->stream_index = st->index;
|
||||||
pkt->pos = pos;
|
pkt->pos = pos;
|
||||||
if (flv->new_extradata[stream_type]) {
|
if (!multitrack && flv->new_extradata[stream_type]) {
|
||||||
int ret = av_packet_add_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA,
|
int ret = av_packet_add_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA,
|
||||||
flv->new_extradata[stream_type],
|
flv->new_extradata[stream_type],
|
||||||
flv->new_extradata_size[stream_type]);
|
flv->new_extradata_size[stream_type]);
|
||||||
@@ -1526,6 +1591,16 @@ retry_duration:
|
|||||||
flv->new_extradata[stream_type] = NULL;
|
flv->new_extradata[stream_type] = NULL;
|
||||||
flv->new_extradata_size[stream_type] = 0;
|
flv->new_extradata_size[stream_type] = 0;
|
||||||
}
|
}
|
||||||
|
} else if (multitrack &&
|
||||||
|
flv->mt_extradata_cnt > track_idx &&
|
||||||
|
flv->mt_extradata[track_idx]) {
|
||||||
|
int ret = av_packet_add_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA,
|
||||||
|
flv->mt_extradata[track_idx],
|
||||||
|
flv->mt_extradata_sz[track_idx]);
|
||||||
|
if (ret >= 0) {
|
||||||
|
flv->mt_extradata[track_idx] = NULL;
|
||||||
|
flv->mt_extradata_sz[track_idx] = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (stream_type == FLV_STREAM_TYPE_AUDIO &&
|
if (stream_type == FLV_STREAM_TYPE_AUDIO &&
|
||||||
(sample_rate != flv->last_sample_rate ||
|
(sample_rate != flv->last_sample_rate ||
|
||||||
|
@@ -69,6 +69,10 @@ static const AVCodecTag flv_audio_codec_ids[] = {
|
|||||||
{ AV_CODEC_ID_PCM_MULAW, FLV_CODECID_PCM_MULAW >> FLV_AUDIO_CODECID_OFFSET },
|
{ 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_PCM_ALAW, FLV_CODECID_PCM_ALAW >> FLV_AUDIO_CODECID_OFFSET },
|
||||||
{ AV_CODEC_ID_SPEEX, FLV_CODECID_SPEEX >> FLV_AUDIO_CODECID_OFFSET },
|
{ AV_CODEC_ID_SPEEX, FLV_CODECID_SPEEX >> FLV_AUDIO_CODECID_OFFSET },
|
||||||
|
{ AV_CODEC_ID_OPUS, MKBETAG('O', 'p', 'u', 's') },
|
||||||
|
{ AV_CODEC_ID_FLAC, MKBETAG('f', 'L', 'a', 'C') },
|
||||||
|
{ AV_CODEC_ID_AC3, MKBETAG('a', 'c', '-', '3') },
|
||||||
|
{ AV_CODEC_ID_EAC3, MKBETAG('e', 'c', '-', '3') },
|
||||||
{ AV_CODEC_ID_NONE, 0 }
|
{ AV_CODEC_ID_NONE, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -139,6 +143,9 @@ static int get_audio_flags(AVFormatContext *s, AVCodecParameters *par)
|
|||||||
if (par->codec_id == AV_CODEC_ID_AAC) // specs force these parameters
|
if (par->codec_id == AV_CODEC_ID_AAC) // specs force these parameters
|
||||||
return FLV_CODECID_AAC | FLV_SAMPLERATE_44100HZ |
|
return FLV_CODECID_AAC | FLV_SAMPLERATE_44100HZ |
|
||||||
FLV_SAMPLESSIZE_16BIT | FLV_STEREO;
|
FLV_SAMPLESSIZE_16BIT | FLV_STEREO;
|
||||||
|
if (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)
|
||||||
|
return FLV_CODECID_EX_HEADER; // only needed for codec support check
|
||||||
else if (par->codec_id == AV_CODEC_ID_SPEEX) {
|
else if (par->codec_id == AV_CODEC_ID_SPEEX) {
|
||||||
if (par->sample_rate != 16000) {
|
if (par->sample_rate != 16000) {
|
||||||
av_log(s, AV_LOG_ERROR,
|
av_log(s, AV_LOG_ERROR,
|
||||||
@@ -635,6 +642,42 @@ static int unsupported_codec(AVFormatContext *s,
|
|||||||
return AVERROR(ENOSYS);
|
return AVERROR(ENOSYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void flv_write_aac_header(AVFormatContext* s, AVCodecParameters* par)
|
||||||
|
{
|
||||||
|
AVIOContext *pb = s->pb;
|
||||||
|
FLVContext *flv = s->priv_data;
|
||||||
|
|
||||||
|
if (!par->extradata_size && (flv->flags & FLV_AAC_SEQ_HEADER_DETECT)) {
|
||||||
|
PutBitContext pbc;
|
||||||
|
int samplerate_index;
|
||||||
|
int channels = par->ch_layout.nb_channels
|
||||||
|
- (par->ch_layout.nb_channels == 8 ? 1 : 0);
|
||||||
|
uint8_t data[2];
|
||||||
|
|
||||||
|
for (samplerate_index = 0; samplerate_index < 16;
|
||||||
|
samplerate_index++)
|
||||||
|
if (par->sample_rate
|
||||||
|
== ff_mpeg4audio_sample_rates[samplerate_index])
|
||||||
|
break;
|
||||||
|
|
||||||
|
init_put_bits(&pbc, data, sizeof(data));
|
||||||
|
put_bits(&pbc, 5, par->profile + 1); //profile
|
||||||
|
put_bits(&pbc, 4, samplerate_index); //sample rate index
|
||||||
|
put_bits(&pbc, 4, channels);
|
||||||
|
put_bits(&pbc, 1, 0); //frame length - 1024 samples
|
||||||
|
put_bits(&pbc, 1, 0); //does not depend on core coder
|
||||||
|
put_bits(&pbc, 1, 0); //is not extension
|
||||||
|
flush_put_bits(&pbc);
|
||||||
|
|
||||||
|
avio_w8(pb, data[0]);
|
||||||
|
avio_w8(pb, data[1]);
|
||||||
|
|
||||||
|
av_log(s, AV_LOG_WARNING, "AAC sequence header: %02x %02x.\n",
|
||||||
|
data[0], data[1]);
|
||||||
|
}
|
||||||
|
avio_write(pb, par->extradata, par->extradata_size);
|
||||||
|
}
|
||||||
|
|
||||||
static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, int64_t ts, int stream_index) {
|
static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, int64_t ts, int stream_index) {
|
||||||
int64_t data_size;
|
int64_t data_size;
|
||||||
AVIOContext *pb = s->pb;
|
AVIOContext *pb = s->pb;
|
||||||
@@ -642,7 +685,9 @@ 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
|
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_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_AV1 || par->codec_id == AV_CODEC_ID_VP9
|
||||||
|
|| 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) {
|
||||||
int64_t pos;
|
int64_t pos;
|
||||||
avio_w8(pb,
|
avio_w8(pb,
|
||||||
par->codec_type == AVMEDIA_TYPE_VIDEO ?
|
par->codec_type == AVMEDIA_TYPE_VIDEO ?
|
||||||
@@ -651,39 +696,38 @@ static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, i
|
|||||||
put_timestamp(pb, ts);
|
put_timestamp(pb, ts);
|
||||||
avio_wb24(pb, 0); // streamid
|
avio_wb24(pb, 0); // streamid
|
||||||
pos = avio_tell(pb);
|
pos = avio_tell(pb);
|
||||||
if (par->codec_id == AV_CODEC_ID_AAC) {
|
if (par->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||||
avio_w8(pb, get_audio_flags(s, par));
|
int extended_flv = par->codec_id == AV_CODEC_ID_OPUS
|
||||||
avio_w8(pb, 0); // AAC sequence header
|
|| par->codec_id == AV_CODEC_ID_FLAC
|
||||||
|
|| par->codec_id == AV_CODEC_ID_AC3
|
||||||
|
|| par->codec_id == AV_CODEC_ID_EAC3;
|
||||||
|
|
||||||
if (!par->extradata_size && (flv->flags & FLV_AAC_SEQ_HEADER_DETECT)) {
|
if (extended_flv) {
|
||||||
PutBitContext pbc;
|
avio_w8(pb, FLV_CODECID_EX_HEADER | AudioPacketTypeSequenceStart);
|
||||||
int samplerate_index;
|
|
||||||
int channels = par->ch_layout.nb_channels
|
|
||||||
- (par->ch_layout.nb_channels == 8 ? 1 : 0);
|
|
||||||
uint8_t data[2];
|
|
||||||
|
|
||||||
for (samplerate_index = 0; samplerate_index < 16;
|
if (par->codec_id == AV_CODEC_ID_AAC) {
|
||||||
samplerate_index++)
|
avio_write(pb, "mp4a", 4);
|
||||||
if (par->sample_rate
|
flv_write_aac_header(s, par);
|
||||||
== ff_mpeg4audio_sample_rates[samplerate_index])
|
} else if (par->codec_id == AV_CODEC_ID_OPUS) {
|
||||||
break;
|
avio_write(pb, "Opus", 4);
|
||||||
|
av_assert0(par->extradata_size);
|
||||||
|
avio_write(pb, par->extradata, par->extradata_size);
|
||||||
|
} else if (par->codec_id == AV_CODEC_ID_FLAC) {
|
||||||
|
avio_write(pb, "fLaC", 4);
|
||||||
|
av_assert0(par->extradata_size);
|
||||||
|
avio_write(pb, par->extradata, par->extradata_size);
|
||||||
|
} else if (par->codec_id == AV_CODEC_ID_MP3)
|
||||||
|
avio_write(pb, ".mp3", 4);
|
||||||
|
else if (par->codec_id == AV_CODEC_ID_AC3)
|
||||||
|
avio_write(pb, "ac-3", 4);
|
||||||
|
else if (par->codec_id == AV_CODEC_ID_EAC3)
|
||||||
|
avio_write(pb, "ec-3", 4);
|
||||||
|
} else if (par->codec_id == AV_CODEC_ID_AAC) {
|
||||||
|
avio_w8(pb, get_audio_flags(s, par));
|
||||||
|
avio_w8(pb, 0); // AAC sequence header
|
||||||
|
|
||||||
init_put_bits(&pbc, data, sizeof(data));
|
flv_write_aac_header(s, par);
|
||||||
put_bits(&pbc, 5, par->profile + 1); //profile
|
|
||||||
put_bits(&pbc, 4, samplerate_index); //sample rate index
|
|
||||||
put_bits(&pbc, 4, channels);
|
|
||||||
put_bits(&pbc, 1, 0); //frame length - 1024 samples
|
|
||||||
put_bits(&pbc, 1, 0); //does not depend on core coder
|
|
||||||
put_bits(&pbc, 1, 0); //is not extension
|
|
||||||
flush_put_bits(&pbc);
|
|
||||||
|
|
||||||
avio_w8(pb, data[0]);
|
|
||||||
avio_w8(pb, data[1]);
|
|
||||||
|
|
||||||
av_log(s, AV_LOG_WARNING, "AAC sequence header: %02x %02x.\n",
|
|
||||||
data[0], data[1]);
|
|
||||||
}
|
}
|
||||||
avio_write(pb, par->extradata, par->extradata_size);
|
|
||||||
} else {
|
} else {
|
||||||
int track_idx = flv->video_track_idx_map[stream_index];
|
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
|
// If video stream has track_idx > 0 we need to send H.264 as extended video packet
|
||||||
@@ -1021,13 +1065,20 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
int64_t cur_offset = avio_tell(pb);
|
int64_t cur_offset = avio_tell(pb);
|
||||||
int track_idx = flv->video_track_idx_map[pkt->stream_index];
|
int track_idx = flv->video_track_idx_map[pkt->stream_index];
|
||||||
|
|
||||||
|
int extended_audio = 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 (par->codec_type == AVMEDIA_TYPE_AUDIO && !pkt->size) {
|
if (par->codec_type == AVMEDIA_TYPE_AUDIO && !pkt->size) {
|
||||||
av_log(s, AV_LOG_WARNING, "Empty audio Packet\n");
|
av_log(s, AV_LOG_WARNING, "Empty audio Packet\n");
|
||||||
return AVERROR(EINVAL);
|
return AVERROR(EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (par->codec_id == AV_CODEC_ID_VP6F || par->codec_id == AV_CODEC_ID_VP6A ||
|
if (extended_audio)
|
||||||
par->codec_id == AV_CODEC_ID_VP6 || par->codec_id == AV_CODEC_ID_AAC)
|
flags_size = 5;
|
||||||
|
else 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;
|
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 ||
|
par->codec_id == AV_CODEC_ID_HEVC || par->codec_id == AV_CODEC_ID_AV1 ||
|
||||||
@@ -1046,7 +1097,8 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
|
|
||||||
if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264
|
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_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_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;
|
size_t side_size;
|
||||||
uint8_t *side = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &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))) {
|
if (side && side_size > 0 && (side_size != par->extradata_size || memcmp(side, par->extradata, side_size))) {
|
||||||
@@ -1178,12 +1230,12 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
avio_seek(pb, data_size + 10 - 3, SEEK_CUR);
|
avio_seek(pb, data_size + 10 - 3, SEEK_CUR);
|
||||||
avio_wb32(pb, data_size + 11);
|
avio_wb32(pb, data_size + 11);
|
||||||
} else {
|
} else {
|
||||||
int extended_flv = (par->codec_id == AV_CODEC_ID_H264 && track_idx) ||
|
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_HEVC ||
|
||||||
par->codec_id == AV_CODEC_ID_AV1 ||
|
par->codec_id == AV_CODEC_ID_AV1 ||
|
||||||
par->codec_id == AV_CODEC_ID_VP9;
|
par->codec_id == AV_CODEC_ID_VP9;
|
||||||
|
|
||||||
if (extended_flv) {
|
if (extended_video) {
|
||||||
int h2645 = par->codec_id == AV_CODEC_ID_H264 ||
|
int h2645 = par->codec_id == AV_CODEC_ID_H264 ||
|
||||||
par->codec_id == AV_CODEC_ID_HEVC;
|
par->codec_id == AV_CODEC_ID_HEVC;
|
||||||
int pkttype = PacketTypeCodedFrames;
|
int pkttype = PacketTypeCodedFrames;
|
||||||
@@ -1211,6 +1263,21 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
avio_w8(pb, track_idx);
|
avio_w8(pb, track_idx);
|
||||||
if (h2645 && pkttype == PacketTypeCodedFrames)
|
if (h2645 && pkttype == PacketTypeCodedFrames)
|
||||||
avio_wb24(pb, pkt->pts - pkt->dts);
|
avio_wb24(pb, pkt->pts - pkt->dts);
|
||||||
|
} else if (extended_audio) {
|
||||||
|
avio_w8(pb, FLV_CODECID_EX_HEADER | AudioPacketTypeCodedFrames);
|
||||||
|
|
||||||
|
if (par->codec_id == AV_CODEC_ID_AAC)
|
||||||
|
avio_write(pb, "mp4a", 4);
|
||||||
|
else if (par->codec_id == AV_CODEC_ID_OPUS)
|
||||||
|
avio_write(pb, "Opus", 4);
|
||||||
|
else if (par->codec_id == AV_CODEC_ID_FLAC)
|
||||||
|
avio_write(pb, "fLaC", 4);
|
||||||
|
else if (par->codec_id == AV_CODEC_ID_MP3)
|
||||||
|
avio_write(pb, ".mp3", 4);
|
||||||
|
else if (par->codec_id == AV_CODEC_ID_AC3)
|
||||||
|
avio_write(pb, "ac-3", 4);
|
||||||
|
else if (par->codec_id == AV_CODEC_ID_EAC3)
|
||||||
|
avio_write(pb, "ec-3", 4);
|
||||||
} else {
|
} else {
|
||||||
av_assert1(flags >= 0);
|
av_assert1(flags >= 0);
|
||||||
avio_w8(pb, flags);
|
avio_w8(pb, flags);
|
||||||
|
Reference in New Issue
Block a user