package org.mp4parser.muxer.tracks; import org.mp4parser.boxes.dolby.AC3SpecificBox; import org.mp4parser.boxes.iso14496.part1.objectdescriptors.BitReaderBuffer; import org.mp4parser.boxes.iso14496.part12.CompositionTimeToSample; import org.mp4parser.boxes.iso14496.part12.SampleDependencyTypeBox; import org.mp4parser.boxes.iso14496.part12.SampleDescriptionBox; import org.mp4parser.boxes.iso14496.part12.SubSampleInformationBox; import org.mp4parser.boxes.sampleentry.AudioSampleEntry; import org.mp4parser.muxer.AbstractTrack; import org.mp4parser.muxer.DataSource; import org.mp4parser.muxer.Sample; import org.mp4parser.muxer.TrackMetaData; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; public class AC3TrackImpl extends AbstractTrack { static int[][][][] bitRateAndFrameSizeTable; static { bitRateAndFrameSizeTable = new int[19][2][3][2]; // ETSI 102 366 Table 4.13, in frmsizecod, flag, fscod, bitrate/size order. Note that all sizes are in words, and all bitrates in kbps // 48kHz bitRateAndFrameSizeTable[0][0][0][0] = 32; bitRateAndFrameSizeTable[0][1][0][0] = 32; bitRateAndFrameSizeTable[0][0][0][1] = 64; bitRateAndFrameSizeTable[0][1][0][1] = 64; bitRateAndFrameSizeTable[1][0][0][0] = 40; bitRateAndFrameSizeTable[1][1][0][0] = 40; bitRateAndFrameSizeTable[1][0][0][1] = 80; bitRateAndFrameSizeTable[1][1][0][1] = 80; bitRateAndFrameSizeTable[2][0][0][0] = 48; bitRateAndFrameSizeTable[2][1][0][0] = 48; bitRateAndFrameSizeTable[2][0][0][1] = 96; bitRateAndFrameSizeTable[2][1][0][1] = 96; bitRateAndFrameSizeTable[3][0][0][0] = 56; bitRateAndFrameSizeTable[3][1][0][0] = 56; bitRateAndFrameSizeTable[3][0][0][1] = 112; bitRateAndFrameSizeTable[3][1][0][1] = 112; bitRateAndFrameSizeTable[4][0][0][0] = 64; bitRateAndFrameSizeTable[4][1][0][0] = 64; bitRateAndFrameSizeTable[4][0][0][1] = 128; bitRateAndFrameSizeTable[4][1][0][1] = 128; bitRateAndFrameSizeTable[5][0][0][0] = 80; bitRateAndFrameSizeTable[5][1][0][0] = 80; bitRateAndFrameSizeTable[5][0][0][1] = 160; bitRateAndFrameSizeTable[5][1][0][1] = 160; bitRateAndFrameSizeTable[6][0][0][0] = 96; bitRateAndFrameSizeTable[6][1][0][0] = 96; bitRateAndFrameSizeTable[6][0][0][1] = 192; bitRateAndFrameSizeTable[6][1][0][1] = 192; bitRateAndFrameSizeTable[7][0][0][0] = 112; bitRateAndFrameSizeTable[7][1][0][0] = 112; bitRateAndFrameSizeTable[7][0][0][1] = 224; bitRateAndFrameSizeTable[7][1][0][1] = 224; bitRateAndFrameSizeTable[8][0][0][0] = 128; bitRateAndFrameSizeTable[8][1][0][0] = 128; bitRateAndFrameSizeTable[8][0][0][1] = 256; bitRateAndFrameSizeTable[8][1][0][1] = 256; bitRateAndFrameSizeTable[9][0][0][0] = 160; bitRateAndFrameSizeTable[9][1][0][0] = 160; bitRateAndFrameSizeTable[9][0][0][1] = 320; bitRateAndFrameSizeTable[9][1][0][1] = 320; bitRateAndFrameSizeTable[10][0][0][0] = 192; bitRateAndFrameSizeTable[10][1][0][0] = 192; bitRateAndFrameSizeTable[10][0][0][1] = 384; bitRateAndFrameSizeTable[10][1][0][1] = 384; bitRateAndFrameSizeTable[11][0][0][0] = 224; bitRateAndFrameSizeTable[11][1][0][0] = 224; bitRateAndFrameSizeTable[11][0][0][1] = 448; bitRateAndFrameSizeTable[11][1][0][1] = 448; bitRateAndFrameSizeTable[12][0][0][0] = 256; bitRateAndFrameSizeTable[12][1][0][0] = 256; bitRateAndFrameSizeTable[12][0][0][1] = 512; bitRateAndFrameSizeTable[12][1][0][1] = 512; bitRateAndFrameSizeTable[13][0][0][0] = 320; bitRateAndFrameSizeTable[13][1][0][0] = 320; bitRateAndFrameSizeTable[13][0][0][1] = 640; bitRateAndFrameSizeTable[13][1][0][1] = 640; bitRateAndFrameSizeTable[14][0][0][0] = 384; bitRateAndFrameSizeTable[14][1][0][0] = 384; bitRateAndFrameSizeTable[14][0][0][1] = 768; bitRateAndFrameSizeTable[14][1][0][1] = 768; bitRateAndFrameSizeTable[15][0][0][0] = 448; bitRateAndFrameSizeTable[15][1][0][0] = 448; bitRateAndFrameSizeTable[15][0][0][1] = 896; bitRateAndFrameSizeTable[15][1][0][1] = 896; bitRateAndFrameSizeTable[16][0][0][0] = 512; bitRateAndFrameSizeTable[16][1][0][0] = 512; bitRateAndFrameSizeTable[16][0][0][1] = 1024; bitRateAndFrameSizeTable[16][1][0][1] = 1024; bitRateAndFrameSizeTable[17][0][0][0] = 576; bitRateAndFrameSizeTable[17][1][0][0] = 576; bitRateAndFrameSizeTable[17][0][0][1] = 1152; bitRateAndFrameSizeTable[17][1][0][1] = 1152; bitRateAndFrameSizeTable[18][0][0][0] = 640; bitRateAndFrameSizeTable[18][1][0][0] = 640; bitRateAndFrameSizeTable[18][0][0][1] = 1280; bitRateAndFrameSizeTable[18][1][0][1] = 1280; // 44.1 kHz bitRateAndFrameSizeTable[0][0][1][0] = 32; bitRateAndFrameSizeTable[0][1][1][0] = 32; bitRateAndFrameSizeTable[0][0][1][1] = 69; bitRateAndFrameSizeTable[0][1][1][1] = 70; bitRateAndFrameSizeTable[1][0][1][0] = 40; bitRateAndFrameSizeTable[1][1][1][0] = 40; bitRateAndFrameSizeTable[1][0][1][1] = 87; bitRateAndFrameSizeTable[1][1][1][1] = 88; bitRateAndFrameSizeTable[2][0][1][0] = 48; bitRateAndFrameSizeTable[2][1][1][0] = 48; bitRateAndFrameSizeTable[2][0][1][1] = 104; bitRateAndFrameSizeTable[2][1][1][1] = 105; bitRateAndFrameSizeTable[3][0][1][0] = 56; bitRateAndFrameSizeTable[3][1][1][0] = 56; bitRateAndFrameSizeTable[3][0][1][1] = 121; bitRateAndFrameSizeTable[3][1][1][1] = 122; bitRateAndFrameSizeTable[4][0][1][0] = 64; bitRateAndFrameSizeTable[4][1][1][0] = 64; bitRateAndFrameSizeTable[4][0][1][1] = 139; bitRateAndFrameSizeTable[4][1][1][1] = 140; bitRateAndFrameSizeTable[5][0][1][0] = 80; bitRateAndFrameSizeTable[5][1][1][0] = 80; bitRateAndFrameSizeTable[5][0][1][1] = 174; bitRateAndFrameSizeTable[5][1][1][1] = 175; bitRateAndFrameSizeTable[6][0][1][0] = 96; bitRateAndFrameSizeTable[6][1][1][0] = 96; bitRateAndFrameSizeTable[6][0][1][1] = 208; bitRateAndFrameSizeTable[6][1][1][1] = 209; bitRateAndFrameSizeTable[7][0][1][0] = 112; bitRateAndFrameSizeTable[7][1][1][0] = 112; bitRateAndFrameSizeTable[7][0][1][1] = 243; bitRateAndFrameSizeTable[7][1][1][1] = 244; bitRateAndFrameSizeTable[8][0][1][0] = 128; bitRateAndFrameSizeTable[8][1][1][0] = 128; bitRateAndFrameSizeTable[8][0][1][1] = 278; bitRateAndFrameSizeTable[8][1][1][1] = 279; bitRateAndFrameSizeTable[9][0][1][0] = 160; bitRateAndFrameSizeTable[9][1][1][0] = 160; bitRateAndFrameSizeTable[9][0][1][1] = 348; bitRateAndFrameSizeTable[9][1][1][1] = 349; bitRateAndFrameSizeTable[10][0][1][0] = 192; bitRateAndFrameSizeTable[10][1][1][0] = 192; bitRateAndFrameSizeTable[10][0][1][1] = 417; bitRateAndFrameSizeTable[10][1][1][1] = 418; bitRateAndFrameSizeTable[11][0][1][0] = 224; bitRateAndFrameSizeTable[11][1][1][0] = 224; bitRateAndFrameSizeTable[11][0][1][1] = 487; bitRateAndFrameSizeTable[11][1][1][1] = 488; bitRateAndFrameSizeTable[12][0][1][0] = 256; bitRateAndFrameSizeTable[12][1][1][0] = 256; bitRateAndFrameSizeTable[12][0][1][1] = 557; bitRateAndFrameSizeTable[12][1][1][1] = 558; bitRateAndFrameSizeTable[13][0][1][0] = 320; bitRateAndFrameSizeTable[13][1][1][0] = 320; bitRateAndFrameSizeTable[13][0][1][1] = 696; bitRateAndFrameSizeTable[13][1][1][1] = 697; bitRateAndFrameSizeTable[14][0][1][0] = 384; bitRateAndFrameSizeTable[14][1][1][0] = 384; bitRateAndFrameSizeTable[14][0][1][1] = 835; bitRateAndFrameSizeTable[14][1][1][1] = 836; bitRateAndFrameSizeTable[15][0][1][0] = 448; bitRateAndFrameSizeTable[15][1][1][0] = 448; bitRateAndFrameSizeTable[15][0][1][1] = 975; bitRateAndFrameSizeTable[15][1][1][1] = 975; bitRateAndFrameSizeTable[16][0][1][0] = 512; bitRateAndFrameSizeTable[16][1][1][0] = 512; bitRateAndFrameSizeTable[16][0][1][1] = 1114; bitRateAndFrameSizeTable[16][1][1][1] = 1115; bitRateAndFrameSizeTable[17][0][1][0] = 576; bitRateAndFrameSizeTable[17][1][1][0] = 576; bitRateAndFrameSizeTable[17][0][1][1] = 1253; bitRateAndFrameSizeTable[17][1][1][1] = 1254; bitRateAndFrameSizeTable[18][0][1][0] = 640; bitRateAndFrameSizeTable[18][1][1][0] = 640; bitRateAndFrameSizeTable[18][0][1][1] = 1393; bitRateAndFrameSizeTable[18][1][1][1] = 1394; // 32kHz bitRateAndFrameSizeTable[0][0][2][0] = 32; bitRateAndFrameSizeTable[0][1][2][0] = 32; bitRateAndFrameSizeTable[0][0][2][1] = 96; bitRateAndFrameSizeTable[0][1][2][1] = 96; bitRateAndFrameSizeTable[1][0][2][0] = 40; bitRateAndFrameSizeTable[1][1][2][0] = 40; bitRateAndFrameSizeTable[1][0][2][1] = 120; bitRateAndFrameSizeTable[1][1][2][1] = 120; bitRateAndFrameSizeTable[2][0][2][0] = 48; bitRateAndFrameSizeTable[2][1][2][0] = 48; bitRateAndFrameSizeTable[2][0][2][1] = 144; bitRateAndFrameSizeTable[2][1][2][1] = 144; bitRateAndFrameSizeTable[3][0][2][0] = 56; bitRateAndFrameSizeTable[3][1][2][0] = 56; bitRateAndFrameSizeTable[3][0][2][1] = 168; bitRateAndFrameSizeTable[3][1][2][1] = 168; bitRateAndFrameSizeTable[4][0][2][0] = 64; bitRateAndFrameSizeTable[4][1][2][0] = 64; bitRateAndFrameSizeTable[4][0][2][1] = 192; bitRateAndFrameSizeTable[4][1][2][1] = 192; bitRateAndFrameSizeTable[5][0][2][0] = 80; bitRateAndFrameSizeTable[5][1][2][0] = 80; bitRateAndFrameSizeTable[5][0][2][1] = 240; bitRateAndFrameSizeTable[5][1][2][1] = 240; bitRateAndFrameSizeTable[6][0][2][0] = 96; bitRateAndFrameSizeTable[6][1][2][0] = 96; bitRateAndFrameSizeTable[6][0][2][1] = 288; bitRateAndFrameSizeTable[6][1][2][1] = 288; bitRateAndFrameSizeTable[7][0][2][0] = 112; bitRateAndFrameSizeTable[7][1][2][0] = 112; bitRateAndFrameSizeTable[7][0][2][1] = 336; bitRateAndFrameSizeTable[7][1][2][1] = 336; bitRateAndFrameSizeTable[8][0][2][0] = 128; bitRateAndFrameSizeTable[8][1][2][0] = 128; bitRateAndFrameSizeTable[8][0][2][1] = 384; bitRateAndFrameSizeTable[8][1][2][1] = 384; bitRateAndFrameSizeTable[9][0][2][0] = 160; bitRateAndFrameSizeTable[9][1][2][0] = 160; bitRateAndFrameSizeTable[9][0][2][1] = 480; bitRateAndFrameSizeTable[9][1][2][1] = 480; bitRateAndFrameSizeTable[10][0][2][0] = 192; bitRateAndFrameSizeTable[10][1][2][0] = 192; bitRateAndFrameSizeTable[10][0][2][1] = 576; bitRateAndFrameSizeTable[10][1][2][1] = 576; bitRateAndFrameSizeTable[11][0][2][0] = 224; bitRateAndFrameSizeTable[11][1][2][0] = 224; bitRateAndFrameSizeTable[11][0][2][1] = 672; bitRateAndFrameSizeTable[11][1][2][1] = 672; bitRateAndFrameSizeTable[12][0][2][0] = 256; bitRateAndFrameSizeTable[12][1][2][0] = 256; bitRateAndFrameSizeTable[12][0][2][1] = 768; bitRateAndFrameSizeTable[12][1][2][1] = 768; bitRateAndFrameSizeTable[13][0][2][0] = 320; bitRateAndFrameSizeTable[13][1][2][0] = 320; bitRateAndFrameSizeTable[13][0][2][1] = 960; bitRateAndFrameSizeTable[13][1][2][1] = 960; bitRateAndFrameSizeTable[14][0][2][0] = 384; bitRateAndFrameSizeTable[14][1][2][0] = 384; bitRateAndFrameSizeTable[14][0][2][1] = 1152; bitRateAndFrameSizeTable[14][1][2][1] = 1152; bitRateAndFrameSizeTable[15][0][2][0] = 448; bitRateAndFrameSizeTable[15][1][2][0] = 448; bitRateAndFrameSizeTable[15][0][2][1] = 1344; bitRateAndFrameSizeTable[15][1][2][1] = 1344; bitRateAndFrameSizeTable[16][0][2][0] = 512; bitRateAndFrameSizeTable[16][1][2][0] = 512; bitRateAndFrameSizeTable[16][0][2][1] = 1536; bitRateAndFrameSizeTable[16][1][2][1] = 1536; bitRateAndFrameSizeTable[17][0][2][0] = 576; bitRateAndFrameSizeTable[17][1][2][0] = 576; bitRateAndFrameSizeTable[17][0][2][1] = 1728; bitRateAndFrameSizeTable[17][1][2][1] = 1728; bitRateAndFrameSizeTable[18][0][2][0] = 640; bitRateAndFrameSizeTable[18][1][2][0] = 640; bitRateAndFrameSizeTable[18][0][2][1] = 1920; bitRateAndFrameSizeTable[18][1][2][1] = 1920; } private final DataSource dataSource; private List<Sample> samples; private long[] duration; private TrackMetaData trackMetaData = new TrackMetaData(); private SampleDescriptionBox sampleDescriptionBox; public AC3TrackImpl(DataSource dataSource) throws IOException { this(dataSource, "eng"); } public AC3TrackImpl(DataSource dataSource, String lang) throws IOException { super(dataSource.toString()); this.dataSource = dataSource; this.trackMetaData.setLanguage(lang); samples = readSamples(); sampleDescriptionBox = new SampleDescriptionBox(); AudioSampleEntry ase = createAudioSampleEntry(); sampleDescriptionBox.addBox(ase); trackMetaData.setCreationTime(new Date()); trackMetaData.setModificationTime(new Date()); trackMetaData.setLanguage(lang); trackMetaData.setTimescale(ase.getSampleRate()); // Audio tracks always use samplerate as timescale trackMetaData.setVolume(1); } public void close() throws IOException { dataSource.close(); } public List<Sample> getSamples() { return samples; } public SampleDescriptionBox getSampleDescriptionBox() { return sampleDescriptionBox; } public synchronized long[] getSampleDurations() { return duration; } public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() { return null; } public long[] getSyncSamples() { return null; } public List<SampleDependencyTypeBox.Entry> getSampleDependencies() { return null; } public TrackMetaData getTrackMetaData() { return trackMetaData; } public String getHandler() { return "soun"; } public SubSampleInformationBox getSubsampleInformationBox() { return null; } private AudioSampleEntry createAudioSampleEntry() throws IOException { ByteBuffer bb = samples.get(0).asByteBuffer(); BitReaderBuffer brb = new BitReaderBuffer(bb); int syncword = brb.readBits(16); if (syncword != 0xb77) { throw new RuntimeException("Stream doesn't seem to be AC3"); } brb.readBits(16); // CRC-1 int fscod = brb.readBits(2); int samplerate; switch (fscod) { case 0: samplerate = 48000; break; case 1: samplerate = 44100; break; case 2: samplerate = 32000; break; default: throw new RuntimeException("Unsupported Sample Rate"); } int frmsizecod = brb.readBits(6); int bsid = brb.readBits(5); int bsmod = brb.readBits(3); int acmod = brb.readBits(3); if (bsid == 16) { throw new RuntimeException("You cannot read E-AC-3 track with AC3TrackImpl.class - user EC3TrackImpl.class"); } if (bsid == 9) { samplerate /= 2; } else if (bsid != 8 && bsid != 6) { throw new RuntimeException("Unsupported bsid"); } if ((acmod != 1) && ((acmod & 1) == 1)) { brb.readBits(2); } if (0 != (acmod & 4)) { brb.readBits(2); } if (acmod == 2) { brb.readBits(2); } int channelCount; switch (acmod) { case 0: channelCount = 2; break; case 1: channelCount = 1; break; case 2: channelCount = 2; break; case 3: channelCount = 3; break; case 4: channelCount = 3; break; case 5: channelCount = 4; break; case 6: channelCount = 4; break; case 7: channelCount = 5; break; default: throw new RuntimeException("Unsupported acmod"); } int lfeon = brb.readBits(1); if (lfeon == 1) { channelCount++; } AudioSampleEntry audioSampleEntry = new AudioSampleEntry("ac-3"); audioSampleEntry.setChannelCount(2); // According to ETSI TS 102 366 Annex F audioSampleEntry.setSampleRate(samplerate); audioSampleEntry.setDataReferenceIndex(1); audioSampleEntry.setSampleSize(16); AC3SpecificBox ac3 = new AC3SpecificBox(); ac3.setAcmod(acmod); ac3.setBitRateCode(frmsizecod >> 1); ac3.setBsid(bsid); ac3.setBsmod(bsmod); ac3.setFscod(fscod); ac3.setLfeon(lfeon); ac3.setReserved(0); audioSampleEntry.addBox(ac3); return audioSampleEntry; } private static int getFrameSize(int code, int fscod) { int frmsizecode = code >>> 1; int flag = code & 1; if (frmsizecode > 18 || flag > 1 || fscod > 2) { throw new RuntimeException("Cannot determine framesize of current sample"); } return 2 * bitRateAndFrameSizeTable[frmsizecode][flag][fscod][1]; } private List<Sample> readSamples() throws IOException { class SampleImpl implements Sample { private final long start; private final long size; private final DataSource dataSource; public SampleImpl(long start, long size, DataSource dataSource) { this.start = start; this.size = size; this.dataSource = dataSource; } public void writeTo(WritableByteChannel channel) throws IOException { dataSource.transferTo(start, size, channel); } public long getSize() { return size; } public ByteBuffer asByteBuffer() { try { return dataSource.map(start, size); } catch (IOException e) { throw new RuntimeException(e); } } } ByteBuffer header = ByteBuffer.allocate(5); List<Sample> mysamples = new ArrayList<Sample>(); while (-1 != dataSource.read(header)) { int frmsizecode = header.get(4) & 63; int fscod = header.get(4) >> 6; int frameSize = getFrameSize(frmsizecode, fscod); mysamples.add(new SampleImpl(dataSource.position() - 5, frameSize, dataSource)); dataSource.position(dataSource.position() - 5 + frameSize); header.rewind(); } duration = new long[mysamples.size()]; Arrays.fill(duration, 1536); return mysamples; } }