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