• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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