/** * Seek for box with the specified id starting from the current location of filepointer, * * <p>Note it won't find the box if it is contained with a level below the current level, nor if * we are at a parent atom that also contains data and we havent yet processed the data. It will * work if we are at the start of a child box even if it not the required box as long as the box * we are looking for is the same level (or the level above in some cases). * * @param data * @param id * @throws java.io.IOException */ public static Mp4BoxHeader seekWithinLevel(ByteBuffer data, String id) throws IOException { logger.finer("Started searching for:" + id + " in bytebuffer at" + data.position()); Mp4BoxHeader boxHeader = new Mp4BoxHeader(); if (data.remaining() >= Mp4BoxHeader.HEADER_LENGTH) { boxHeader.update(data); } else { return null; } while (!boxHeader.getId().equals(id)) { logger.finer( "Found" + boxHeader.getId() + "Still searching for:" + id + " in bytebuffer at" + data.position()); // Something gone wrong probably not at the start of an atom so return null; if (boxHeader.getLength() < Mp4BoxHeader.HEADER_LENGTH) { return null; } data.position(data.position() + (boxHeader.getLength() - HEADER_LENGTH)); if (data.remaining() >= Mp4BoxHeader.HEADER_LENGTH) { boxHeader.update(data); } else { return null; } } return boxHeader; }
/** * Seek for box with the specified id starting from the current location of filepointer, * * <p>Note it wont find the box if it is contained with a level below the current level, nor if we * are at a parent atom that also contains data and we havent yet processed the data. It will work * if we are at the start of a child box even if it not the required box as long as the box we are * looking for is the same level (or the level above in some cases). * * @param raf * @param id * @throws java.io.IOException */ public static Mp4BoxHeader seekWithinLevel(RandomAccessFile raf, String id) throws IOException { logger.finer("Started searching for:" + id + " in file at:" + raf.getChannel().position()); Mp4BoxHeader boxHeader = new Mp4BoxHeader(); ByteBuffer headerBuffer = ByteBuffer.allocate(HEADER_LENGTH); int bytesRead = raf.getChannel().read(headerBuffer); if (bytesRead != HEADER_LENGTH) { return null; } headerBuffer.rewind(); boxHeader.update(headerBuffer); while (!boxHeader.getId().equals(id)) { logger.finer("Still searching for:" + id + " in file at:" + raf.getChannel().position()); // Something gone wrong probably not at the start of an atom so return null; if (boxHeader.getLength() < Mp4BoxHeader.HEADER_LENGTH) { return null; } int noOfBytesSkipped = raf.skipBytes(boxHeader.getDataLength()); logger.finer("Skipped:" + noOfBytesSkipped); if (noOfBytesSkipped < boxHeader.getDataLength()) { return null; } headerBuffer.rewind(); bytesRead = raf.getChannel().read(headerBuffer); logger.finer("Header Bytes Read:" + bytesRead); headerBuffer.rewind(); if (bytesRead == Mp4BoxHeader.HEADER_LENGTH) { boxHeader.update(headerBuffer); } else { return null; } } return boxHeader; }
/** * @param header parentHeader info * @param dataBuffer data of box (doesnt include parentHeader data) */ public Mp4NameBox(Mp4BoxHeader header, ByteBuffer dataBuffer) { this.header = header; // Double check if (!header.getId().equals(IDENTIFIER)) { throw new RuntimeException( "Unable to process name box because identifier is:" + header.getId()); } // Make slice so operations here don't effect position of main buffer this.dataBuffer = dataBuffer.slice(); // issuer this.name = Utils.getString( this.dataBuffer, PRE_DATA_LENGTH, header.getDataLength() - PRE_DATA_LENGTH, header.getEncoding()); }
/** * @param header parentHeader info * @param dataBuffer data of box (doesnt include parentHeader data) */ public Mp4DataBox(Mp4BoxHeader header, ByteBuffer dataBuffer) { this.header = header; // Double check if (!header.getId().equals(IDENTIFIER)) { throw new RuntimeException( "Unable to process data box because identifier is:" + header.getId()); } // Make slice so operations here don't effect position of main buffer this.dataBuffer = dataBuffer.slice(); // Type type = Utils.getIntBE( this.dataBuffer, Mp4DataBox.TYPE_POS, Mp4DataBox.TYPE_POS + Mp4DataBox.TYPE_LENGTH - 1); if (type == Mp4FieldType.TEXT.getFileClassId()) { content = Utils.getString( this.dataBuffer, PRE_DATA_LENGTH, header.getDataLength() - PRE_DATA_LENGTH, header.getEncoding()); } else if (type == Mp4FieldType.NUMERIC.getFileClassId()) { numbers = new ArrayList<Short>(); for (int i = 0; i < ((header.getDataLength() - PRE_DATA_LENGTH) / NUMBER_LENGTH); i++) { short number = Utils.getShortBE( this.dataBuffer, PRE_DATA_LENGTH + (i * NUMBER_LENGTH), PRE_DATA_LENGTH + (i * NUMBER_LENGTH) + (NUMBER_LENGTH - 1)); numbers.add(number); } // Make String representation (separate values with slash) StringBuffer sb = new StringBuffer(); ListIterator<Short> iterator = numbers.listIterator(); while (iterator.hasNext()) { sb.append(iterator.next()); if (iterator.hasNext()) { sb.append("/"); } } content = sb.toString(); } else if (type == Mp4FieldType.BYTE.getFileClassId()) { // TODO byte data length seems to be 1 for pgap and cpil but 2 for tmpo ? // Create String representation for display content = Utils.getIntBE(this.dataBuffer, PRE_DATA_LENGTH, header.getDataLength() - 1) + ""; // But store data for safer writng back to file bytedata = new byte[header.getDataLength() - PRE_DATA_LENGTH]; int pos = dataBuffer.position(); dataBuffer.position(pos + PRE_DATA_LENGTH); dataBuffer.get(bytedata); dataBuffer.position(pos); } else if (type == Mp4FieldType.COVERART_JPEG.getFileClassId()) { content = Utils.getString( this.dataBuffer, PRE_DATA_LENGTH, header.getDataLength() - PRE_DATA_LENGTH, header.getEncoding()); } }