1 /* 2 * Copyright 2008 CoreMedia AG, Hamburg 3 * 4 * Licensed under the Apache License, Version 2.0 (the License); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an AS IS BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.googlecode.mp4parser; 18 19 import com.coremedia.iso.BoxParser; 20 import com.coremedia.iso.boxes.Box; 21 import com.coremedia.iso.boxes.ContainerBox; 22 import com.googlecode.mp4parser.util.ByteBufferByteChannel; 23 24 import java.io.IOException; 25 import java.nio.ByteBuffer; 26 import java.nio.channels.ReadableByteChannel; 27 import java.nio.channels.WritableByteChannel; 28 import java.util.ArrayList; 29 import java.util.LinkedList; 30 import java.util.List; 31 import java.util.logging.Logger; 32 33 /** 34 * Abstract base class for a full iso box only containing ither boxes. 35 */ 36 public abstract class FullContainerBox extends AbstractFullBox implements ContainerBox { 37 protected List<Box> boxes = new LinkedList<Box>(); 38 private static Logger LOG = Logger.getLogger(FullContainerBox.class.getName()); 39 BoxParser boxParser; 40 41 public void setBoxes(List<Box> boxes) { 42 this.boxes = new LinkedList<Box>(boxes); 43 } 44 45 @SuppressWarnings("unchecked") 46 public <T extends Box> List<T> getBoxes(Class<T> clazz) { 47 return getBoxes(clazz, false); 48 } 49 50 @SuppressWarnings("unchecked") 51 public <T extends Box> List<T> getBoxes(Class<T> clazz, boolean recursive) { 52 List<T> boxesToBeReturned = new ArrayList<T>(2); 53 for (Box boxe : boxes) { //clazz.isInstance(boxe) / clazz == boxe.getClass()? 54 if (clazz == boxe.getClass()) { 55 boxesToBeReturned.add((T) boxe); 56 } 57 58 if (recursive && boxe instanceof ContainerBox) { 59 boxesToBeReturned.addAll((((ContainerBox) boxe).getBoxes(clazz, recursive))); 60 } 61 } 62 // Optimize here! Spare object creation work on arrays directly! System.arrayCopy 63 return boxesToBeReturned; 64 //return (T[]) boxesToBeReturned.toArray(); 65 } 66 67 protected long getContentSize() { 68 long contentSize = 4; // flags and version 69 for (Box boxe : boxes) { 70 contentSize += boxe.getSize(); 71 } 72 return contentSize; 73 } 74 75 public void addBox(Box b) { 76 b.setParent(this); 77 boxes.add(b); 78 } 79 80 public void removeBox(Box b) { 81 b.setParent(null); 82 boxes.remove(b); 83 } 84 85 public FullContainerBox(String type) { 86 super(type); 87 } 88 89 public List<Box> getBoxes() { 90 return boxes; 91 } 92 93 @Override 94 public void parse(ReadableByteChannel readableByteChannel, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException { 95 this.boxParser = boxParser; 96 super.parse(readableByteChannel, header, contentSize, boxParser); 97 98 } 99 100 @Override 101 public void _parseDetails(ByteBuffer content) { 102 parseVersionAndFlags(content); 103 parseChildBoxes(content); 104 } 105 106 protected final void parseChildBoxes(ByteBuffer content) { 107 try { 108 while (content.remaining() >= 8) { // 8 is the minimal size for a sane box 109 boxes.add(boxParser.parseBox(new ByteBufferByteChannel(content), this)); 110 } 111 112 if (content.remaining() != 0) { 113 setDeadBytes(content.slice()); 114 LOG.severe("Some sizes are wrong"); 115 } 116 } catch (IOException e) { 117 throw new RuntimeException(e); 118 } 119 } 120 121 public String toString() { 122 StringBuilder buffer = new StringBuilder(); 123 buffer.append(this.getClass().getSimpleName()).append("["); 124 for (int i = 0; i < boxes.size(); i++) { 125 if (i > 0) { 126 buffer.append(";"); 127 } 128 buffer.append(boxes.get(i).toString()); 129 } 130 buffer.append("]"); 131 return buffer.toString(); 132 } 133 134 135 protected void getContent(ByteBuffer byteBuffer) { 136 writeVersionAndFlags(byteBuffer); 137 writeChildBoxes(byteBuffer); 138 } 139 140 protected final void writeChildBoxes(ByteBuffer bb) { 141 WritableByteChannel wbc = new ByteBufferByteChannel(bb); 142 for (Box box : boxes) { 143 try { 144 box.getBox(wbc); 145 } catch (IOException e) { 146 // cannot happen since my WritableByteChannel won't throw any excpetion 147 throw new RuntimeException("Cannot happen.", e); 148 } 149 150 } 151 } 152 153 public long getNumOfBytesToFirstChild() { 154 long sizeOfChildren = 0; 155 for (Box box : boxes) { 156 sizeOfChildren += box.getSize(); 157 } 158 return getSize() - sizeOfChildren; 159 } 160 } 161