1package com.googlecode.mp4parser.authoring.tracks;
2
3import com.coremedia.iso.boxes.*;
4import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
5import com.googlecode.mp4parser.authoring.AbstractTrack;
6import com.googlecode.mp4parser.authoring.TrackMetaData;
7import com.googlecode.mp4parser.boxes.EC3SpecificBox;
8import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer;
9
10import java.io.BufferedInputStream;
11import java.io.IOException;
12import java.io.InputStream;
13import java.nio.ByteBuffer;
14import java.util.Date;
15import java.util.LinkedList;
16import 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 */
25public 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