• 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.codec.cts;
18 
19 import android.media.MediaFormat;
20 import android.util.Log;
21 
22 import java.io.IOException;
23 import java.io.RandomAccessFile;
24 
25 /**
26  * Writes an IVF file.
27  *
28  * IVF format is a simple container format for VP8 encoded frames defined at
29  * http://wiki.multimedia.cx/index.php?title=IVF.
30  */
31 
32 public class IvfWriter {
33     private static final String TAG = "IvfWriter";
34     private static final byte HEADER_END = 32;
35     private RandomAccessFile mOutputFile;
36     private int mWidth;
37     private int mHeight;
38     private int mScale;
39     private int mRate;
40     private int mFrameCount;
41     private String mMimeType;
42 
43     /**
44      * Initializes the IVF file writer.
45      *
46      * Timebase fraction is in format scale/rate, e.g. 1/1000
47      * Timestamp values supplied while writing frames should be in accordance
48      * with this timebase value.
49      *
50      * @param filename   name of the IVF file
51      * @param mimeType   mime type of the codec
52      * @param width      frame width
53      * @param height     frame height
54      * @param scale      timebase scale (or numerator of the timebase fraction)
55      * @param rate       timebase rate (or denominator of the timebase fraction)
56      */
IvfWriter( String filename, String mimeType, int width, int height, int scale, int rate)57     public IvfWriter(
58             String filename, String mimeType, int width, int height, int scale,
59             int rate) throws IOException {
60         mOutputFile = new RandomAccessFile(filename, "rw");
61         mMimeType = mimeType;
62         mWidth = width;
63         mHeight = height;
64         mScale = scale;
65         mRate = rate;
66         mFrameCount = 0;
67         mOutputFile.setLength(0);
68         mOutputFile.seek(HEADER_END);  // Skip the header for now, as framecount is unknown
69     }
70 
71     /**
72      * Initializes the IVF file writer with a microsecond timebase.
73      *
74      * Microsecond timebase is default for OMX thus stagefright.
75      *
76      * @param filename   name of the IVF file
77      * @param mimeType   mime type of the codec
78      * @param width      frame width
79      * @param height     frame height
80      */
IvfWriter(String filename, String mimeType, int width, int height)81     public IvfWriter(String filename, String mimeType, int width, int height) throws IOException {
82         this(filename, mimeType, width, height, 1, 1000000);
83     }
84 
85     /**
86      * Finalizes the IVF header and closes the file.
87      */
close()88     public void close() throws IOException{
89         // Write header now
90         mOutputFile.seek(0);
91         mOutputFile.write(makeIvfHeader(mFrameCount, mWidth, mHeight, mScale, mRate, mMimeType));
92         mOutputFile.close();
93     }
94 
95     /**
96      * Writes a single encoded VP8 frame with its frame header.
97      *
98      * @param frame     actual contents of the encoded frame data
99      * @param timeStamp timestamp of the frame (in accordance to specified timebase)
100      */
writeFrame(byte[] frame, long timeStamp)101     public void writeFrame(byte[] frame, long timeStamp) throws IOException {
102         mOutputFile.write(makeIvfFrameHeader(frame.length, timeStamp));
103         mOutputFile.write(frame);
104         mFrameCount++;
105     }
106 
getCodecFourcc(String mimeType)107     private static byte[] getCodecFourcc(String mimeType) {
108         switch(mimeType) {
109         case MediaFormat.MIMETYPE_VIDEO_AVC:
110             return new byte[] {'H', '2', '6', '4'};
111         case MediaFormat.MIMETYPE_VIDEO_HEVC:
112             return new byte[] {'H', 'E', 'V', 'C'};
113         case MediaFormat.MIMETYPE_VIDEO_VP8:
114             return new byte[] {'V', 'P', '8', '0'};
115         case MediaFormat.MIMETYPE_VIDEO_VP9:
116             return new byte[] {'V', 'P', '9', '0'};
117         case MediaFormat.MIMETYPE_VIDEO_AV1:
118             return new byte[] {'A', 'V', '0', '1'};
119         default:
120             Log.w(TAG, "Unexpected mimeType in getCodecFourcc: " + mimeType);
121             return new byte[] {'0', '0', '0', '0'};
122       }
123     }
124 
125     /**
126      * Makes a 32 byte file header for IVF format.
127      *
128      * Timebase fraction is in format scale/rate, e.g. 1/1000
129      *
130      * @param frameCount total number of frames file contains
131      * @param width      frame width
132      * @param height     frame height
133      * @param scale      timebase scale (or numerator of the timebase fraction)
134      * @param rate       timebase rate (or denominator of the timebase fraction)
135      */
makeIvfHeader( int frameCount, int width, int height, int scale, int rate, String mimeType)136     private static byte[] makeIvfHeader(
137             int frameCount, int width, int height, int scale, int rate, String mimeType) {
138         byte[] ivfHeader = new byte[32];
139         ivfHeader[0] = 'D';
140         ivfHeader[1] = 'K';
141         ivfHeader[2] = 'I';
142         ivfHeader[3] = 'F';
143         lay16Bits(ivfHeader, 4, 0);  // version
144         lay16Bits(ivfHeader, 6, 32);  // header size
145         byte[] codecFourcc = getCodecFourcc(mimeType);
146         ivfHeader[8] = codecFourcc[0];
147         ivfHeader[9] = codecFourcc[1];
148         ivfHeader[10] = codecFourcc[2];
149         ivfHeader[11] = codecFourcc[3];
150         lay16Bits(ivfHeader, 12, width);
151         lay16Bits(ivfHeader, 14, height);
152         lay32Bits(ivfHeader, 16, rate);  // scale/rate
153         lay32Bits(ivfHeader, 20, scale);
154         lay32Bits(ivfHeader, 24, frameCount);
155         lay32Bits(ivfHeader, 28, 0);  // unused
156         return ivfHeader;
157     }
158 
159     /**
160      * Makes a 12 byte header for an encoded frame.
161      *
162      * @param size      frame size
163      * @param timestamp presentation timestamp of the frame
164      */
makeIvfFrameHeader(int size, long timestamp)165     private static byte[] makeIvfFrameHeader(int size, long timestamp){
166         byte[] frameHeader = new byte[12];
167         lay32Bits(frameHeader, 0, size);
168         lay64bits(frameHeader, 4, timestamp);
169         return frameHeader;
170     }
171 
172 
173     /**
174      * Lays least significant 16 bits of an int into 2 items of a byte array.
175      *
176      * Note that ordering is little-endian.
177      *
178      * @param array     the array to be modified
179      * @param index     index of the array to start laying down
180      * @param value     the integer to use least significant 16 bits
181      */
lay16Bits(byte[] array, int index, int value)182     private static void lay16Bits(byte[] array, int index, int value){
183         array[index] = (byte) (value);
184         array[index + 1] = (byte) (value >> 8);
185     }
186 
187     /**
188      * Lays an int into 4 items of a byte array.
189      *
190      * Note that ordering is little-endian.
191      *
192      * @param array     the array to be modified
193      * @param index     index of the array to start laying down
194      * @param value     the integer to use
195      */
lay32Bits(byte[] array, int index, int value)196     private static void lay32Bits(byte[] array, int index, int value){
197         for (int i = 0; i < 4; i++){
198             array[index + i] = (byte) (value >> (i * 8));
199         }
200     }
201 
202     /**
203      * Lays a long int into 8 items of a byte array.
204      *
205      * Note that ordering is little-endian.
206      *
207      * @param array     the array to be modified
208      * @param index     index of the array to start laying down
209      * @param value     the integer to use
210      */
lay64bits(byte[] array, int index, long value)211     private static void lay64bits(byte[] array, int index, long value){
212         for (int i = 0; i < 8; i++){
213             array[index + i] = (byte) (value >> (i * 8));
214         }
215     }
216 }
217