1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2025-08-10 06:10:52 +02:00

avformat/flvenc: add support for writing multi track audio

This commit is contained in:
Timo Rothenpieler
2024-05-18 22:38:42 +02:00
parent 67b5fb4b59
commit bbf1f3cf6f

View File

@@ -132,7 +132,7 @@ typedef struct FLVContext {
int flags; int flags;
int64_t *last_ts; int64_t *last_ts;
int *metadata_pkt_written; int *metadata_pkt_written;
int *video_track_idx_map; int *track_idx_map;
} FLVContext; } FLVContext;
static int get_audio_flags(AVFormatContext *s, AVCodecParameters *par) static int get_audio_flags(AVFormatContext *s, AVCodecParameters *par)
@@ -765,19 +765,33 @@ static int flv_get_multichannel_body_size(AVCodecParameters* par)
return res; return res;
} }
static void flv_write_multichannel_header(AVFormatContext* s, AVCodecParameters* par, int64_t ts) static void flv_write_multichannel_header(AVFormatContext* s, AVCodecParameters* par, int64_t ts, int stream_index)
{ {
AVIOContext *pb = s->pb; AVIOContext *pb = s->pb;
FLVContext *flv = s->priv_data;
int track_idx = flv->track_idx_map[stream_index];
int data_size = flv_get_multichannel_body_size(par); int data_size = flv_get_multichannel_body_size(par);
if (track_idx)
data_size += 2;
avio_w8(pb, FLV_TAG_TYPE_AUDIO); avio_w8(pb, FLV_TAG_TYPE_AUDIO);
avio_wb24(pb, data_size + 5); // size avio_wb24(pb, data_size + 5); // size
put_timestamp(pb, ts); put_timestamp(pb, ts);
avio_wb24(pb, 0); // streamid avio_wb24(pb, 0); // streamid
if (track_idx) {
avio_w8(pb, FLV_CODECID_EX_HEADER | AudioPacketTypeMultitrack);
avio_w8(pb, MultitrackTypeOneTrack | AudioPacketTypeMultichannelConfig);
} else {
avio_w8(pb, FLV_CODECID_EX_HEADER | AudioPacketTypeMultichannelConfig); avio_w8(pb, FLV_CODECID_EX_HEADER | AudioPacketTypeMultichannelConfig);
}
write_codec_fourcc(pb, par->codec_id); write_codec_fourcc(pb, par->codec_id);
if (track_idx)
avio_w8(pb, track_idx);
flv_write_multichannel_body(s, par); flv_write_multichannel_body(s, par);
avio_wb32(pb, data_size + 5 + 11); // previous tag size avio_wb32(pb, data_size + 5 + 11); // previous tag size
@@ -787,6 +801,7 @@ static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, i
int64_t data_size; int64_t data_size;
AVIOContext *pb = s->pb; AVIOContext *pb = s->pb;
FLVContext *flv = s->priv_data; FLVContext *flv = s->priv_data;
int track_idx = flv->track_idx_map[stream_index];
int extended_flv = 0; int extended_flv = 0;
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
@@ -803,15 +818,26 @@ static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, i
avio_wb24(pb, 0); // streamid avio_wb24(pb, 0); // streamid
pos = avio_tell(pb); pos = avio_tell(pb);
if (par->codec_type == AVMEDIA_TYPE_AUDIO) { if (par->codec_type == AVMEDIA_TYPE_AUDIO) {
extended_flv = par->codec_id == AV_CODEC_ID_OPUS extended_flv = (par->codec_id == AV_CODEC_ID_AAC && track_idx)
|| (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_FLAC
|| par->codec_id == AV_CODEC_ID_AC3 || par->codec_id == AV_CODEC_ID_AC3
|| par->codec_id == AV_CODEC_ID_EAC3; || par->codec_id == AV_CODEC_ID_EAC3;
if (extended_flv) { if (extended_flv) {
if (track_idx) {
avio_w8(pb, FLV_CODECID_EX_HEADER | AudioPacketTypeMultitrack);
avio_w8(pb, MultitrackTypeOneTrack | AudioPacketTypeSequenceStart);
} else {
avio_w8(pb, FLV_CODECID_EX_HEADER | AudioPacketTypeSequenceStart); avio_w8(pb, FLV_CODECID_EX_HEADER | AudioPacketTypeSequenceStart);
}
write_codec_fourcc(pb, par->codec_id); write_codec_fourcc(pb, par->codec_id);
if (track_idx)
avio_w8(pb, track_idx);
if (par->codec_id == AV_CODEC_ID_AAC) { if (par->codec_id == AV_CODEC_ID_AAC) {
flv_write_aac_header(s, par); flv_write_aac_header(s, par);
} else if (par->codec_id == AV_CODEC_ID_OPUS || par->codec_id == AV_CODEC_ID_FLAC) { } else if (par->codec_id == AV_CODEC_ID_OPUS || par->codec_id == AV_CODEC_ID_FLAC) {
@@ -825,7 +851,6 @@ static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, i
flv_write_aac_header(s, par); flv_write_aac_header(s, par);
} }
} else { } 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 // 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) || extended_flv = (par->codec_id == AV_CODEC_ID_H264 && track_idx) ||
par->codec_id == AV_CODEC_ID_HEVC || par->codec_id == AV_CODEC_ID_HEVC ||
@@ -869,7 +894,7 @@ static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, i
if (par->codec_type == AVMEDIA_TYPE_AUDIO && (extended_flv || 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_STEREO) == 1 &&
av_channel_layout_compare(&par->ch_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_MONO) == 1))) av_channel_layout_compare(&par->ch_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_MONO) == 1)))
flv_write_multichannel_header(s, par, ts); flv_write_multichannel_header(s, par, ts, stream_index);
} }
static int flv_append_keyframe_info(AVFormatContext *s, FLVContext *flv, double ts, int64_t pos) static int flv_append_keyframe_info(AVFormatContext *s, FLVContext *flv, double ts, int64_t pos)
@@ -931,13 +956,13 @@ static int shift_data(AVFormatContext *s)
static int flv_init(struct AVFormatContext *s) static int flv_init(struct AVFormatContext *s)
{ {
int i; int i;
int video_ctr = 0; int video_ctr = 0, audio_ctr = 0;
FLVContext *flv = s->priv_data; FLVContext *flv = s->priv_data;
flv->last_ts = av_calloc(s->nb_streams, sizeof(*flv->last_ts)); flv->last_ts = av_calloc(s->nb_streams, sizeof(*flv->last_ts));
flv->metadata_pkt_written = av_calloc(s->nb_streams, sizeof(*flv->metadata_pkt_written)); flv->metadata_pkt_written = av_calloc(s->nb_streams, sizeof(*flv->metadata_pkt_written));
flv->video_track_idx_map = av_calloc(s->nb_streams, sizeof(*flv->video_track_idx_map)); flv->track_idx_map = av_calloc(s->nb_streams, sizeof(*flv->track_idx_map));
if (!flv->last_ts || !flv->metadata_pkt_written || !flv->video_track_idx_map) if (!flv->last_ts || !flv->metadata_pkt_written || !flv->track_idx_map)
return AVERROR(ENOMEM); return AVERROR(ENOMEM);
for (i = 0; i < s->nb_streams; i++) { for (i = 0; i < s->nb_streams; i++) {
@@ -949,7 +974,7 @@ static int flv_init(struct AVFormatContext *s)
s->streams[i]->avg_frame_rate.num) { s->streams[i]->avg_frame_rate.num) {
flv->framerate = av_q2d(s->streams[i]->avg_frame_rate); flv->framerate = av_q2d(s->streams[i]->avg_frame_rate);
} }
flv->video_track_idx_map[i] = video_ctr++; flv->track_idx_map[i] = video_ctr++;
if (flv->video_par && flv->flags & FLV_ADD_KEYFRAME_INDEX) { if (flv->video_par && flv->flags & FLV_ADD_KEYFRAME_INDEX) {
av_log(s, AV_LOG_ERROR, av_log(s, AV_LOG_ERROR,
"at most one video stream is supported in flv with keyframe index\n"); "at most one video stream is supported in flv with keyframe index\n");
@@ -980,11 +1005,21 @@ static int flv_init(struct AVFormatContext *s)
} }
break; break;
case AVMEDIA_TYPE_AUDIO: case AVMEDIA_TYPE_AUDIO:
if (flv->audio_par) { if (audio_ctr &&
av_log(s, AV_LOG_ERROR, par->codec_id != AV_CODEC_ID_AAC &&
"at most one audio stream is supported in flv\n"); par->codec_id != AV_CODEC_ID_MP3 &&
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) {
av_log(s, AV_LOG_ERROR, "Unsupported multi-track codec.\n");
return AVERROR(EINVAL); return AVERROR(EINVAL);
} }
flv->track_idx_map[i] = audio_ctr++;
if (flv->audio_par)
av_log(s, AV_LOG_WARNING,
"more than one audio stream is not supported by most flv demuxers.\n");
else
flv->audio_par = par; flv->audio_par = par;
if (get_audio_flags(s, par) < 0) if (get_audio_flags(s, par) < 0)
return unsupported_codec(s, "Audio", par->codec_id); return unsupported_codec(s, "Audio", par->codec_id);
@@ -1157,9 +1192,11 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
uint8_t frametype = pkt->flags & AV_PKT_FLAG_KEY ? FLV_FRAME_KEY : FLV_FRAME_INTER; uint8_t frametype = pkt->flags & AV_PKT_FLAG_KEY ? FLV_FRAME_KEY : FLV_FRAME_INTER;
int flags = -1, flags_size, ret = 0; int flags = -1, flags_size, ret = 0;
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->track_idx_map[pkt->stream_index];
int extended_audio = par->codec_id == AV_CODEC_ID_OPUS int extended_audio = (par->codec_id == AV_CODEC_ID_AAC && track_idx)
|| (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_FLAC
|| par->codec_id == AV_CODEC_ID_AC3 || par->codec_id == AV_CODEC_ID_AC3
|| par->codec_id == AV_CODEC_ID_EAC3; || par->codec_id == AV_CODEC_ID_EAC3;
@@ -1176,8 +1213,8 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
else else
flags_size = 1; flags_size = 1;
if (par->codec_type == AVMEDIA_TYPE_VIDEO && track_idx) if ((par->codec_type == AVMEDIA_TYPE_VIDEO || par->codec_type == AVMEDIA_TYPE_AUDIO) && track_idx)
flags_size += 2; // additional header bytes for multi-track video 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_H264 && track_idx)) (par->codec_id == AV_CODEC_ID_H264 && track_idx))
@@ -1344,8 +1381,15 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
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) { } else if (extended_audio) {
if (track_idx) {
avio_w8(pb, FLV_CODECID_EX_HEADER | AudioPacketTypeMultitrack);
avio_w8(pb, MultitrackTypeOneTrack | AudioPacketTypeCodedFrames);
} else {
avio_w8(pb, FLV_CODECID_EX_HEADER | AudioPacketTypeCodedFrames); avio_w8(pb, FLV_CODECID_EX_HEADER | AudioPacketTypeCodedFrames);
}
write_codec_fourcc(pb, par->codec_id); write_codec_fourcc(pb, par->codec_id);
if (track_idx)
avio_w8(pb, track_idx);
} else { } else {
av_assert1(flags >= 0); av_assert1(flags >= 0);
avio_w8(pb, flags); avio_w8(pb, flags);
@@ -1435,7 +1479,7 @@ static void flv_deinit(AVFormatContext *s)
av_freep(&flv->last_ts); av_freep(&flv->last_ts);
av_freep(&flv->metadata_pkt_written); av_freep(&flv->metadata_pkt_written);
av_freep(&flv->video_track_idx_map); av_freep(&flv->track_idx_map);
} }
static const AVOption options[] = { static const AVOption options[] = {