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