mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
avformat/movenc: add EAC3 muxing support.
Support only one independent substream right now, and only syncframes containing 6 blocks. Fixes part of ticket #3074 Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
This commit is contained in:
parent
5402d1bce5
commit
5e6fd132ff
@ -57,6 +57,7 @@ const AVCodecTag ff_mp4_obj_type[] = {
|
||||
{ AV_CODEC_ID_VC1 , 0xA3 },
|
||||
{ AV_CODEC_ID_DIRAC , 0xA4 },
|
||||
{ AV_CODEC_ID_AC3 , 0xA5 },
|
||||
{ AV_CODEC_ID_EAC3 , 0xA6 },
|
||||
{ AV_CODEC_ID_DTS , 0xA9 }, /* mp4ra.org */
|
||||
{ AV_CODEC_ID_VORBIS , 0xDD }, /* non standard, gpac uses it */
|
||||
{ AV_CODEC_ID_DVD_SUBTITLE, 0xE0 }, /* non standard, see unsupported-embedded-subs-2.mp4 */
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "avio.h"
|
||||
#include "isom.h"
|
||||
#include "avc.h"
|
||||
#include "libavcodec/ac3_parser.h"
|
||||
#include "libavcodec/get_bits.h"
|
||||
#include "libavcodec/put_bits.h"
|
||||
#include "libavcodec/vc1_common.h"
|
||||
@ -292,6 +293,184 @@ static int mov_write_ac3_tag(AVIOContext *pb, MOVTrack *track)
|
||||
return 11;
|
||||
}
|
||||
|
||||
struct eac3_info {
|
||||
uint8_t ec3_done;
|
||||
|
||||
/* Layout of the EC3SpecificBox */
|
||||
/* maximum bitrate */
|
||||
uint16_t data_rate;
|
||||
/* number of independent substreams */
|
||||
uint8_t num_ind_sub;
|
||||
struct {
|
||||
/* sample rate code (see ff_ac3_sample_rate_tab) 2 bits */
|
||||
uint8_t fscod;
|
||||
/* bit stream identification 5 bits */
|
||||
uint8_t bsid;
|
||||
/* one bit reserved */
|
||||
/* audio service mixing (not supported yet) 1 bit */
|
||||
/* bit stream mode 3 bits */
|
||||
uint8_t bsmod;
|
||||
/* audio coding mode 3 bits */
|
||||
uint8_t acmod;
|
||||
/* sub woofer on 1 bit */
|
||||
uint8_t lfeon;
|
||||
/* 3 bits reserved */
|
||||
/* number of dependent substreams associated with this substream 4 bits */
|
||||
uint8_t num_dep_sub;
|
||||
/* channel locations of the dependent substream(s), if any, 9 bits */
|
||||
uint16_t chan_loc;
|
||||
/* if there is no dependent substream, then one bit reserved instead */
|
||||
} substream[1]; /* TODO: support 8 independent substreams */
|
||||
};
|
||||
|
||||
static int handle_eac3(MOVMuxContext *mov, AVPacket *pkt, MOVTrack *track)
|
||||
{
|
||||
GetBitContext gbc;
|
||||
AC3HeaderInfo tmp, *hdr = &tmp;
|
||||
struct eac3_info *info;
|
||||
int num_blocks;
|
||||
|
||||
if (!track->eac3_priv && !(track->eac3_priv = av_mallocz(sizeof(*info))))
|
||||
return AVERROR(ENOMEM);
|
||||
info = track->eac3_priv;
|
||||
|
||||
init_get_bits(&gbc, pkt->data, pkt->size * 8);
|
||||
if (avpriv_ac3_parse_header2(&gbc, &hdr) < 0) {
|
||||
/* drop the packets until we see a good one */
|
||||
if (!track->entry) {
|
||||
av_log(mov, AV_LOG_WARNING, "Dropping invalid packet from start of the stream\n");
|
||||
return 0;
|
||||
}
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
|
||||
info->data_rate = FFMAX(info->data_rate, hdr->bit_rate / 1000);
|
||||
num_blocks = hdr->num_blocks;
|
||||
|
||||
if (!info->ec3_done) {
|
||||
/* AC-3 substream must be the first one */
|
||||
if (hdr->bitstream_id <= 10 && hdr->substreamid != 0)
|
||||
return AVERROR(EINVAL);
|
||||
|
||||
/* this should always be the case, given that our AC-3 parser
|
||||
* concatenates dependent frames to their independent parent */
|
||||
if (hdr->frame_type == EAC3_FRAME_TYPE_INDEPENDENT) {
|
||||
/* substream ids must be incremental */
|
||||
if (hdr->substreamid > info->num_ind_sub + 1)
|
||||
return AVERROR(EINVAL);
|
||||
|
||||
if (hdr->substreamid == info->num_ind_sub + 1) {
|
||||
//info->num_ind_sub++;
|
||||
avpriv_request_sample(track->enc, "Multiple independent substreams");
|
||||
return AVERROR_PATCHWELCOME;
|
||||
} else if (hdr->substreamid < info->num_ind_sub ||
|
||||
hdr->substreamid == 0 && info->substream[0].bsid) {
|
||||
info->ec3_done = 1;
|
||||
goto concatenate;
|
||||
}
|
||||
}
|
||||
|
||||
/* fill the info needed for the "dec3" atom */
|
||||
info->substream[hdr->substreamid].fscod = hdr->sr_code;
|
||||
info->substream[hdr->substreamid].bsid = hdr->bitstream_id;
|
||||
info->substream[hdr->substreamid].bsmod = hdr->bitstream_mode;
|
||||
info->substream[hdr->substreamid].acmod = hdr->channel_mode;
|
||||
info->substream[hdr->substreamid].lfeon = hdr->lfe_on;
|
||||
|
||||
/* Parse dependent substream(s), if any */
|
||||
if (pkt->size != hdr->frame_size) {
|
||||
int cumul_size = hdr->frame_size;
|
||||
int parent = hdr->substreamid;
|
||||
|
||||
while (cumul_size != pkt->size) {
|
||||
int i;
|
||||
init_get_bits(&gbc, pkt->data + cumul_size, (pkt->size - cumul_size) * 8);
|
||||
if (avpriv_ac3_parse_header2(&gbc, &hdr) < 0)
|
||||
return AVERROR_INVALIDDATA;
|
||||
if (hdr->frame_type != EAC3_FRAME_TYPE_DEPENDENT)
|
||||
return AVERROR(EINVAL);
|
||||
cumul_size += hdr->frame_size;
|
||||
info->substream[parent].num_dep_sub++;
|
||||
|
||||
/* header is parsed up to lfeon, but custom channel map may be needed */
|
||||
/* skip bsid */
|
||||
skip_bits(&gbc, 5);
|
||||
/* skip volume control params */
|
||||
for (i = 0; i < (hdr->channel_mode ? 1 : 2); i++) {
|
||||
skip_bits(&gbc, 5); // skip dialog normalization
|
||||
if (get_bits1(&gbc)) {
|
||||
skip_bits(&gbc, 8); // skip compression gain word
|
||||
}
|
||||
}
|
||||
/* get the dependent stream channel map, if exists */
|
||||
if (get_bits1(&gbc))
|
||||
info->substream[parent].chan_loc |= (get_bits(&gbc, 16) >> 5) & 0x1f;
|
||||
else
|
||||
info->substream[parent].chan_loc |= hdr->channel_mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: concatenate syncframes to have 6 blocks per entry */
|
||||
concatenate:
|
||||
if (num_blocks != 6) {
|
||||
avpriv_request_sample(track->enc, "%d block(s) in syncframe", num_blocks);
|
||||
return AVERROR_PATCHWELCOME;
|
||||
}
|
||||
|
||||
return pkt->size;
|
||||
}
|
||||
|
||||
static int mov_write_eac3_tag(AVIOContext *pb, MOVTrack *track)
|
||||
{
|
||||
PutBitContext pbc;
|
||||
uint8_t *buf;
|
||||
struct eac3_info *info;
|
||||
int size, i;
|
||||
|
||||
if (!track->eac3_priv)
|
||||
return AVERROR(EINVAL);
|
||||
|
||||
info = track->eac3_priv;
|
||||
size = 2 + 4 * (info->num_ind_sub + 1);
|
||||
buf = av_malloc(size);
|
||||
if (!buf) {
|
||||
av_freep(&track->eac3_priv);
|
||||
return AVERROR(ENOMEM);
|
||||
}
|
||||
|
||||
init_put_bits(&pbc, buf, size);
|
||||
put_bits(&pbc, 13, info->data_rate);
|
||||
put_bits(&pbc, 3, info->num_ind_sub);
|
||||
for (i = 0; i <= info->num_ind_sub; i++) {
|
||||
put_bits(&pbc, 2, info->substream[i].fscod);
|
||||
put_bits(&pbc, 5, info->substream[i].bsid);
|
||||
put_bits(&pbc, 1, 0); /* reserved */
|
||||
put_bits(&pbc, 1, 0); /* asvc */
|
||||
put_bits(&pbc, 3, info->substream[i].bsmod);
|
||||
put_bits(&pbc, 3, info->substream[i].acmod);
|
||||
put_bits(&pbc, 1, info->substream[i].lfeon);
|
||||
put_bits(&pbc, 5, 0); /* reserved */
|
||||
put_bits(&pbc, 4, info->substream[i].num_dep_sub);
|
||||
if (!info->substream[i].num_dep_sub) {
|
||||
put_bits(&pbc, 1, 0); /* reserved */
|
||||
size--;
|
||||
} else {
|
||||
put_bits(&pbc, 9, info->substream[i].chan_loc);
|
||||
}
|
||||
}
|
||||
flush_put_bits(&pbc);
|
||||
|
||||
avio_wb32(pb, size + 8);
|
||||
ffio_wfourcc(pb, "dec3");
|
||||
avio_write(pb, buf, size);
|
||||
|
||||
av_free(buf);
|
||||
av_freep(&track->eac3_priv);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function writes extradata "as is".
|
||||
* Extradata must be formatted like a valid atom (with size and tag).
|
||||
@ -486,6 +665,8 @@ static int mov_write_wave_tag(AVIOContext *pb, MOVTrack *track)
|
||||
mov_write_amr_tag(pb, track);
|
||||
} else if (track->enc->codec_id == AV_CODEC_ID_AC3) {
|
||||
mov_write_ac3_tag(pb, track);
|
||||
} else if (track->enc->codec_id == AV_CODEC_ID_EAC3) {
|
||||
mov_write_eac3_tag(pb, track);
|
||||
} else if (track->enc->codec_id == AV_CODEC_ID_ALAC ||
|
||||
track->enc->codec_id == AV_CODEC_ID_QDM2) {
|
||||
mov_write_extradata_tag(pb, track);
|
||||
@ -756,6 +937,7 @@ static int mov_write_audio_tag(AVIOContext *pb, MOVTrack *track)
|
||||
if (track->mode == MODE_MOV &&
|
||||
(track->enc->codec_id == AV_CODEC_ID_AAC ||
|
||||
track->enc->codec_id == AV_CODEC_ID_AC3 ||
|
||||
track->enc->codec_id == AV_CODEC_ID_EAC3 ||
|
||||
track->enc->codec_id == AV_CODEC_ID_AMR_NB ||
|
||||
track->enc->codec_id == AV_CODEC_ID_ALAC ||
|
||||
track->enc->codec_id == AV_CODEC_ID_ADPCM_MS ||
|
||||
@ -770,6 +952,8 @@ static int mov_write_audio_tag(AVIOContext *pb, MOVTrack *track)
|
||||
mov_write_amr_tag(pb, track);
|
||||
else if (track->enc->codec_id == AV_CODEC_ID_AC3)
|
||||
mov_write_ac3_tag(pb, track);
|
||||
else if (track->enc->codec_id == AV_CODEC_ID_EAC3)
|
||||
mov_write_eac3_tag(pb, track);
|
||||
else if (track->enc->codec_id == AV_CODEC_ID_ALAC)
|
||||
mov_write_extradata_tag(pb, track);
|
||||
else if (track->enc->codec_id == AV_CODEC_ID_WMAPRO)
|
||||
@ -877,6 +1061,7 @@ static int mp4_get_codec_tag(AVFormatContext *s, MOVTrack *track)
|
||||
if (track->enc->codec_id == AV_CODEC_ID_H264) tag = MKTAG('a','v','c','1');
|
||||
else if (track->enc->codec_id == AV_CODEC_ID_HEVC) tag = MKTAG('h','e','v','1');
|
||||
else if (track->enc->codec_id == AV_CODEC_ID_AC3) tag = MKTAG('a','c','-','3');
|
||||
else if (track->enc->codec_id == AV_CODEC_ID_EAC3) tag = MKTAG('e','c','-','3');
|
||||
else if (track->enc->codec_id == AV_CODEC_ID_DIRAC) tag = MKTAG('d','r','a','c');
|
||||
else if (track->enc->codec_id == AV_CODEC_ID_MOV_TEXT) tag = MKTAG('t','x','3','g');
|
||||
else if (track->enc->codec_id == AV_CODEC_ID_VC1) tag = MKTAG('v','c','-','1');
|
||||
@ -3600,6 +3785,11 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
|
||||
} else {
|
||||
size = ff_hevc_annexb2mp4(pb, pkt->data, pkt->size, 0, NULL);
|
||||
}
|
||||
} else if (enc->codec_id == AV_CODEC_ID_EAC3) {
|
||||
size = handle_eac3(mov, pkt, trk);
|
||||
if (size < 0)
|
||||
return size;
|
||||
avio_write(pb, pkt->data, size);
|
||||
} else {
|
||||
avio_write(pb, pkt->data, size);
|
||||
}
|
||||
|
@ -142,6 +142,8 @@ typedef struct MOVTrack {
|
||||
int packet_entry;
|
||||
int slices;
|
||||
} vc1_info;
|
||||
|
||||
void *eac3_priv;
|
||||
} MOVTrack;
|
||||
|
||||
typedef struct MOVMuxContext {
|
||||
|
Loading…
Reference in New Issue
Block a user