Home | History | Annotate | Download | only in tracks
      1 package com.googlecode.mp4parser.authoring.tracks;
      2 
      3 import com.coremedia.iso.boxes.*;
      4 import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
      5 import com.googlecode.mp4parser.authoring.AbstractTrack;
      6 import com.googlecode.mp4parser.authoring.TrackMetaData;
      7 import com.googlecode.mp4parser.boxes.EC3SpecificBox;
      8 import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer;
      9 
     10 import java.io.BufferedInputStream;
     11 import java.io.IOException;
     12 import java.io.InputStream;
     13 import java.nio.ByteBuffer;
     14 import java.util.Date;
     15 import java.util.LinkedList;
     16 import java.util.List;
     17 
     18 /**
     19  * Created by IntelliJ IDEA.
     20  * User: magnus
     21  * Date: 2012-03-14
     22  * Time: 10:39
     23  * To change this template use File | Settings | File Templates.
     24  */
     25 public class EC3TrackImpl extends AbstractTrack {
     26     TrackMetaData trackMetaData = new TrackMetaData();
     27     SampleDescriptionBox sampleDescriptionBox;
     28 
     29     int samplerate;
     30     int bitrate;
     31     int frameSize;
     32 
     33     List<BitStreamInfo> entries = new LinkedList<BitStreamInfo>();
     34 
     35     private BufferedInputStream inputStream;
     36     private List<ByteBuffer> samples;
     37     List<TimeToSampleBox.Entry> stts = new LinkedList<TimeToSampleBox.Entry>();
     38     private String lang = "und";
     39 
     40     public EC3TrackImpl(InputStream fin, String lang) throws IOException {
     41         this.lang = lang;
     42         parse(fin);
     43     }
     44 
     45     public EC3TrackImpl(InputStream fin) throws IOException {
     46         parse(fin);
     47     }
     48 
     49     private void parse(InputStream fin) throws IOException {
     50         inputStream = new BufferedInputStream(fin);
     51 
     52         boolean done = false;
     53         inputStream.mark(10000);
     54         while (!done) {
     55             BitStreamInfo bsi = readVariables();
     56             if (bsi == null) {
     57                 throw new IOException();
     58             }
     59             for (BitStreamInfo entry : entries) {
     60                 if (bsi.strmtyp != 1 && entry.substreamid == bsi.substreamid) {
     61                     done = true;
     62                 }
     63             }
     64             if (!done) {
     65                 entries.add(bsi);
     66                 long skipped = inputStream.skip(bsi.frameSize);
     67                 assert skipped == bsi.frameSize;
     68             }
     69         }
     70 
     71         inputStream.reset();
     72 
     73         if (entries.size() == 0) {
     74             throw new IOException();
     75         }
     76         samplerate = entries.get(0).samplerate;
     77 
     78         sampleDescriptionBox = new SampleDescriptionBox();
     79         AudioSampleEntry audioSampleEntry = new AudioSampleEntry("ec-3");
     80         audioSampleEntry.setChannelCount(2);  // According to  ETSI TS 102 366 Annex F
     81         audioSampleEntry.setSampleRate(samplerate);
     82         audioSampleEntry.setDataReferenceIndex(1);
     83         audioSampleEntry.setSampleSize(16);
     84 
     85         EC3SpecificBox ec3 = new EC3SpecificBox();
     86         int[] deps = new int[entries.size()];
     87         int[] chan_locs = new int[entries.size()];
     88         for (BitStreamInfo bsi : entries) {
     89             if (bsi.strmtyp == 1) {
     90                 deps[bsi.substreamid]++;
     91                 chan_locs[bsi.substreamid] = ((bsi.chanmap >> 6) & 0x100) | ((bsi.chanmap >> 5) & 0xff);
     92             }
     93         }
     94         for (BitStreamInfo bsi : entries) {
     95             if (bsi.strmtyp != 1) {
     96                 EC3SpecificBox.Entry e = new EC3SpecificBox.Entry();
     97                 e.fscod = bsi.fscod;
     98                 e.bsid = bsi.bsid;
     99                 e.bsmod = bsi.bsmod;
    100                 e.acmod = bsi.acmod;
    101                 e.lfeon = bsi.lfeon;
    102                 e.reserved = 0;
    103                 e.num_dep_sub = deps[bsi.substreamid];
    104                 e.chan_loc = chan_locs[bsi.substreamid];
    105                 e.reserved2 = 0;
    106                 ec3.addEntry(e);
    107             }
    108             bitrate += bsi.bitrate;
    109             frameSize += bsi.frameSize;
    110         }
    111 
    112         ec3.setDataRate(bitrate / 1000);
    113         audioSampleEntry.addBox(ec3);
    114         sampleDescriptionBox.addBox(audioSampleEntry);
    115 
    116         trackMetaData.setCreationTime(new Date());
    117         trackMetaData.setModificationTime(new Date());
    118         trackMetaData.setLanguage(lang);
    119         trackMetaData.setTimescale(samplerate); // Audio tracks always use samplerate as timescale
    120 
    121         samples = new LinkedList<ByteBuffer>();
    122         if (!readSamples()) {
    123             throw new IOException();
    124         }
    125     }
    126 
    127 
    128     public List<ByteBuffer> getSamples() {
    129 
    130         return samples;
    131     }
    132 
    133     public SampleDescriptionBox getSampleDescriptionBox() {
    134         return sampleDescriptionBox;
    135     }
    136 
    137     public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
    138         return stts;
    139     }
    140 
    141     public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
    142         return null;
    143     }
    144 
    145     public long[] getSyncSamples() {
    146         return null;
    147     }
    148 
    149     public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
    150         return null;
    151     }
    152 
    153     public TrackMetaData getTrackMetaData() {
    154         return trackMetaData;
    155     }
    156 
    157     public String getHandler() {
    158         return "soun";
    159     }
    160 
    161     public AbstractMediaHeaderBox getMediaHeaderBox() {
    162         return new SoundMediaHeaderBox();
    163     }
    164 
    165     public SubSampleInformationBox getSubsampleInformationBox() {
    166         return null;
    167     }
    168 
    169     private BitStreamInfo readVariables() throws IOException {
    170         byte[] data = new byte[200];
    171         inputStream.mark(200);
    172         if (200 != inputStream.read(data, 0, 200)) {
    173             return null;
    174         }
    175         inputStream.reset(); // Rewind
    176         ByteBuffer bb = ByteBuffer.wrap(data);
    177         BitReaderBuffer brb = new BitReaderBuffer(bb);
    178         int syncword = brb.readBits(16);
    179         if (syncword != 0xb77) {
    180             return null;
    181         }
    182 
    183         BitStreamInfo entry = new BitStreamInfo();
    184 
    185         entry.strmtyp = brb.readBits(2);
    186         entry.substreamid = brb.readBits(3);
    187         int frmsiz = brb.readBits(11);
    188         entry.frameSize = 2 * (frmsiz + 1);
    189 
    190         entry.fscod = brb.readBits(2);
    191         int fscod2 = -1;
    192         int numblkscod;
    193         if (entry.fscod == 3) {
    194             fscod2 = brb.readBits(2);
    195             numblkscod = 3;
    196         } else {
    197             numblkscod = brb.readBits(2);
    198         }
    199         int numberOfBlocksPerSyncFrame = 0;
    200         switch (numblkscod) {
    201             case 0:
    202                 numberOfBlocksPerSyncFrame = 1;
    203                 break;
    204 
    205             case 1:
    206                 numberOfBlocksPerSyncFrame = 2;
    207                 break;
    208 
    209             case 2:
    210                 numberOfBlocksPerSyncFrame = 3;
    211                 break;
    212 
    213             case 3:
    214                 numberOfBlocksPerSyncFrame = 6;
    215                 break;
    216 
    217         }
    218         entry.frameSize *= (6 / numberOfBlocksPerSyncFrame);
    219 
    220         entry.acmod = brb.readBits(3);
    221         entry.lfeon = brb.readBits(1);
    222         entry.bsid = brb.readBits(5);
    223         brb.readBits(5);
    224         if (1 == brb.readBits(1)) {
    225             brb.readBits(8); // compr
    226         }
    227         if (0 == entry.acmod) {
    228             brb.readBits(5);
    229             if (1 == brb.readBits(1)) {
    230                 brb.readBits(8);
    231             }
    232         }
    233         if (1 == entry.strmtyp) {
    234             if (1 == brb.readBits(1)) {
    235                 entry.chanmap = brb.readBits(16);
    236             }
    237         }
    238         if (1 == brb.readBits(1)) {     // mixing metadata
    239             if (entry.acmod > 2) {
    240                 brb.readBits(2);
    241             }
    242             if (1 == (entry.acmod & 1) && entry.acmod > 2) {
    243                 brb.readBits(3);
    244                 brb.readBits(3);
    245             }
    246             if (0 < (entry.acmod & 4)) {
    247                 brb.readBits(3);
    248                 brb.readBits(3);
    249             }
    250             if (1 == entry.lfeon) {
    251                 if (1 == brb.readBits(1)) {
    252                     brb.readBits(5);
    253                 }
    254             }
    255             if (0 == entry.strmtyp) {
    256                 if (1 == brb.readBits(1)) {
    257                     brb.readBits(6);
    258                 }
    259                 if (0 == entry.acmod) {
    260                     if (1 == brb.readBits(1)) {
    261                         brb.readBits(6);
    262                     }
    263                 }
    264                 if (1 == brb.readBits(1)) {
    265                     brb.readBits(6);
    266                 }
    267                 int mixdef = brb.readBits(2);
    268                 if (1 == mixdef) {
    269                     brb.readBits(5);
    270                 } else if (2 == mixdef) {
    271                     brb.readBits(12);
    272                 } else if (3 == mixdef) {
    273                     int mixdeflen = brb.readBits(5);
    274                     if (1 == brb.readBits(1)) {
    275                         brb.readBits(5);
    276                         if (1 == brb.readBits(1)) {
    277                             brb.readBits(4);
    278                         }
    279                         if (1 == brb.readBits(1)) {
    280                             brb.readBits(4);
    281                         }
    282                         if (1 == brb.readBits(1)) {
    283                             brb.readBits(4);
    284                         }
    285                         if (1 == brb.readBits(1)) {
    286                             brb.readBits(4);
    287                         }
    288                         if (1 == brb.readBits(1)) {
    289                             brb.readBits(4);
    290                         }
    291                         if (1 == brb.readBits(1)) {
    292                             brb.readBits(4);
    293                         }
    294                         if (1 == brb.readBits(1)) {
    295                             brb.readBits(4);
    296                         }
    297                         if (1 == brb.readBits(1)) {
    298                             if (1 == brb.readBits(1)) {
    299                                 brb.readBits(4);
    300                             }
    301                             if (1 == brb.readBits(1)) {
    302                                 brb.readBits(4);
    303                             }
    304                         }
    305                     }
    306                     if (1 == brb.readBits(1)) {
    307                         brb.readBits(5);
    308                         if (1 == brb.readBits(1)) {
    309                             brb.readBits(7);
    310                             if (1 == brb.readBits(1)) {
    311                                 brb.readBits(8);
    312                             }
    313                         }
    314                     }
    315                     for (int i = 0; i < (mixdeflen + 2); i++) {
    316                         brb.readBits(8);
    317                     }
    318                     brb.byteSync();
    319                 }
    320                 if (entry.acmod < 2) {
    321                     if (1 == brb.readBits(1)) {
    322                         brb.readBits(14);
    323                     }
    324                     if (0 == entry.acmod) {
    325                         if (1 == brb.readBits(1)) {
    326                             brb.readBits(14);
    327                         }
    328                     }
    329                     if (1 == brb.readBits(1)) {
    330                         if (numblkscod == 0) {
    331                             brb.readBits(5);
    332                         } else {
    333                             for (int i = 0; i < numberOfBlocksPerSyncFrame; i++) {
    334                                 if (1 == brb.readBits(1)) {
    335                                     brb.readBits(5);
    336                                 }
    337                             }
    338                         }
    339 
    340                     }
    341                 }
    342             }
    343         }
    344         if (1 == brb.readBits(1)) { // infomdate
    345             entry.bsmod = brb.readBits(3);
    346         }
    347 
    348         switch (entry.fscod) {
    349             case 0:
    350                 entry.samplerate = 48000;
    351                 break;
    352 
    353             case 1:
    354                 entry.samplerate = 44100;
    355                 break;
    356 
    357             case 2:
    358                 entry.samplerate = 32000;
    359                 break;
    360 
    361             case 3: {
    362                 switch (fscod2) {
    363                     case 0:
    364                         entry.samplerate = 24000;
    365                         break;
    366 
    367                     case 1:
    368                         entry.samplerate = 22050;
    369                         break;
    370 
    371                     case 2:
    372                         entry.samplerate = 16000;
    373                         break;
    374 
    375                     case 3:
    376                         entry.samplerate = 0;
    377                         break;
    378                 }
    379                 break;
    380             }
    381 
    382         }
    383         if (entry.samplerate == 0) {
    384             return null;
    385         }
    386 
    387         entry.bitrate = (int) (((double) entry.samplerate) / 1536.0 * entry.frameSize * 8);
    388 
    389         return entry;
    390     }
    391 
    392     private boolean readSamples() throws IOException {
    393         int read = frameSize;
    394         boolean ret = false;
    395         while (frameSize == read) {
    396             ret = true;
    397             byte[] data = new byte[frameSize];
    398             read = inputStream.read(data);
    399             if (read == frameSize) {
    400                 samples.add(ByteBuffer.wrap(data));
    401                 stts.add(new TimeToSampleBox.Entry(1, 1536));
    402             }
    403         }
    404         return ret;
    405     }
    406 
    407     public static class BitStreamInfo extends EC3SpecificBox.Entry {
    408         public int frameSize;
    409         public int substreamid;
    410         public int bitrate;
    411         public int samplerate;
    412         public int strmtyp;
    413         public int chanmap;
    414 
    415         @Override
    416         public String toString() {
    417             return "BitStreamInfo{" +
    418                     "frameSize=" + frameSize +
    419                     ", substreamid=" + substreamid +
    420                     ", bitrate=" + bitrate +
    421                     ", samplerate=" + samplerate +
    422                     ", strmtyp=" + strmtyp +
    423                     ", chanmap=" + chanmap +
    424                     '}';
    425         }
    426     }
    427 
    428     @Override
    429     public String toString() {
    430         return "EC3TrackImpl{" +
    431                 "bitrate=" + bitrate +
    432                 ", samplerate=" + samplerate +
    433                 ", entries=" + entries +
    434                 '}';
    435     }
    436 }
    437