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.Collections; 30 import java.util.LinkedList; 31 import java.util.List; 32 import java.util.logging.Logger; 33 34 35 /** 36 * Abstract base class suitable for most boxes acting purely as container for other boxes. 37 */ 38 public abstract class AbstractContainerBox extends AbstractBox implements ContainerBox { 39 private static Logger LOG = Logger.getLogger(AbstractContainerBox.class.getName()); 40 41 protected List<Box> boxes = new LinkedList<Box>(); 42 protected BoxParser boxParser; 43 44 @Override 45 protected long getContentSize() { 46 long contentSize = 0; 47 for (Box boxe : boxes) { 48 contentSize += boxe.getSize(); 49 } 50 return contentSize; 51 } 52 53 public AbstractContainerBox(String type) { 54 super(type); 55 } 56 57 public List<Box> getBoxes() { 58 return Collections.unmodifiableList(boxes); 59 } 60 61 public void setBoxes(List<Box> boxes) { 62 this.boxes = new LinkedList<Box>(boxes); 63 } 64 65 @SuppressWarnings("unchecked") 66 public <T extends Box> List<T> getBoxes(Class<T> clazz) { 67 return getBoxes(clazz, false); 68 } 69 70 @SuppressWarnings("unchecked") 71 public <T extends Box> List<T> getBoxes(Class<T> clazz, boolean recursive) { 72 List<T> boxesToBeReturned = new ArrayList<T>(2); 73 for (Box boxe : boxes) { 74 //clazz.isInstance(boxe) / clazz == boxe.getClass()? 75 // I hereby finally decide to use isInstance 76 77 if (clazz.isInstance(boxe)) { 78 boxesToBeReturned.add((T) boxe); 79 } 80 81 if (recursive && boxe instanceof ContainerBox) { 82 boxesToBeReturned.addAll(((ContainerBox) boxe).getBoxes(clazz, recursive)); 83 } 84 } 85 return boxesToBeReturned; 86 } 87 88 /** 89 * Add <code>b</code> to the container and sets the parent correctly. 90 * 91 * @param b will be added to the container 92 */ 93 public void addBox(Box b) { 94 b.setParent(this); 95 boxes.add(b); 96 } 97 98 public void removeBox(Box b) { 99 b.setParent(this); 100 boxes.remove(b); 101 } 102 103 @Override 104 public void parse(ReadableByteChannel readableByteChannel, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException { 105 this.boxParser = boxParser; 106 super.parse(readableByteChannel, header, contentSize, boxParser); 107 } 108 109 @Override 110 public void _parseDetails(ByteBuffer content) { 111 parseChildBoxes(content); 112 } 113 114 115 public String toString() { 116 StringBuilder buffer = new StringBuilder(); 117 118 buffer.append(this.getClass().getSimpleName()).append("["); 119 for (int i = 0; i < boxes.size(); i++) { 120 if (i > 0) { 121 buffer.append(";"); 122 } 123 buffer.append(boxes.get(i).toString()); 124 } 125 buffer.append("]"); 126 return buffer.toString(); 127 } 128 129 /** 130 * The number of bytes from box start (first length byte) to the 131 * first length byte of the first child box 132 * 133 * @return offset to first child box 134 */ 135 public long getNumOfBytesToFirstChild() { 136 return 8; 137 } 138 139 @Override 140 protected void getContent(ByteBuffer byteBuffer) { 141 writeChildBoxes(byteBuffer); 142 } 143 144 protected final void parseChildBoxes(ByteBuffer content) { 145 try { 146 while (content.remaining() >= 8) { // 8 is the minimal size for a sane box 147 boxes.add(boxParser.parseBox(new ByteBufferByteChannel(content), this)); 148 } 149 150 if (content.remaining() != 0) { 151 setDeadBytes(content.slice()); 152 LOG.warning("Something's wrong with the sizes. There are dead bytes in a container box."); 153 } 154 } catch (IOException e) { 155 throw new RuntimeException(e); 156 } 157 } 158 159 protected final void writeChildBoxes(ByteBuffer bb) { 160 WritableByteChannel wbc = new ByteBufferByteChannel(bb); 161 for (Box box : boxes) { 162 try { 163 box.getBox(wbc); 164 } catch (IOException e) { 165 // My WritableByteChannel won't throw any excpetion 166 throw new RuntimeException("Cannot happen to me", e); 167 } 168 } 169 } 170 171 } 172