| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.media.cts; |
| |
| import java.io.IOException; |
| import java.io.RandomAccessFile; |
| |
| /** |
| * A simple reader for an IVF file. |
| * |
| * IVF format is a simple container format for VP8 encoded frames defined at |
| * http://wiki.multimedia.cx/index.php?title=IVF. |
| * This reader is capable of getting frame count, width and height |
| * from the header, and access individual frames randomly by |
| * frame number. |
| */ |
| |
| public class IvfReader { |
| private static final byte HEADER_SIZE = 32; |
| private static final byte FOURCC_OFFSET = 8; |
| private static final byte WIDTH_OFFSET = 12; |
| private static final byte HEIGHT_OFFSET = 14; |
| private static final byte FRAMECOUNT_OFFSET = 24; |
| private static final byte FRAME_HEADER_SIZE = 12; |
| |
| private RandomAccessFile mIvfFile; |
| private boolean mHeaderValid; |
| private int mWidth; |
| private int mHeight; |
| private int mFrameCount; |
| private int[] mFrameHeads; // Head of frame header |
| private int[] mFrameSizes; // Frame size excluding header |
| |
| |
| /** |
| * Initializes the IVF file reader. |
| * |
| * Only minimal verification is done to check if this |
| * is indeed a valid IVF file. (fourcc, signature) |
| * |
| * All frame headers are read in advance. |
| * |
| * @param filename name of the IVF file |
| */ |
| public IvfReader(String filename) throws IOException{ |
| mIvfFile = new RandomAccessFile(filename, "r"); |
| |
| mHeaderValid = verifyHeader(); |
| readHeaderData(); |
| readFrameMetadata(); |
| } |
| |
| /** |
| * Tells if file header seems to be valid. |
| * |
| * Only minimal verification is done to check if this |
| * is indeed a valid IVF file. (fourcc, signature) |
| */ |
| public boolean isHeaderValid(){ |
| return mHeaderValid; |
| } |
| |
| /** |
| * Returns frame width according to header information. |
| */ |
| public int getWidth(){ |
| return mWidth; |
| } |
| |
| /** |
| * Returns frame height according to header information. |
| */ |
| public int getHeight(){ |
| return mHeight; |
| } |
| |
| /** |
| * Returns frame count according to header information. |
| */ |
| public int getFrameCount(){ |
| return mFrameCount; |
| } |
| |
| /** |
| * Returns frame data by index. |
| * |
| * @param frameIndex index of the frame to read, greater-equal |
| * than 0 and less than frameCount. |
| */ |
| public byte[] readFrame(int frameIndex) throws IOException { |
| if (frameIndex > mFrameCount || frameIndex < 0){ |
| return null; |
| } |
| int frameSize = mFrameSizes[frameIndex]; |
| int frameHead = mFrameHeads[frameIndex]; |
| |
| byte[] frame = new byte[frameSize]; |
| mIvfFile.seek(frameHead + FRAME_HEADER_SIZE); |
| mIvfFile.read(frame); |
| |
| return frame; |
| } |
| |
| /** |
| * Closes IVF file. |
| */ |
| public void close() throws IOException{ |
| mIvfFile.close(); |
| } |
| |
| private boolean verifyHeader() throws IOException{ |
| mIvfFile.seek(0); |
| |
| if (mIvfFile.length() < HEADER_SIZE){ |
| return false; |
| } |
| |
| // DKIF signature |
| boolean signatureMatch = ((mIvfFile.readByte() == (byte)'D') && |
| (mIvfFile.readByte() == (byte)'K') && |
| (mIvfFile.readByte() == (byte)'I') && |
| (mIvfFile.readByte() == (byte)'F')); |
| |
| // Fourcc |
| mIvfFile.seek(FOURCC_OFFSET); |
| boolean fourccMatch = ((mIvfFile.readByte() == (byte)'V') && |
| (mIvfFile.readByte() == (byte)'P') && |
| (mIvfFile.readByte() == (byte)'8') && |
| (mIvfFile.readByte() == (byte)'0')); |
| |
| return signatureMatch && fourccMatch; |
| } |
| |
| private void readHeaderData() throws IOException{ |
| // width |
| mIvfFile.seek(WIDTH_OFFSET); |
| mWidth = (int) changeEndianness(mIvfFile.readShort()); |
| |
| // height |
| mIvfFile.seek(HEIGHT_OFFSET); |
| mHeight = (int) changeEndianness(mIvfFile.readShort()); |
| |
| // frame count |
| mIvfFile.seek(FRAMECOUNT_OFFSET); |
| mFrameCount = changeEndianness(mIvfFile.readInt()); |
| |
| // allocate frame metadata |
| mFrameHeads = new int[mFrameCount]; |
| mFrameSizes = new int[mFrameCount]; |
| } |
| |
| private void readFrameMetadata() throws IOException{ |
| int frameHead = HEADER_SIZE; |
| for(int i = 0; i < mFrameCount; i++){ |
| mIvfFile.seek(frameHead); |
| int frameSize = changeEndianness(mIvfFile.readInt()); |
| mFrameHeads[i] = frameHead; |
| mFrameSizes[i] = frameSize; |
| // next frame |
| frameHead += FRAME_HEADER_SIZE + frameSize; |
| } |
| } |
| |
| private static short changeEndianness(short value){ |
| // Rationale for down-cast; |
| // Java Language specification 15.19: |
| // "The type of the shift expression is the promoted type of the left-hand operand." |
| // Java Language specification 5.6: |
| // "...if the operand is of compile-time type byte, short, or char, |
| // unary numeric promotion promotes it to a value of type int by a widening conversion." |
| return (short) (((value << 8) & 0XFF00) | ((value >> 8) & 0X00FF)); |
| } |
| |
| private static int changeEndianness(int value){ |
| return (((value << 24) & 0XFF000000) | |
| ((value << 8) & 0X00FF0000) | |
| ((value >> 8) & 0X0000FF00) | |
| ((value >> 24) & 0X000000FF)); |
| } |
| } |