1 /* 2 * Copyright 2008 CoreMedia AG, Hamburg 3 * 4 * Licensed under the Apache License, Version 2.0 (the License); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an AS IS BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.coremedia.iso; 18 19 import com.googlecode.mp4parser.AbstractContainerBox; 20 import com.coremedia.iso.boxes.Box; 21 import com.coremedia.iso.boxes.MovieBox; 22 import com.googlecode.mp4parser.annotations.DoNotParseDetail; 23 24 import java.io.*; 25 import java.nio.ByteBuffer; 26 import java.nio.channels.FileChannel; 27 import java.nio.channels.ReadableByteChannel; 28 import java.nio.channels.WritableByteChannel; 29 30 /** 31 * The most upper container for ISO Boxes. It is a container box that is a file. 32 * Uses IsoBufferWrapper to access the underlying file. 33 */ 34 @DoNotParseDetail 35 public class IsoFile extends AbstractContainerBox implements Closeable { 36 protected BoxParser boxParser = new PropertyBoxParserImpl(); 37 ReadableByteChannel byteChannel; 38 IsoFile()39 public IsoFile() { 40 super(""); 41 } 42 IsoFile(File f)43 public IsoFile(File f) throws IOException { 44 super(""); 45 this.byteChannel = new FileInputStream(f).getChannel(); 46 boxParser = createBoxParser(); 47 parse(); 48 } 49 IsoFile(ReadableByteChannel byteChannel)50 public IsoFile(ReadableByteChannel byteChannel) throws IOException { 51 super(""); 52 this.byteChannel = byteChannel; 53 boxParser = createBoxParser(); 54 parse(); 55 } 56 IsoFile(ReadableByteChannel byteChannel, BoxParser boxParser)57 public IsoFile(ReadableByteChannel byteChannel, BoxParser boxParser) throws IOException { 58 super(""); 59 this.byteChannel = byteChannel; 60 this.boxParser = boxParser; 61 parse(); 62 63 64 } 65 createBoxParser()66 protected BoxParser createBoxParser() { 67 return new PropertyBoxParserImpl(); 68 } 69 70 71 @Override _parseDetails(ByteBuffer content)72 public void _parseDetails(ByteBuffer content) { 73 // there are no details to parse we should be just file 74 } 75 parse(ReadableByteChannel inFC, ByteBuffer header, long contentSize, AbstractBoxParser abstractBoxParser)76 public void parse(ReadableByteChannel inFC, ByteBuffer header, long contentSize, AbstractBoxParser abstractBoxParser) throws IOException { 77 throw new IOException("This method is not meant to be called. Use #parse() directly."); 78 } 79 parse()80 private void parse() throws IOException { 81 82 boolean done = false; 83 while (!done) { 84 try { 85 Box box = boxParser.parseBox(byteChannel, this); 86 if (box != null) { 87 // System.err.println(box.getType()); 88 boxes.add(box); 89 } else { 90 done = true; 91 } 92 } catch (EOFException e) { 93 done = true; 94 } 95 } 96 } 97 98 @DoNotParseDetail toString()99 public String toString() { 100 StringBuilder buffer = new StringBuilder(); 101 buffer.append("IsoFile["); 102 if (boxes == null) { 103 buffer.append("unparsed"); 104 } else { 105 for (int i = 0; i < boxes.size(); i++) { 106 if (i > 0) { 107 buffer.append(";"); 108 } 109 buffer.append(boxes.get(i).toString()); 110 } 111 } 112 buffer.append("]"); 113 return buffer.toString(); 114 } 115 116 @DoNotParseDetail fourCCtoBytes(String fourCC)117 public static byte[] fourCCtoBytes(String fourCC) { 118 byte[] result = new byte[4]; 119 if (fourCC != null) { 120 for (int i = 0; i < Math.min(4, fourCC.length()); i++) { 121 result[i] = (byte) fourCC.charAt(i); 122 } 123 } 124 return result; 125 } 126 127 @DoNotParseDetail bytesToFourCC(byte[] type)128 public static String bytesToFourCC(byte[] type) { 129 byte[] result = new byte[]{0, 0, 0, 0}; 130 if (type != null) { 131 System.arraycopy(type, 0, result, 0, Math.min(type.length, 4)); 132 } 133 try { 134 return new String(result, "ISO-8859-1"); 135 } catch (UnsupportedEncodingException e) { 136 throw new Error("Required character encoding is missing", e); 137 } 138 } 139 140 141 @Override getNumOfBytesToFirstChild()142 public long getNumOfBytesToFirstChild() { 143 return 0; 144 } 145 146 @Override getSize()147 public long getSize() { 148 long size = 0; 149 for (Box box : boxes) { 150 size += box.getSize(); 151 } 152 return size; 153 } 154 155 @Override getIsoFile()156 public IsoFile getIsoFile() { 157 return this; 158 } 159 160 161 /** 162 * Shortcut to get the MovieBox since it is often needed and present in 163 * nearly all ISO 14496 files (at least if they are derived from MP4 ). 164 * 165 * @return the MovieBox or <code>null</code> 166 */ 167 @DoNotParseDetail getMovieBox()168 public MovieBox getMovieBox() { 169 for (Box box : boxes) { 170 if (box instanceof MovieBox) { 171 return (MovieBox) box; 172 } 173 } 174 return null; 175 } 176 getBox(WritableByteChannel os)177 public void getBox(WritableByteChannel os) throws IOException { 178 for (Box box : boxes) { 179 180 if (os instanceof FileChannel) { 181 long startPos = ((FileChannel) os).position(); 182 box.getBox(os); 183 long size = ((FileChannel) os).position() - startPos; 184 assert size == box.getSize(); 185 } else { 186 box.getBox(os); 187 } 188 189 } 190 } 191 close()192 public void close() throws IOException { 193 this.byteChannel.close(); 194 } 195 } 196