package org.jcodec.containers.mp4.boxes;
import java.util.Iterator;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.tools.ToJSON;
import org.jcodec.containers.mp4.IBoxFactory;
import java.lang.StringBuilder;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import org.jcodec.platform.Platform;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* A node box
*
* A box containing children, no data
*
* @author The JCodec project
*
*/
public class NodeBox extends Box {
protected List<Box> boxes;
protected IBoxFactory factory;
public NodeBox(Header atom) {
super(atom);
this.boxes = new LinkedList<Box>();
}
public void setFactory(IBoxFactory factory) {
this.factory = factory;
}
public void parse(ByteBuffer input) {
while (input.remaining() >= 8) {
Box child = parseChildBox(input, factory);
if (child != null)
boxes.add(child);
}
}
public static Box parseChildBox(ByteBuffer input, IBoxFactory factory) {
ByteBuffer fork = input.duplicate();
while (input.remaining() >= 4 && fork.getInt() == 0)
input.getInt();
if (input.remaining() < 4)
return null;
Header childAtom = Header.read(input);
if (childAtom != null && input.remaining() >= childAtom.getBodySize())
return Box.parseBox(NIOUtils.read(input, (int) childAtom.getBodySize()), childAtom, factory);
else
return null;
}
public List<Box> getBoxes() {
return boxes;
}
public void add(Box box) {
boxes.add(box);
}
protected void doWrite(ByteBuffer out) {
for (Box box : boxes) {
box.write(out);
}
}
public void addFirst(MovieHeaderBox box) {
boxes.add(0, box);
}
public void replace(String fourcc, Box box) {
removeChildren(fourcc);
add(box);
}
public void replaceBox(Box box) {
removeChildren(box.getFourcc());
add(box);
}
protected void dump(StringBuilder sb) {
sb.append("{\"tag\":\"" + header.getFourcc() + "\",");
List<String> fields = new ArrayList<String>(0);
collectModel(this.getClass(), fields);
ToJSON.fieldsToJSON(this, sb, fields.toArray(new String[0]));
sb.append("\"boxes\": [");
dumpBoxes(sb);
sb.append("]");
sb.append("}");
}
protected void getModelFields(List<String> model) {
}
protected void dumpBoxes(StringBuilder sb) {
for (int i = 0; i < boxes.size(); i++) {
boxes.get(i).dump(sb);
if (i < boxes.size() - 1)
sb.append(",");
}
}
public void removeChildren(String... arguments) {
for (Iterator<Box> it = boxes.iterator(); it.hasNext();) {
Box box = it.next();
String fcc = box.getFourcc();
for (int i = 0; i < arguments.length; i++) {
String cand = arguments[i];
if (cand.equals(fcc)) {
it.remove();
break;
}
}
}
}
public static Box doCloneBox(Box box, int approxSize, IBoxFactory bf) {
ByteBuffer buf = ByteBuffer.allocate(approxSize);
box.write(buf);
buf.flip();
return parseChildBox(buf, bf);
}
public static Box cloneBox(Box box, int approxSize, IBoxFactory bf) {
return NodeBox.doCloneBox(box, approxSize, bf);
}
public static <T extends Box> T[] findAll(Box box, Class<T> class1, String path) {
return findAllPath(box, class1, new String[] { path });
}
public static <T extends Box> T findFirst(NodeBox box, Class<T> clazz, String path) {
return findFirstPath(box, clazz, new String[] { path });
}
public static <T extends Box> T findFirstPath(NodeBox box, Class<T> clazz, String[] path) {
T[] result = (T[]) findAllPath(box, clazz, path);
return result.length > 0 ? result[0] : null;
}
public static <T extends Box> T[] findAllPath(Box box, Class<T> class1, String[] path) {
List<Box> result = new LinkedList<Box>();
List<String> tlist = new LinkedList<String>();
for (int i = 0; i < path.length; i++) {
String type = path[i];
tlist.add(type);
}
findBox(box, tlist, result);
for (ListIterator<Box> it = result.listIterator(); it.hasNext();) {
Box next = it.next();
if (next == null) {
it.remove();
} else if (!Platform.isAssignableFrom(class1, next.getClass())) {
// Trying to reinterpret one box as the other
try {
it.set(Box.asBox(class1, next));
} catch (Exception e) {
Logger.warn("Failed to reinterpret box: " + next.getFourcc() + " as: " + class1.getName() + "."
+ e.getMessage());
it.remove();
}
}
}
return result.toArray((T[]) Array.newInstance(class1, 0));
}
public static void findBox(Box root, List<String> path, Collection<Box> result) {
if (path.size() > 0) {
String head = path.remove(0);
if (root instanceof NodeBox) {
NodeBox nb = (NodeBox) root;
for (Box candidate : nb.getBoxes()) {
if (head == null || head.equals(candidate.header.getFourcc())) {
findBox(candidate, path, result);
}
}
}
path.add(0, head);
} else {
result.add(root);
}
}
}