package com.googlecode.mp4parser;
import com.coremedia.iso.BoxParser;
import com.coremedia.iso.boxes.Box;
import com.coremedia.iso.boxes.Container;
import com.googlecode.mp4parser.util.LazyList;
import com.googlecode.mp4parser.util.Logger;
import java.io.Closeable;
import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import java.util.*;
/**
* Created by sannies on 18.05.13.
*/
public class BasicContainer implements Container, Iterator<Box>, Closeable {
private static Logger LOG = Logger.getLogger(BasicContainer.class);
private List<Box> boxes = new ArrayList<Box>();
protected BoxParser boxParser;
protected DataSource dataSource;
Box lookahead = null;
long parsePosition = 0;
long startPosition = 0;
long endPosition = 0;
private static final Box EOF = new AbstractBox("eof ") {
@Override
protected long getContentSize() {
return 0;
}
@Override
protected void getContent(ByteBuffer byteBuffer) {
}
@Override
protected void _parseDetails(ByteBuffer content) {
}
};
public List<Box> getBoxes() {
if (dataSource != null && lookahead != EOF) {
return new LazyList<Box>(boxes, this);
} else {
return boxes;
}
}
protected long getContainerSize() {
long contentSize = 0;
for (int i = 0; i < getBoxes().size(); i++) {
// it's quicker to iterate an array list like that since no iterator
// needs to be instantiated
contentSize += boxes.get(i).getSize();
}
return contentSize;
}
public BasicContainer() {
}
public void setBoxes(List<Box> boxes) {
this.boxes = new ArrayList<Box>(boxes);
this.lookahead = EOF;
this.dataSource = null;
}
@SuppressWarnings("unchecked")
public <T extends Box> List<T> getBoxes(Class<T> clazz) {
List<T> boxesToBeReturned = null;
T oneBox = null;
List<Box> boxes = getBoxes();
for (int i = 0; i < boxes.size(); i++) {
Box boxe = boxes.get(i);
//clazz.isInstance(boxe) / clazz == boxe.getClass()?
// I hereby finally decide to use isInstance
if (clazz.isInstance(boxe)) {
if (oneBox == null) {
oneBox = (T) boxe;
} else {
if (boxesToBeReturned == null) {
boxesToBeReturned = new ArrayList<T>(2);
boxesToBeReturned.add(oneBox);
}
boxesToBeReturned.add((T) boxe);
}
}
}
if (boxesToBeReturned != null) {
return boxesToBeReturned;
} else if (oneBox != null) {
return Collections.singletonList(oneBox);
} else {
return Collections.emptyList();
}
}
@SuppressWarnings("unchecked")
public <T extends Box> List<T> getBoxes(Class<T> clazz, boolean recursive) {
List<T> boxesToBeReturned = new ArrayList<T>(2);
List<Box> boxes = getBoxes();
for (int i = 0; i < boxes.size(); i++) {
Box boxe = boxes.get(i);
//clazz.isInstance(boxe) / clazz == boxe.getClass()?
// I hereby finally decide to use isInstance
if (clazz.isInstance(boxe)) {
boxesToBeReturned.add((T) boxe);
}
if (recursive && boxe instanceof Container) {
boxesToBeReturned.addAll(((Container) boxe).getBoxes(clazz, recursive));
}
}
return boxesToBeReturned;
}
/**
* Add <code>b</code> to the container and sets the parent correctly.
*
* @param b will be added to the container
*/
public void addBox(Box b) {
boxes = new ArrayList<Box>(getBoxes());
b.setParent(this);
boxes.add(b);
}
public void parseContainer(DataSource dataSource, long containerSize, BoxParser boxParser) throws IOException {
this.dataSource = dataSource;
this.parsePosition = this.startPosition = dataSource.position();
dataSource.position(dataSource.position() + containerSize);
this.endPosition = dataSource.position();
this.boxParser = boxParser;
}
public void remove() {
throw new UnsupportedOperationException();
}
public boolean hasNext() {
if (lookahead == EOF) {
return false;
}
if (lookahead != null) {
return true;
} else {
try {
lookahead = next();
return true;
} catch (NoSuchElementException e) {
lookahead = EOF;
return false;
}
}
}
public Box next() {
if (lookahead != null && lookahead != EOF) {
Box b = lookahead;
lookahead = null;
return b;
} else {
LOG.logDebug("Parsing next() box");
if (dataSource == null || parsePosition >= endPosition) {
lookahead = EOF;
throw new NoSuchElementException();
}
try {
synchronized (dataSource) {
dataSource.position(parsePosition);
Box b = boxParser.parseBox(dataSource, this);
//System.err.println(b.getType());
parsePosition = dataSource.position();
return b;
}
} catch (EOFException e) {
throw new NoSuchElementException();
} catch (IOException e) {
throw new NoSuchElementException();
}
}
}
public String toString() {
StringBuilder buffer = new StringBuilder();
buffer.append(this.getClass().getSimpleName()).append("[");
for (int i = 0; i < boxes.size(); i++) {
if (i > 0) {
buffer.append(";");
}
buffer.append(boxes.get(i).toString());
}
buffer.append("]");
return buffer.toString();
}
public final void writeContainer(WritableByteChannel bb) throws IOException {
for (Box box : getBoxes()) {
box.getBox(bb);
}
}
public ByteBuffer getByteBuffer(long start, long size) throws IOException {
synchronized (this.dataSource) {
return this.dataSource.map(this.startPosition + start, size);
}
}
public void close() throws IOException {
dataSource.close();
}
}