1 /* 2 * Copyright (C) 2013 The Android Open Source Project 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 android.media.cts; 18 19 import java.io.IOException; 20 import java.io.RandomAccessFile; 21 22 /** 23 * A simple reader for an IVF file. 24 * 25 * IVF format is a simple container format for VP8 encoded frames defined at 26 * http://wiki.multimedia.cx/index.php?title=IVF. 27 * This reader is capable of getting frame count, width and height 28 * from the header, and access individual frames randomly by 29 * frame number. 30 */ 31 32 public class IvfReader { 33 private static final byte HEADER_SIZE = 32; 34 private static final byte FOURCC_OFFSET = 8; 35 private static final byte WIDTH_OFFSET = 12; 36 private static final byte HEIGHT_OFFSET = 14; 37 private static final byte FRAMECOUNT_OFFSET = 24; 38 private static final byte FRAME_HEADER_SIZE = 12; 39 40 private RandomAccessFile mIvfFile; 41 private boolean mHeaderValid; 42 private int mWidth; 43 private int mHeight; 44 private int mFrameCount; 45 private int[] mFrameHeads; // Head of frame header 46 private int[] mFrameSizes; // Frame size excluding header 47 48 49 /** 50 * Initializes the IVF file reader. 51 * 52 * Only minimal verification is done to check if this 53 * is indeed a valid IVF file. (fourcc, signature) 54 * 55 * All frame headers are read in advance. 56 * 57 * @param filename name of the IVF file 58 */ IvfReader(String filename)59 public IvfReader(String filename) throws IOException{ 60 mIvfFile = new RandomAccessFile(filename, "r"); 61 62 mHeaderValid = verifyHeader(); 63 readHeaderData(); 64 readFrameMetadata(); 65 } 66 67 /** 68 * Tells if file header seems to be valid. 69 * 70 * Only minimal verification is done to check if this 71 * is indeed a valid IVF file. (fourcc, signature) 72 */ isHeaderValid()73 public boolean isHeaderValid(){ 74 return mHeaderValid; 75 } 76 77 /** 78 * Returns frame width according to header information. 79 */ getWidth()80 public int getWidth(){ 81 return mWidth; 82 } 83 84 /** 85 * Returns frame height according to header information. 86 */ getHeight()87 public int getHeight(){ 88 return mHeight; 89 } 90 91 /** 92 * Returns frame count according to header information. 93 */ getFrameCount()94 public int getFrameCount(){ 95 return mFrameCount; 96 } 97 98 /** 99 * Returns frame data by index. 100 * 101 * @param frameIndex index of the frame to read, greater-equal 102 * than 0 and less than frameCount. 103 */ readFrame(int frameIndex)104 public byte[] readFrame(int frameIndex) throws IOException { 105 if (frameIndex > mFrameCount || frameIndex < 0){ 106 return null; 107 } 108 int frameSize = mFrameSizes[frameIndex]; 109 int frameHead = mFrameHeads[frameIndex]; 110 111 byte[] frame = new byte[frameSize]; 112 mIvfFile.seek(frameHead + FRAME_HEADER_SIZE); 113 mIvfFile.read(frame); 114 115 return frame; 116 } 117 118 /** 119 * Closes IVF file. 120 */ close()121 public void close() throws IOException{ 122 mIvfFile.close(); 123 } 124 verifyHeader()125 private boolean verifyHeader() throws IOException{ 126 mIvfFile.seek(0); 127 128 if (mIvfFile.length() < HEADER_SIZE){ 129 return false; 130 } 131 132 // DKIF signature 133 boolean signatureMatch = ((mIvfFile.readByte() == (byte)'D') && 134 (mIvfFile.readByte() == (byte)'K') && 135 (mIvfFile.readByte() == (byte)'I') && 136 (mIvfFile.readByte() == (byte)'F')); 137 138 // Fourcc 139 mIvfFile.seek(FOURCC_OFFSET); 140 boolean fourccMatch = ((mIvfFile.readByte() == (byte)'V') && 141 (mIvfFile.readByte() == (byte)'P') && 142 (mIvfFile.readByte() == (byte)'8') && 143 (mIvfFile.readByte() == (byte)'0')); 144 145 return signatureMatch && fourccMatch; 146 } 147 readHeaderData()148 private void readHeaderData() throws IOException{ 149 // width 150 mIvfFile.seek(WIDTH_OFFSET); 151 mWidth = (int) changeEndianness(mIvfFile.readShort()); 152 153 // height 154 mIvfFile.seek(HEIGHT_OFFSET); 155 mHeight = (int) changeEndianness(mIvfFile.readShort()); 156 157 // frame count 158 mIvfFile.seek(FRAMECOUNT_OFFSET); 159 mFrameCount = changeEndianness(mIvfFile.readInt()); 160 161 // allocate frame metadata 162 mFrameHeads = new int[mFrameCount]; 163 mFrameSizes = new int[mFrameCount]; 164 } 165 readFrameMetadata()166 private void readFrameMetadata() throws IOException{ 167 int frameHead = HEADER_SIZE; 168 for(int i = 0; i < mFrameCount; i++){ 169 mIvfFile.seek(frameHead); 170 int frameSize = changeEndianness(mIvfFile.readInt()); 171 mFrameHeads[i] = frameHead; 172 mFrameSizes[i] = frameSize; 173 // next frame 174 frameHead += FRAME_HEADER_SIZE + frameSize; 175 } 176 } 177 changeEndianness(short value)178 private static short changeEndianness(short value){ 179 // Rationale for down-cast; 180 // Java Language specification 15.19: 181 // "The type of the shift expression is the promoted type of the left-hand operand." 182 // Java Language specification 5.6: 183 // "...if the operand is of compile-time type byte, short, or char, 184 // unary numeric promotion promotes it to a value of type int by a widening conversion." 185 return (short) (((value << 8) & 0XFF00) | ((value >> 8) & 0X00FF)); 186 } 187 changeEndianness(int value)188 private static int changeEndianness(int value){ 189 return (((value << 24) & 0XFF000000) | 190 ((value << 8) & 0X00FF0000) | 191 ((value >> 8) & 0X0000FF00) | 192 ((value >> 24) & 0X000000FF)); 193 } 194 } 195