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