package org.jcodec.containers.mp4.boxes; import org.jcodec.common.Assert; import org.jcodec.common.StringUtils; import org.jcodec.common.UsedViaReflection; 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 org.jcodec.platform.Platform; import java.lang.StringBuilder; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * An MP4 file struncture (box). * * @author The JCodec project * */ public abstract class Box { private static final String GET_MODEL_FIELDS = "getModelFields"; public Header header; public static final int MAX_BOX_SIZE = 128 * 1024 * 1024; @UsedViaReflection public Box(Header header) { this.header = header; } public Header getHeader() { return header; } public abstract void parse(ByteBuffer buf); public void write(ByteBuffer buf) { ByteBuffer dup = buf.duplicate(); NIOUtils.skip(buf, 8); doWrite(buf); header.setBodySize(buf.position() - dup.position() - 8); Assert.assertEquals(header.headerSize(), 8); header.write(dup); } protected abstract void doWrite(ByteBuffer out); public String getFourcc() { return header.getFourcc(); } public String toString() { StringBuilder sb = new StringBuilder(); dump(sb); return sb.toString(); } 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("}"); } protected void collectModel(Class claz, List<String> model) { if (Box.class == claz || !Box.class.isAssignableFrom(claz)) return; collectModel(claz.getSuperclass(), model); try { Platform.invokeMethod(this, GET_MODEL_FIELDS, new Object[]{model}); } catch (Exception e) { checkWrongSignature(claz); model.addAll(ToJSON.allFields(claz)); } } private void checkWrongSignature(Class claz) { Method[] declaredMethods = Platform.getDeclaredMethods(claz); for (int i = 0; i < declaredMethods.length; i++) { Method method = declaredMethods[i]; if (method.getName().equals(GET_MODEL_FIELDS)) { Logger.warn("Class " + claz.getCanonicalName() + " contains 'getModelFields' of wrong signature.\n" + "Did you mean to define 'protected void " + GET_MODEL_FIELDS + "(List<String> model) ?"); break; } } } public static String[] path(String path) { return StringUtils.splitC(path, '.'); } public static LeafBox createLeafBox(Header atom, ByteBuffer data) { LeafBox leaf = new LeafBox(atom); leaf.data = data; return leaf; } public static Box parseBox(ByteBuffer input, Header childAtom, IBoxFactory factory) { Box box = factory.newBox(childAtom); if (childAtom.getBodySize() < Box.MAX_BOX_SIZE) { box.parse(input); return box; } else { return new LeafBox(Header.createHeader("free", 8)); } } public static <T extends Box> T asBox(Class<T> class1, Box box) { try { T res = Platform.newInstance(class1, new Object[]{box.getHeader()}); ByteBuffer buffer = ByteBuffer.allocate((int)box.getHeader().getBodySize()); box.doWrite(buffer); buffer.flip(); res.parse(buffer); return res; } catch (Exception e) { throw new RuntimeException(e); } } public static class LeafBox extends Box { ByteBuffer data; public LeafBox(Header atom) { super(atom); } public void parse(ByteBuffer input) { data = NIOUtils.read(input, (int) header.getBodySize()); } public ByteBuffer getData() { return data.duplicate(); } @Override protected void doWrite(ByteBuffer out) { NIOUtils.write(out, data); } } }