use of org.jaudiotagger.utils.tree.DefaultMutableTreeNode in project MusicDNA by harjot-oberai.
the class Mp4AtomTree method buildTree.
/**
* Build a tree of the atoms in the file
*
* @param raf
* @param closeExit false to keep randomfileacces open, only used when randomaccessfile already being used
* @return
* @throws java.io.IOException
* @throws org.jaudiotagger.audio.exceptions.CannotReadException
*/
public DefaultTreeModel buildTree(RandomAccessFile raf, boolean closeExit) throws IOException, CannotReadException {
FileChannel fc = null;
try {
fc = raf.getChannel();
// make sure at start of file
fc.position(0);
// Build up map of nodes
rootNode = new DefaultMutableTreeNode();
dataTree = new DefaultTreeModel(rootNode);
// Iterate though all the top level Nodes
ByteBuffer headerBuffer = ByteBuffer.allocate(Mp4BoxHeader.HEADER_LENGTH);
while (fc.position() < fc.size()) {
Mp4BoxHeader boxHeader = new Mp4BoxHeader();
headerBuffer.clear();
fc.read(headerBuffer);
headerBuffer.rewind();
try {
boxHeader.update(headerBuffer);
} catch (NullBoxIdException ne) {
// If we only get this error after all the expected data has been found we allow it
if (moovNode != null & mdatNode != null) {
NullPadding np = new NullPadding(fc.position() - Mp4BoxHeader.HEADER_LENGTH, fc.size());
DefaultMutableTreeNode trailingPaddingNode = new DefaultMutableTreeNode(np);
rootNode.add(trailingPaddingNode);
logger.warning(ErrorMessage.NULL_PADDING_FOUND_AT_END_OF_MP4.getMsg(np.getFilePos()));
break;
} else {
// File appears invalid
throw ne;
}
}
boxHeader.setFilePos(fc.position() - Mp4BoxHeader.HEADER_LENGTH);
DefaultMutableTreeNode newAtom = new DefaultMutableTreeNode(boxHeader);
// Go down moov
if (boxHeader.getId().equals(Mp4AtomIdentifier.MOOV.getFieldName())) {
// and finish
if (moovNode != null & mdatNode != null) {
logger.warning(ErrorMessage.ADDITIONAL_MOOV_ATOM_AT_END_OF_MP4.getMsg(fc.position() - Mp4BoxHeader.HEADER_LENGTH));
break;
}
moovNode = newAtom;
moovHeader = boxHeader;
long filePosStart = fc.position();
moovBuffer = ByteBuffer.allocate(boxHeader.getDataLength());
int bytesRead = fc.read(moovBuffer);
// If Moov atom is incomplete we are not going to be able to read this file properly
if (bytesRead < boxHeader.getDataLength()) {
String msg = ErrorMessage.ATOM_LENGTH_LARGER_THAN_DATA.getMsg(boxHeader.getId(), boxHeader.getDataLength(), bytesRead);
throw new CannotReadException(msg);
}
moovBuffer.rewind();
buildChildrenOfNode(moovBuffer, newAtom);
fc.position(filePosStart);
} else if (boxHeader.getId().equals(Mp4AtomIdentifier.FREE.getFieldName())) {
// Might be multiple in different locations
freeNodes.add(newAtom);
} else if (boxHeader.getId().equals(Mp4AtomIdentifier.MDAT.getFieldName())) {
// mdatNode always points to the last mDatNode, normally there is just one mdatnode but do have
// a valid example of multiple mdatnode
// if(mdatNode!=null)
// {
// throw new CannotReadException(ErrorMessage.MP4_FILE_CONTAINS_MULTIPLE_DATA_ATOMS.getMsg());
// }
mdatNode = newAtom;
mdatNodes.add(newAtom);
}
rootNode.add(newAtom);
fc.position(fc.position() + boxHeader.getDataLength());
}
return dataTree;
} finally {
// now rather than later when try and write to it.
if (mdatNode == null) {
throw new CannotReadException(ErrorMessage.MP4_CANNOT_FIND_AUDIO.getMsg());
}
if (closeExit) {
fc.close();
}
}
}
use of org.jaudiotagger.utils.tree.DefaultMutableTreeNode in project MusicDNA by harjot-oberai.
the class Mp4AtomTree method printAtomTree.
/**
* Display atom tree
*/
@SuppressWarnings("unchecked")
public void printAtomTree() {
Enumeration<DefaultMutableTreeNode> e = rootNode.preorderEnumeration();
DefaultMutableTreeNode nextNode;
while (e.hasMoreElements()) {
nextNode = e.nextElement();
Mp4BoxHeader header = (Mp4BoxHeader) nextNode.getUserObject();
if (header != null) {
String tabbing = "";
for (int i = 1; i < nextNode.getLevel(); i++) {
tabbing += "\t";
}
if (header instanceof NullPadding) {
System.out.println(tabbing + "Null pad " + " @ " + header.getFilePos() + " of size:" + header.getLength() + " ,ends @ " + (header.getFilePos() + header.getLength()));
} else {
System.out.println(tabbing + "Atom " + header.getId() + " @ " + header.getFilePos() + " of size:" + header.getLength() + " ,ends @ " + (header.getFilePos() + header.getLength()));
}
}
}
}
use of org.jaudiotagger.utils.tree.DefaultMutableTreeNode in project MusicDNA by harjot-oberai.
the class Mp4AtomTree method buildChildrenOfNode.
/**
* @param moovBuffer
* @param parentNode
* @throws IOException
* @throws CannotReadException
*/
public void buildChildrenOfNode(ByteBuffer moovBuffer, DefaultMutableTreeNode parentNode) throws IOException, CannotReadException {
Mp4BoxHeader boxHeader;
// Preprocessing for nodes that contain data before their children atoms
Mp4BoxHeader parentBoxHeader = (Mp4BoxHeader) parentNode.getUserObject();
// We set the buffers position back to this after processing the children
int justAfterHeaderPos = moovBuffer.position();
// Preprocessing for meta that normally contains 4 data bytes, but doesn'timer where found under track or tags atom
if (parentBoxHeader.getId().equals(Mp4AtomIdentifier.META.getFieldName())) {
Mp4MetaBox meta = new Mp4MetaBox(parentBoxHeader, moovBuffer);
meta.processData();
try {
boxHeader = new Mp4BoxHeader(moovBuffer);
} catch (NullBoxIdException nbe) {
// It might be that the meta box didn'timer actually have any additional data after it so we adjust the buffer
// to be immediately after metabox and code can retry
moovBuffer.position(moovBuffer.position() - Mp4MetaBox.FLAGS_LENGTH);
} finally {
// Skip back last header cos this was only a test
moovBuffer.position(moovBuffer.position() - Mp4BoxHeader.HEADER_LENGTH);
}
}
// Defines where to start looking for the first child node
int startPos = moovBuffer.position();
while (moovBuffer.position() < ((startPos + parentBoxHeader.getDataLength()) - Mp4BoxHeader.HEADER_LENGTH)) {
boxHeader = new Mp4BoxHeader(moovBuffer);
if (boxHeader != null) {
boxHeader.setFilePos(moovHeader.getFilePos() + moovBuffer.position());
logger.finest("Atom " + boxHeader.getId() + " @ " + boxHeader.getFilePos() + " of size:" + boxHeader.getLength() + " ,ends @ " + (boxHeader.getFilePos() + boxHeader.getLength()));
DefaultMutableTreeNode newAtom = new DefaultMutableTreeNode(boxHeader);
parentNode.add(newAtom);
if (boxHeader.getId().equals(Mp4AtomIdentifier.UDTA.getFieldName())) {
udtaNode = newAtom;
} else // only interested in metaNode that is child of udta node
if (boxHeader.getId().equals(Mp4AtomIdentifier.META.getFieldName()) && parentBoxHeader.getId().equals(Mp4AtomIdentifier.UDTA.getFieldName())) {
metaNode = newAtom;
} else if (boxHeader.getId().equals(Mp4AtomIdentifier.HDLR.getFieldName()) && parentBoxHeader.getId().equals(Mp4AtomIdentifier.META.getFieldName())) {
hdlrWithinMetaNode = newAtom;
} else if (boxHeader.getId().equals(Mp4AtomIdentifier.HDLR.getFieldName())) {
hdlrWithinMdiaNode = newAtom;
} else if (boxHeader.getId().equals(Mp4AtomIdentifier.TAGS.getFieldName())) {
tagsNode = newAtom;
} else if (boxHeader.getId().equals(Mp4AtomIdentifier.STCO.getFieldName())) {
if (stco == null) {
stco = new Mp4StcoBox(boxHeader, moovBuffer);
stcoNode = newAtom;
}
} else if (boxHeader.getId().equals(Mp4AtomIdentifier.ILST.getFieldName())) {
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) parentNode.getParent();
if (parent != null) {
Mp4BoxHeader parentsParent = (Mp4BoxHeader) (parent).getUserObject();
if (parentsParent != null) {
if (parentBoxHeader.getId().equals(Mp4AtomIdentifier.META.getFieldName()) && parentsParent.getId().equals(Mp4AtomIdentifier.UDTA.getFieldName())) {
ilstNode = newAtom;
}
}
}
} else if (boxHeader.getId().equals(Mp4AtomIdentifier.FREE.getFieldName())) {
// Might be multiple in different locations
freeNodes.add(newAtom);
} else if (boxHeader.getId().equals(Mp4AtomIdentifier.TRAK.getFieldName())) {
// Might be multiple in different locations, although only one should be audio track
trakNodes.add(newAtom);
}
// For these atoms iterate down to build their children
if ((boxHeader.getId().equals(Mp4AtomIdentifier.TRAK.getFieldName())) || (boxHeader.getId().equals(Mp4AtomIdentifier.MDIA.getFieldName())) || (boxHeader.getId().equals(Mp4AtomIdentifier.MINF.getFieldName())) || (boxHeader.getId().equals(Mp4AtomIdentifier.STBL.getFieldName())) || (boxHeader.getId().equals(Mp4AtomIdentifier.UDTA.getFieldName())) || (boxHeader.getId().equals(Mp4AtomIdentifier.META.getFieldName())) || (boxHeader.getId().equals(Mp4AtomIdentifier.ILST.getFieldName()))) {
buildChildrenOfNode(moovBuffer, newAtom);
}
// Now adjust buffer for the next atom header at this level
moovBuffer.position(moovBuffer.position() + boxHeader.getDataLength());
}
}
moovBuffer.position(justAfterHeaderPos);
}
use of org.jaudiotagger.utils.tree.DefaultMutableTreeNode in project MusicDNA by harjot-oberai.
the class Mp4TagWriter method getMetaLevelFreeAtomSize.
/**
* Determine the size of the free atom immediately after ilst atom at the same level (if any), we can use this if
* ilst needs to grow or shrink because of more less metadata
*
* @param atomTree
* @return
*/
private int getMetaLevelFreeAtomSize(Mp4AtomTree atomTree) {
// Level 4 - Free
int oldMetaLevelFreeAtomSize;
oldMetaLevelFreeAtomSize = 0;
for (DefaultMutableTreeNode freeNode : atomTree.getFreeNodes()) {
DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) freeNode.getParent();
DefaultMutableTreeNode brotherNode = freeNode.getPreviousSibling();
if (!parentNode.isRoot()) {
Mp4BoxHeader parentHeader = ((Mp4BoxHeader) parentNode.getUserObject());
Mp4BoxHeader freeHeader = ((Mp4BoxHeader) freeNode.getUserObject());
// We are only interested in free atoms at this level if they come after the ilst node
if (brotherNode != null) {
Mp4BoxHeader brotherHeader = ((Mp4BoxHeader) brotherNode.getUserObject());
if (parentHeader.getId().equals(Mp4AtomIdentifier.META.getFieldName()) && brotherHeader.getId().equals(Mp4AtomIdentifier.ILST.getFieldName())) {
oldMetaLevelFreeAtomSize = freeHeader.getLength();
break;
}
}
}
}
return oldMetaLevelFreeAtomSize;
}
use of org.jaudiotagger.utils.tree.DefaultMutableTreeNode in project MusicDNA by harjot-oberai.
the class Mp4TagWriter method write.
/**
* Write tag to rafTemp file
*
* @param tag tag data
* @param raf current file
* @param rafTemp temporary file for writing
* @throws CannotWriteException
* @throws IOException
*/
public void write(Tag tag, RandomAccessFile raf, RandomAccessFile rafTemp) throws CannotWriteException, IOException {
logger.config("Started writing tag data");
// Read Channel for reading from old file
FileChannel fileReadChannel = raf.getChannel();
// Write channel for writing to new file
FileChannel fileWriteChannel = rafTemp.getChannel();
// TODO we shouldn'timer need all these variables, and some are very badly named - used by new and old methods
int oldIlstSize = 0;
int relativeIlstposition;
int startIlstWithinFile;
int newIlstSize;
int oldMetaLevelFreeAtomSize;
int topLevelFreePosition;
int topLevelFreeSize;
long endOfMoov = 0;
// Found top level free atom that comes after moov and before mdat, (also true if no free atom ?)
boolean topLevelFreeAtomComesBeforeMdatAtomAndAfterMetadata;
// Found top level free atom that comes between ftyp and moov
boolean topLevelFreeAtomComesBeforeMdatAndMetadata;
Mp4BoxHeader topLevelFreeHeader;
Mp4AtomTree atomTree;
// Build AtomTree
try {
atomTree = new Mp4AtomTree(raf, false);
} catch (CannotReadException cre) {
throw new CannotWriteException(cre.getMessage());
}
Mp4BoxHeader mdatHeader = atomTree.getBoxHeader(atomTree.getMdatNode());
// Unable to find audio so no chance of saving any changes
if (mdatHeader == null) {
throw new CannotWriteException(ErrorMessage.MP4_CHANGES_TO_FILE_FAILED_CANNOT_FIND_AUDIO.getMsg());
}
// Go through every field constructing the data that will appear starting from ilst box
ByteBuffer rawIlstData = tc.convert(tag);
rawIlstData.rewind();
newIlstSize = rawIlstData.limit();
// Moov Box header
Mp4BoxHeader moovHeader = atomTree.getBoxHeader(atomTree.getMoovNode());
long positionWithinFileAfterFindingMoovHeader = moovHeader.getFilePos() + Mp4BoxHeader.HEADER_LENGTH;
endOfMoov = moovHeader.getFilePos() + moovHeader.getLength();
Mp4StcoBox stco = atomTree.getStco();
Mp4BoxHeader ilstHeader = atomTree.getBoxHeader(atomTree.getIlstNode());
Mp4BoxHeader udtaHeader = atomTree.getBoxHeader(atomTree.getUdtaNode());
Mp4BoxHeader metaHeader = atomTree.getBoxHeader(atomTree.getMetaNode());
Mp4BoxHeader hdlrMetaHeader = atomTree.getBoxHeader(atomTree.getHdlrWithinMetaNode());
Mp4BoxHeader tagsHeader = atomTree.getBoxHeader(atomTree.getTagsNode());
Mp4BoxHeader trakHeader = atomTree.getBoxHeader(atomTree.getTrakNodes().get(0));
ByteBuffer moovBuffer = atomTree.getMoovBuffer();
// Udta
if (udtaHeader != null) {
// Meta
if (metaHeader != null) {
// ilst - record where ilst is,and where it ends
if (ilstHeader != null) {
oldIlstSize = ilstHeader.getLength();
// Relative means relative to moov buffer after moov header
startIlstWithinFile = (int) ilstHeader.getFilePos();
relativeIlstposition = (int) (startIlstWithinFile - (moovHeader.getFilePos() + Mp4BoxHeader.HEADER_LENGTH));
} else {
// Place ilst immediately after existing hdlr atom
if (hdlrMetaHeader != null) {
startIlstWithinFile = (int) hdlrMetaHeader.getFilePos() + hdlrMetaHeader.getLength();
relativeIlstposition = (int) (startIlstWithinFile - (moovHeader.getFilePos() + Mp4BoxHeader.HEADER_LENGTH));
} else // Place ilst after data fields in meta atom
// TODO Should we create a hdlr atom
{
startIlstWithinFile = (int) metaHeader.getFilePos() + Mp4BoxHeader.HEADER_LENGTH + Mp4MetaBox.FLAGS_LENGTH;
relativeIlstposition = (int) ((startIlstWithinFile) - (moovHeader.getFilePos() + Mp4BoxHeader.HEADER_LENGTH));
}
}
} else {
// There no ilst or meta header so we set to position where it would be if it existed
relativeIlstposition = moovHeader.getLength() - Mp4BoxHeader.HEADER_LENGTH;
startIlstWithinFile = (int) (moovHeader.getFilePos() + moovHeader.getLength());
}
} else // There no udta header so we are going to create a new structure, but we have to be aware that there might be
// an existing meta box structure in which case we preserve it but with our new structure before it.
{
// Create new structure just after the end of the trak atom
if (metaHeader != null) {
startIlstWithinFile = (int) trakHeader.getFilePos() + trakHeader.getLength();
relativeIlstposition = (int) (startIlstWithinFile - (moovHeader.getFilePos() + Mp4BoxHeader.HEADER_LENGTH));
} else {
// There no udta,ilst or meta header so we set to position where it would be if it existed
relativeIlstposition = moovHeader.getLength() - Mp4BoxHeader.HEADER_LENGTH;
startIlstWithinFile = (int) (moovHeader.getFilePos() + moovHeader.getLength());
}
}
// Find size of Level-4 Free atom (if any) immediately after ilst atom
oldMetaLevelFreeAtomSize = getMetaLevelFreeAtomSize(atomTree);
// Level-1 free atom
topLevelFreePosition = 0;
topLevelFreeSize = 0;
topLevelFreeAtomComesBeforeMdatAtomAndAfterMetadata = true;
topLevelFreeAtomComesBeforeMdatAndMetadata = false;
for (DefaultMutableTreeNode freeNode : atomTree.getFreeNodes()) {
DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) freeNode.getParent();
if (parentNode.isRoot()) {
topLevelFreeHeader = ((Mp4BoxHeader) freeNode.getUserObject());
topLevelFreeSize = topLevelFreeHeader.getLength();
topLevelFreePosition = (int) topLevelFreeHeader.getFilePos();
break;
}
}
if (topLevelFreeSize > 0) {
if (topLevelFreePosition > mdatHeader.getFilePos()) {
topLevelFreeAtomComesBeforeMdatAtomAndAfterMetadata = false;
} else if (topLevelFreePosition < moovHeader.getFilePos()) {
topLevelFreeAtomComesBeforeMdatAtomAndAfterMetadata = false;
topLevelFreeAtomComesBeforeMdatAndMetadata = true;
}
} else {
topLevelFreePosition = (int) mdatHeader.getFilePos();
}
logger.config("Read header successfully ready for writing");
// create a new file identical to first file but with replaced metadata
if (oldIlstSize == newIlstSize) {
logger.config("Writing:Option 1:Same Size");
writeMetadataSameSize(rawIlstData, oldIlstSize, startIlstWithinFile, fileReadChannel, fileWriteChannel, tagsHeader);
} else // no other changes necessary and total file size remains the same
if (oldIlstSize > newIlstSize) {
// after ilst as a child of meta
if (oldMetaLevelFreeAtomSize > 0) {
logger.config("Writing:Option 2:Smaller Size have free atom:" + oldIlstSize + ":" + newIlstSize);
writeDataUptoIncludingIlst(fileReadChannel, fileWriteChannel, oldIlstSize, startIlstWithinFile, rawIlstData);
// Write the modified free atom that comes after ilst
int newFreeSize = oldMetaLevelFreeAtomSize + (oldIlstSize - newIlstSize);
Mp4FreeBox newFreeBox = new Mp4FreeBox(newFreeSize - Mp4BoxHeader.HEADER_LENGTH);
fileWriteChannel.write(newFreeBox.getHeader().getHeaderData());
fileWriteChannel.write(newFreeBox.getData());
// Skip over the read channel old free atom
fileReadChannel.position(fileReadChannel.position() + oldMetaLevelFreeAtomSize);
writeDataAfterIlst(fileReadChannel, fileWriteChannel, tagsHeader);
} else // No free atom we need to create a new one or adjust top level free atom
{
int newFreeSize = (oldIlstSize - newIlstSize) - Mp4BoxHeader.HEADER_LENGTH;
// into account size of new header in calculating size of box
if (newFreeSize > 0) {
logger.config("Writing:Option 3:Smaller Size can create free atom");
writeDataUptoIncludingIlst(fileReadChannel, fileWriteChannel, oldIlstSize, startIlstWithinFile, rawIlstData);
// Create new free box
Mp4FreeBox newFreeBox = new Mp4FreeBox(newFreeSize);
fileWriteChannel.write(newFreeBox.getHeader().getHeaderData());
fileWriteChannel.write(newFreeBox.getData());
writeDataAfterIlst(fileReadChannel, fileWriteChannel, tagsHeader);
} else // Ok everything in this bit of tree has to be recalculated because eight or less bytes smaller
{
logger.config("Writing:Option 4:Smaller Size <=8 cannot create free atoms");
// Size will be this amount smaller
int sizeReducedBy = oldIlstSize - newIlstSize;
// Write stuff before Moov (ftyp)
fileReadChannel.position(0);
fileWriteChannel.transferFrom(fileReadChannel, 0, moovHeader.getFilePos());
fileWriteChannel.position(moovHeader.getFilePos());
// unless mdat is at start of file
if (mdatHeader.getFilePos() > moovHeader.getFilePos()) {
stco.adjustOffsets(-sizeReducedBy);
}
// Edit and rewrite the Moov,Udta and Meta header in moov buffer
adjustSizeOfMoovHeader(moovHeader, moovBuffer, -sizeReducedBy, udtaHeader, metaHeader);
fileWriteChannel.write(moovHeader.getHeaderData());
moovBuffer.rewind();
moovBuffer.limit(relativeIlstposition);
fileWriteChannel.write(moovBuffer);
// Now write ilst data
fileWriteChannel.write(rawIlstData);
fileReadChannel.position(startIlstWithinFile + oldIlstSize);
writeDataAfterIlst(fileReadChannel, fileWriteChannel, tagsHeader);
}
}
} else // Size of metadata has increased, the most complex situation, more atoms affected
{
int additionalSpaceRequiredForMetadata = newIlstSize - oldIlstSize;
// solution where can fit in data, but cant fit in free atom header
if (additionalSpaceRequiredForMetadata <= (oldMetaLevelFreeAtomSize - Mp4BoxHeader.HEADER_LENGTH)) {
int newFreeSize = oldMetaLevelFreeAtomSize - (additionalSpaceRequiredForMetadata);
logger.config("Writing:Option 5;Larger Size can use meta free atom need extra:" + newFreeSize + "bytes");
writeDataUptoIncludingIlst(fileReadChannel, fileWriteChannel, oldIlstSize, startIlstWithinFile, rawIlstData);
// Create an amended smaller freeBaos atom and write it to file
Mp4FreeBox newFreeBox = new Mp4FreeBox(newFreeSize - Mp4BoxHeader.HEADER_LENGTH);
fileWriteChannel.write(newFreeBox.getHeader().getHeaderData());
fileWriteChannel.write(newFreeBox.getData());
// Skip over the read channel old free atom
fileReadChannel.position(fileReadChannel.position() + oldMetaLevelFreeAtomSize);
writeDataAfterIlst(fileReadChannel, fileWriteChannel, tagsHeader);
} else // There is not enough padding in the metadata free atom anyway
// Size meta needs to be increased by (if not writing a free atom)
// Special Case this could actually be negative (upto -8)if is actually enough space but would
// not be able to write free atom properly, it doesn'timer matter the parent atoms would still
// need their sizes adjusted.
{
int additionalMetaSizeThatWontFitWithinMetaAtom = additionalSpaceRequiredForMetadata - (oldMetaLevelFreeAtomSize);
// Write stuff before Moov (ftyp)
fileReadChannel.position(0);
fileWriteChannel.transferFrom(fileReadChannel, 0, positionWithinFileAfterFindingMoovHeader - Mp4BoxHeader.HEADER_LENGTH);
fileWriteChannel.position(positionWithinFileAfterFindingMoovHeader - Mp4BoxHeader.HEADER_LENGTH);
if (udtaHeader == null) {
logger.config("Writing:Option 5.1;No udta atom");
Mp4HdlrBox hdlrBox = Mp4HdlrBox.createiTunesStyleHdlrBox();
Mp4MetaBox metaBox = Mp4MetaBox.createiTunesStyleMetaBox(hdlrBox.getHeader().getLength() + rawIlstData.limit());
udtaHeader = new Mp4BoxHeader(Mp4AtomIdentifier.UDTA.getFieldName());
udtaHeader.setLength(Mp4BoxHeader.HEADER_LENGTH + metaBox.getHeader().getLength());
additionalMetaSizeThatWontFitWithinMetaAtom = additionalMetaSizeThatWontFitWithinMetaAtom + (udtaHeader.getLength() - rawIlstData.limit());
// or special case of matching exactly the free atom plus header)
if ((!topLevelFreeAtomComesBeforeMdatAtomAndAfterMetadata) || ((topLevelFreeSize - Mp4BoxHeader.HEADER_LENGTH < additionalMetaSizeThatWontFitWithinMetaAtom) && (topLevelFreeSize != additionalMetaSizeThatWontFitWithinMetaAtom))) {
// by the amount mdat is going to be shifted
if (mdatHeader.getFilePos() > moovHeader.getFilePos()) {
logger.config("Adjusting Offsets");
stco.adjustOffsets(additionalMetaSizeThatWontFitWithinMetaAtom);
}
}
// Edit and rewrite the Moov header
moovHeader.setLength(moovHeader.getLength() + additionalMetaSizeThatWontFitWithinMetaAtom);
fileWriteChannel.write(moovHeader.getHeaderData());
moovBuffer.rewind();
moovBuffer.limit(relativeIlstposition);
fileWriteChannel.write(moovBuffer);
// Write new atoms required for holding metadata in itunes format
fileWriteChannel.write(udtaHeader.getHeaderData());
fileWriteChannel.write(metaBox.getHeader().getHeaderData());
fileWriteChannel.write(metaBox.getData());
fileWriteChannel.write(hdlrBox.getHeader().getHeaderData());
fileWriteChannel.write(hdlrBox.getData());
} else if (metaHeader == null) {
// #291:In this case we throwaway editing udta header and create a new one, would be beter if we coul
// keep the info but rather difficult.
logger.config("Writing:Option 5.2;No meta atom");
int oldUdtaHeaderLength = udtaHeader.getLength();
Mp4HdlrBox hdlrBox = Mp4HdlrBox.createiTunesStyleHdlrBox();
Mp4MetaBox metaBox = Mp4MetaBox.createiTunesStyleMetaBox(hdlrBox.getHeader().getLength() + rawIlstData.limit());
udtaHeader = new Mp4BoxHeader(Mp4AtomIdentifier.UDTA.getFieldName());
udtaHeader.setLength(Mp4BoxHeader.HEADER_LENGTH + metaBox.getHeader().getLength());
additionalMetaSizeThatWontFitWithinMetaAtom = additionalMetaSizeThatWontFitWithinMetaAtom + (udtaHeader.getLength() - rawIlstData.limit());
// or special case of matching exactly the free atom plus header)
if ((!topLevelFreeAtomComesBeforeMdatAtomAndAfterMetadata) || ((topLevelFreeSize - Mp4BoxHeader.HEADER_LENGTH < additionalMetaSizeThatWontFitWithinMetaAtom) && (topLevelFreeSize != additionalMetaSizeThatWontFitWithinMetaAtom))) {
// by the amount mdat is going to be shifted
if (mdatHeader.getFilePos() > moovHeader.getFilePos()) {
logger.config("Adjusting Offsets");
stco.adjustOffsets(additionalMetaSizeThatWontFitWithinMetaAtom);
}
}
// Edit and rewrite the Moov header
moovHeader.setLength(moovHeader.getLength() - oldUdtaHeaderLength + additionalMetaSizeThatWontFitWithinMetaAtom);
fileWriteChannel.write(moovHeader.getHeaderData());
moovBuffer.rewind();
moovBuffer.limit(relativeIlstposition - oldUdtaHeaderLength);
fileWriteChannel.write(moovBuffer);
// Write new atoms required for holding metadata in itunes format
fileWriteChannel.write(udtaHeader.getHeaderData());
fileWriteChannel.write(metaBox.getHeader().getHeaderData());
fileWriteChannel.write(metaBox.getData());
fileWriteChannel.write(hdlrBox.getHeader().getHeaderData());
fileWriteChannel.write(hdlrBox.getData());
} else {
logger.config("Writing:Option 5.3;udta atom exists");
// or special case of matching exactly the free atom plus header)
if ((!topLevelFreeAtomComesBeforeMdatAtomAndAfterMetadata) || ((topLevelFreeSize - Mp4BoxHeader.HEADER_LENGTH < additionalMetaSizeThatWontFitWithinMetaAtom) && (topLevelFreeSize != additionalMetaSizeThatWontFitWithinMetaAtom))) {
// by the amount mdat is going to be shifted
if (mdatHeader.getFilePos() > moovHeader.getFilePos()) {
stco.adjustOffsets(additionalMetaSizeThatWontFitWithinMetaAtom);
}
}
// Edit and rewrite the Moov header
adjustSizeOfMoovHeader(moovHeader, moovBuffer, additionalMetaSizeThatWontFitWithinMetaAtom, udtaHeader, metaHeader);
fileWriteChannel.write(moovHeader.getHeaderData());
// Now write from this edited buffer up until ilst atom
moovBuffer.rewind();
moovBuffer.limit(relativeIlstposition);
fileWriteChannel.write(moovBuffer);
}
// Now write ilst data
fileWriteChannel.write(rawIlstData);
// Skip over the read channel old meta level free atom because now used up
fileReadChannel.position(startIlstWithinFile + oldIlstSize);
fileReadChannel.position(fileReadChannel.position() + oldMetaLevelFreeAtomSize);
if (tagsHeader != null) {
// Write from after ilst upto tags atom
long writeBetweenIlstAndTags = tagsHeader.getFilePos() - fileReadChannel.position();
fileWriteChannel.transferFrom(fileReadChannel, fileWriteChannel.position(), writeBetweenIlstAndTags);
fileWriteChannel.position(fileWriteChannel.position() + writeBetweenIlstAndTags);
convertandWriteTagsAtomToFreeAtom(fileWriteChannel, tagsHeader);
// Write after tags atom upto end of moov
fileReadChannel.position(tagsHeader.getFilePos() + tagsHeader.getLength());
long extraData = endOfMoov - fileReadChannel.position();
fileWriteChannel.transferFrom(fileReadChannel, fileWriteChannel.position(), extraData);
} else {
// Now write the rest of children under moov which wont have changed
long extraData = endOfMoov - fileReadChannel.position();
fileWriteChannel.transferFrom(fileReadChannel, fileWriteChannel.position(), extraData);
fileWriteChannel.position(fileWriteChannel.position() + extraData);
}
// the free atom actually come after the the metadata
if (topLevelFreeAtomComesBeforeMdatAtomAndAfterMetadata && (topLevelFreePosition >= startIlstWithinFile)) {
// ok
if (topLevelFreeSize - Mp4BoxHeader.HEADER_LENGTH >= additionalMetaSizeThatWontFitWithinMetaAtom) {
logger.config("Writing:Option 6;Larger Size can use top free atom");
Mp4FreeBox freeBox = new Mp4FreeBox((topLevelFreeSize - Mp4BoxHeader.HEADER_LENGTH) - additionalMetaSizeThatWontFitWithinMetaAtom);
fileWriteChannel.write(freeBox.getHeader().getHeaderData());
fileWriteChannel.write(freeBox.getData());
// Skip over the read channel old free atom
fileReadChannel.position(fileReadChannel.position() + topLevelFreeSize);
// Write Mdat
writeDataInChunks(fileReadChannel, fileWriteChannel);
} else // we could just remove the header
if (topLevelFreeSize == additionalMetaSizeThatWontFitWithinMetaAtom) {
logger.config("Writing:Option 7;Larger Size uses top free atom including header");
// Skip over the read channel old free atom
fileReadChannel.position(fileReadChannel.position() + topLevelFreeSize);
// Write Mdat
writeDataInChunks(fileReadChannel, fileWriteChannel);
} else // Mdat is going to have to move anyway, so keep free atom as is and write it and mdat
// (have already updated stco above)
{
logger.config("Writing:Option 8;Larger Size cannot use top free atom");
fileWriteChannel.transferFrom(fileReadChannel, fileWriteChannel.position(), fileReadChannel.size() - fileReadChannel.position());
writeDataInChunks(fileReadChannel, fileWriteChannel);
}
} else {
logger.config("Writing:Option 9;Top Level Free comes after Mdat or before Metadata so cant use it");
writeDataInChunks(fileReadChannel, fileWriteChannel);
}
}
}
// Close all channels to original file
fileReadChannel.close();
raf.close();
checkFileWrittenCorrectly(rafTemp, mdatHeader, fileWriteChannel, stco);
}
Aggregations