From 5e6fd132ff5611b8af2d3c7506f0328858fc3de1 Mon Sep 17 00:00:00 2001 From: Benoit Fouet Date: Tue, 7 Oct 2014 14:57:19 +0200 Subject: [PATCH] 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 --- libavformat/isom.c | 1 + libavformat/movenc.c | 190 +++++++++++++++++++++++++++++++++++++++++++ libavformat/movenc.h | 2 + 3 files changed, 193 insertions(+) diff --git a/libavformat/isom.c b/libavformat/isom.c index d768c32b63..150902198b 100644 --- a/libavformat/isom.c +++ b/libavformat/isom.c @@ -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 */ diff --git a/libavformat/movenc.c b/libavformat/movenc.c index bfee86633c..529b9d1b98 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -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); } diff --git a/libavformat/movenc.h b/libavformat/movenc.h index 9ce4a86b9b..4e7db7a476 100644 --- a/libavformat/movenc.h +++ b/libavformat/movenc.h @@ -142,6 +142,8 @@ typedef struct MOVTrack { int packet_entry; int slices; } vc1_info; + + void *eac3_priv; } MOVTrack; typedef struct MOVMuxContext {