/* * Copyright 2012 Sebastian Annies, Hamburg * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.coremedia.iso; import com.coremedia.iso.boxes.Box; import com.coremedia.iso.boxes.ContainerBox; import com.coremedia.iso.boxes.UserBox; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; import java.util.logging.Logger; import static com.googlecode.mp4parser.util.CastUtils.l2i; /** * This BoxParser handles the basic stuff like reading size and extracting box type. */ public abstract class AbstractBoxParser implements BoxParser { private static Logger LOG = Logger.getLogger(AbstractBoxParser.class.getName()); public abstract Box createBox(String type, byte[] userType, String parent); /** * Parses the next size and type, creates a box instance and parses the box's content. * * @param byteChannel the FileChannel pointing to the ISO file * @param parent the current box's parent (null if no parent) * @return the box just parsed * @throws java.io.IOException if reading from in fails */ public Box parseBox(ReadableByteChannel byteChannel, ContainerBox parent) throws IOException { ByteBuffer header = ChannelHelper.readFully(byteChannel, 8); long size = IsoTypeReader.readUInt32(header); // do plausibility check if (size < 8 && size > 1) { LOG.severe("Plausibility check failed: size < 8 (size = " + size + "). Stop parsing!"); return null; } String type = IsoTypeReader.read4cc(header); byte[] usertype = null; long contentSize; if (size == 1) { ByteBuffer bb = ByteBuffer.allocate(8); byteChannel.read(bb); bb.rewind(); size = IsoTypeReader.readUInt64(bb); contentSize = size - 16; } else if (size == 0) { if (byteChannel instanceof FileChannel) { size = ((FileChannel) byteChannel).size() - ((FileChannel) byteChannel).position() - 8; } else { throw new RuntimeException("Only FileChannel inputs may use size == 0 (box reaches to the end of file)"); } contentSize = size - 8; } else { contentSize = size - 8; } if (UserBox.TYPE.equals(type)) { ByteBuffer bb = ByteBuffer.allocate(16); byteChannel.read(bb); bb.rewind(); usertype = bb.array(); contentSize -= 16; } Box box = createBox(type, usertype, parent.getType()); box.setParent(parent); LOG.finest("Parsing " + box.getType()); // System.out.println("parsing " + Arrays.toString(box.getType()) + " " + box.getClass().getName() + " size=" + size); if (l2i(size - contentSize) == 8) { // default - no large box - no uuid // do nothing header's already correct header.rewind(); } else if (l2i(size - contentSize) == 16) { header = ByteBuffer.allocate(16); IsoTypeWriter.writeUInt32(header, 1); header.put(IsoFile.fourCCtoBytes(type)); IsoTypeWriter.writeUInt64(header, size); } else if (l2i(size - contentSize) == 24) { header = ByteBuffer.allocate(24); IsoTypeWriter.writeUInt32(header, size); header.put(IsoFile.fourCCtoBytes(type)); header.put(usertype); } else if (l2i(size - contentSize) == 32) { header = ByteBuffer.allocate(32); IsoTypeWriter.writeUInt32(header, size); header.put(IsoFile.fourCCtoBytes(type)); IsoTypeWriter.writeUInt64(header, size); header.put(usertype); } else { throw new RuntimeException("I didn't expect that"); } box.parse(byteChannel, header, contentSize, this); // System.out.println("box = " + box); assert size == box.getSize() : "Reconstructed Size is not x to the number of parsed bytes! (" + box.getType() + ")" + " Actual Box size: " + size + " Calculated size: " + box.getSize(); return box; } }