├── README.md └── EncoderDecoderTestForMJPEG.java /README.md: -------------------------------------------------------------------------------- 1 | # EncoderDecoderTestForMJPEG 2 | 3 | ## Purpose 4 | 5 | This is a test application for JPEG/M-JPEG throug `MediaCodec`. This could also be a good tutorial of how to using 6 | `MediaCodec` `API` on Android. 7 | 8 | ## Requisites 9 | 10 | Before testing, you should put input frames in the external storage of your target. 11 | 12 | - For Encoder Test, put 10 YUV frames at `/storage/sdcard0/video.YUY2_`. 13 | - For Decoder Test, put 10 JPEG pictures at `/storage/sdcard0/video.jpeg_`. 14 | 15 | ## How to build, how to use 16 | 17 | This was written as if it were a CTS test, but is not part of CTS.It should be straightforward to adapt the code to 18 | other environments. 19 | 20 | 1 . Place the code 21 | 22 | You can put the `src` under `cts` directory of the whole android project. It looks Like: 23 | ``` 24 | /cts/tests/tests/media/src/android/media/cts/EncoderDecoderTestForMJPEG.java 25 | ``` 26 | 27 | 2 . Build the code 28 | 29 | Just build the whole `cts` test target, `EncoderDecoderTestForMJPEG.java` will be included in the `cts` target 30 | automatically. 31 | 32 | ``` 33 | make cts -j4 34 | ``` 35 | 36 | 3 . How to run the test 37 | 38 | Just like any other `cts` test cases, follow command: 39 | 40 | ``` 41 | run cts -c android.media.cts.EncoderDecoderTestForMJPEG -m 42 | ``` 43 | 44 | 4. Test result 45 | 46 | You will see a *PASS* if the test complete successfully. And 10 output frames in the same directory where you keep testing input. 47 | 48 | - For Encoder Test, since your input is like `video.YUY2_` so you will get `video.jpeg_`. 49 | - For Decoder Test, since your input is like `video.jpeg_` so you will get `video.YUY2_`. 50 | 51 | ## Current Status 52 | 53 | * The JPEG encoder can encoder one frame successfully. 54 | * Add test plans for different resolutions. @20150804 55 | * Change to feed 10 input buffers continuously, in this way, we can get multiple output buffers. @20150804 56 | 57 | 58 | ## Working in progress 59 | 60 | ~~1 . Fix infinite looping issue of JPEG encoder.~~ 61 | 62 | ~~2 . Add decoder test.~~ 63 | 64 | 3 . Fix the `info.flags` that can't got EOS flags from output `BufferInfo`. 65 | 66 | ~~4 . Fix run-time warnings.~~ 67 | 68 | ~~5 . Code revise needed.~~ 69 | 70 | 6 . Should handle Java `FileOutputStream`/`FileInputStream` nicely. 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /EncoderDecoderTestForMJPEG.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This was written as if it were a CTS test, but is not part of CTS. It should be straightforward 3 | * to adapt the code to other environments. 4 | * 5 | * It's enable YUVY422 pic buffer to JPEG using MediaCodec 6 | * 7 | * Author: Alan Wang 8 | * Date : 2015-07-10 9 | */ 10 | 11 | package android.media.cts; 12 | 13 | import android.media.MediaCodec; 14 | import android.media.MediaCodecInfo; 15 | import android.media.MediaCodecList; 16 | import android.media.MediaFormat; 17 | import android.os.Environment; 18 | import android.test.AndroidTestCase; 19 | import android.util.Log; 20 | 21 | import java.io.File; 22 | import java.io.FileInputStream; 23 | import java.io.FileOutputStream; 24 | import java.io.IOException; 25 | import java.nio.ByteBuffer; 26 | 27 | /** 28 | * 29 | * 30 | */ 31 | public class EncoderDecoderTestForMJPEG extends AndroidTestCase { 32 | private static final String LOG_TAG = "EncoderDecoderTestForMJPEG"; 33 | 34 | // Where to find the input 35 | // And Where to store the output 36 | private static final String INPUT_FILE_NAME_BASE = 37 | Environment.getExternalStorageDirectory() + "/video.YUY2"; 38 | private static final String OUTPUT_FILE_NAME_BASE = 39 | Environment.getExternalStorageDirectory() + "/video.jpeg"; 40 | 41 | // Parameters for encoder 42 | private static final String MIME_TYPE = "video/mjpeg"; 43 | 44 | private static final int TIME_OUT = 10000; //usec 45 | 46 | private static final int FRAME_RATE = 15; // 15fps, not really needed 47 | private static final int NUM_FRAMES = 10; // Num of frame to input for decode/encode 48 | private static final int IFRAME_INTERVAL = 10; // 10 secs between I-frames, not really needed 49 | 50 | 51 | private int mWidth = -1; 52 | private int mHeight = -1; 53 | private int mBitRate = -1; 54 | 55 | // Allocate one BufferInfo 56 | private MediaCodec.BufferInfo mBufferInfo; 57 | 58 | /** 59 | * Test Encoding of MJPEG from Buffer 60 | * 61 | */ 62 | public void testEncodeVideoFromBufToBuf1080p() throws Exception { 63 | Log.i(LOG_TAG, "Encode Video from Buffer To Buffer 1080p"); 64 | setParameters(1920, 1080, 6000000); 65 | encodeVideoFromBuffer(); 66 | } 67 | 68 | public void testEncodeVideoFromBufToBuf768p() throws Exception { 69 | Log.i(LOG_TAG, "Encode Video from Buffer To Buffer 768p"); 70 | setParameters(1024, 768, 6000000); 71 | encodeVideoFromBuffer(); 72 | } 73 | 74 | public void testEncodeVideoFromBufToBuf720p() throws Exception { 75 | Log.i(LOG_TAG, "Encode Video from Buffer To Buffer 720p"); 76 | setParameters(1280, 720, 6000000); 77 | encodeVideoFromBuffer(); 78 | } 79 | 80 | public void testEncodeVideoFromBufToBufD1() throws Exception { 81 | Log.i(LOG_TAG, "Encode Video from Buffer To Buffer D1"); 82 | setParameters(720, 480, 6000000); 83 | encodeVideoFromBuffer(); 84 | } 85 | 86 | public void testEncodeVideoFromBufToBuf384p() throws Exception { 87 | Log.i(LOG_TAG, "Encode Video from Buffer To Buffer Cross"); 88 | setParameters(512, 384, 6000000); 89 | encodeVideoFromBuffer(); 90 | } 91 | 92 | /** 93 | * Test Decoding of MJPEG from Buffer 94 | * 95 | */ 96 | public void testDecodeVideoFromBufToBuf1080p() throws Exception { 97 | Log.i(LOG_TAG, "Decode Video from Buffer to Buffer 1080p"); 98 | setParameters(1920, 1080, 6000000); 99 | decodeVideoFromBuffer(); 100 | } 101 | 102 | public void testDecodeVideoFromBufToBuf720p() throws Exception { 103 | Log.i(LOG_TAG, "Decode Video from Buffer to Buffer 720p"); 104 | setParameters(1280, 720, 6000000); 105 | decodeVideoFromBuffer(); 106 | } 107 | 108 | public void testDecodeVideoFromBufToBuf768p() throws Exception { 109 | Log.i(LOG_TAG, "Decode Video from Buffer to Buffer 768p"); 110 | setParameters(1024, 768, 6000000); 111 | decodeVideoFromBuffer(); 112 | } 113 | 114 | public void testDecodeVideoFromBufToBufD1() throws Exception { 115 | Log.i(LOG_TAG, "Decode Video from Buffer to Buffer D1"); 116 | setParameters(720, 480, 6000000); 117 | decodeVideoFromBuffer(); 118 | } 119 | 120 | public void testDecodeVideoFromBufToBuf384p() throws Exception { 121 | Log.i(LOG_TAG, "Decode Video from Buffer to Buffer 768p"); 122 | setParameters(512, 384, 6000000); 123 | decodeVideoFromBuffer(); 124 | } 125 | /** 126 | * Sets the desired frame size and bit rate. 127 | */ 128 | private void setParameters(int width, int height, int bitRate) { 129 | if ((width % 16) != 0 || (height % 16) != 0) { 130 | Log.w(LOG_TAG, "WARNING: width or height not multiple of 16"); 131 | } 132 | mWidth = width; 133 | mHeight = height; 134 | mBitRate = bitRate; 135 | } 136 | 137 | private void encodeVideoFromBuffer() throws Exception { 138 | MediaCodec encoder = null; 139 | 140 | try { 141 | MediaCodecInfo codecInfo = selectCodec(MIME_TYPE, true); 142 | if (codecInfo == null) { 143 | Log.e(LOG_TAG, "Unable to find an appropriate codec for " + MIME_TYPE); 144 | return; 145 | } 146 | 147 | Log.i(LOG_TAG, "Found codec: " + codecInfo.getName()); 148 | 149 | // JPEG encoder only support this colorFormat as Input 150 | // COLOR_FormatCbYCrY a.k.a Organized as 16bit UYVY 151 | int colorFormat = MediaCodecInfo.CodecCapabilities.COLOR_FormatCbYCrY; 152 | 153 | MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight); 154 | 155 | // Set some properties. Failing to specify some of these can cause the MediaCodec 156 | // configure() call to throw an unhelpful exception. 157 | format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat); 158 | format.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate); 159 | format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE); 160 | format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL); 161 | Log.i(LOG_TAG, "format: " + format); 162 | 163 | // Create a MediaCodec for the desired codec, then configure it as an encoder with 164 | // our desired properties. 165 | encoder = MediaCodec.createByCodecName(codecInfo.getName()); 166 | Log.i(LOG_TAG, "created Codec " + codecInfo.getName()); 167 | encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 168 | encoder.start(); 169 | 170 | doEncodeVideoFromBuffer(encoder, colorFormat); 171 | 172 | } finally { 173 | 174 | Log.i(LOG_TAG, "releasing encoder"); 175 | if (encoder != null) { 176 | encoder.stop(); 177 | encoder.release(); 178 | } 179 | 180 | } 181 | 182 | } 183 | 184 | private void decodeVideoFromBuffer() throws Exception { 185 | MediaCodec decoder = null; 186 | 187 | try { 188 | MediaCodecInfo codecInfo = selectCodec(MIME_TYPE, false); 189 | if (codecInfo == null) { 190 | Log.e(LOG_TAG, "Unable to find an appropriate codec for " + MIME_TYPE); 191 | return; 192 | } 193 | 194 | Log.i(LOG_TAG, "Found codec: " + codecInfo.getName()); 195 | 196 | // JPEG decoder only support this colorFormat as Output 197 | // COLOR_FormatCbYCrY a.k.a Organized as 16bit UYVY 198 | int colorFormat = MediaCodecInfo.CodecCapabilities.COLOR_FormatCbYCrY; 199 | 200 | MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight); 201 | 202 | // Create a MediaCodec for the desired codec, then configure it as an encoder with 203 | // our desired properties. 204 | decoder = MediaCodec.createByCodecName(codecInfo.getName()); 205 | Log.i(LOG_TAG, "created Codec " + codecInfo.getName()); 206 | decoder.configure(format, null, null, 0); 207 | decoder.start(); 208 | 209 | doDecodeVideoFromBuffer(decoder, colorFormat); 210 | 211 | } finally { 212 | 213 | Log.i(LOG_TAG, "releasing decoder"); 214 | if (decoder != null) { 215 | decoder.stop(); 216 | decoder.release(); 217 | } 218 | 219 | } 220 | 221 | } 222 | 223 | /** 224 | * Returns the first codec capable of encoding the specified MIME_TYPE 225 | */ 226 | private static MediaCodecInfo selectCodec(String mimeType, boolean needEncoder) { 227 | int numCodec = MediaCodecList.getCodecCount(); 228 | Log.i(LOG_TAG, "We got " + numCodec + " Codecs"); 229 | 230 | for (int i = 0; i < numCodec; i++) { 231 | MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); 232 | 233 | if (!codecInfo.isEncoder() && needEncoder) { 234 | continue; 235 | } 236 | 237 | String[] type = codecInfo.getSupportedTypes(); 238 | for (int j = 0; j < type.length; j++) { 239 | Log.i(LOG_TAG, "We got type " + type[j]); 240 | if (type[j].equalsIgnoreCase(mimeType)) { 241 | return codecInfo; 242 | } 243 | } 244 | 245 | } 246 | 247 | return null; 248 | } 249 | 250 | /** 251 | * Do the actual work for encoding frames from buffers of byte[]. 252 | * @param encoder 253 | * @param encoderColorFormat 254 | */ 255 | private void doEncodeVideoFromBuffer(MediaCodec encoder, int encoderColorFormat) throws IOException { 256 | Log.i(LOG_TAG, "In doEncodeVideoFromBuffer..."); 257 | 258 | int generateIndex = 0; 259 | int outPutIndex = 0; 260 | 261 | ByteBuffer[] encoderInputBuffers = encoder.getInputBuffers(); 262 | ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers(); 263 | 264 | MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 265 | 266 | // The size of a frame of video data, in the format we handle, is mWidth*mHeight*2 267 | byte[] frameData = new byte[mWidth * mHeight * 2]; 268 | 269 | // Encoded outputStream 270 | FileOutputStream outputStream = null; 271 | 272 | boolean inputDone = false; 273 | boolean encodeDone = false; 274 | boolean outputDone = false; 275 | 276 | while (!outputDone) { 277 | Log.i(LOG_TAG, "Looping... "); 278 | 279 | // If we're not done submitting frames, generate a new one and submit it. By 280 | // doing this on every loop we're working to ensure that the encoder always has 281 | // work to do. 282 | // 283 | // We don't really want a timeout here, but sometimes there's a delay opening 284 | // the encoder device, so a short timeout can keep us from spinning hard. 285 | if (!inputDone) { 286 | int inputBufIndex = encoder.dequeueInputBuffer(TIME_OUT); 287 | Log.d(LOG_TAG, "inputBufIndex = " + inputBufIndex); 288 | 289 | if (inputBufIndex >= 0) { 290 | long ptsUsec = computePTS(generateIndex); 291 | // Store input data into frameData 292 | generateFrame(generateIndex, encoderColorFormat, frameData, true, true); 293 | 294 | ByteBuffer inputBuf = encoderInputBuffers[inputBufIndex]; 295 | // Buf capacity check 296 | Log.i(LOG_TAG, "inputBuf.capacity() = " + inputBuf.capacity() 297 | + "frameData.length = " + frameData.length); 298 | assertTrue(inputBuf.capacity() >= frameData.length); 299 | inputBuf.clear(); 300 | inputBuf.put(frameData); 301 | 302 | encoder.queueInputBuffer(inputBufIndex, 0, frameData.length, ptsUsec, 0); 303 | Log.i(LOG_TAG, "Buffer " + inputBufIndex + " submitted to encode"); 304 | generateIndex++; 305 | } else { 306 | Log.w(LOG_TAG, "Input buffer not available"); 307 | } 308 | 309 | if (generateIndex >= NUM_FRAMES) { 310 | Log.d(LOG_TAG, "generateIndex = " + generateIndex + " Queuing EOS in InputBuffer"); 311 | //encoder.queueInputBuffer(inputBufIndex, 0, 0, 0, 312 | // MediaCodec.BUFFER_FLAG_END_OF_STREAM); 313 | inputDone = true; 314 | } 315 | } 316 | 317 | // Check for output from the encoder. If there's no output yet, we either need to 318 | // provide more input, or we need to wait for the encoder to work its magic. We 319 | // can't actually tell which is the case, so if we can't get an output buffer right 320 | // away we loop around and see if it wants more input. 321 | // 322 | // Once we get EOS from the encoder, we don't need to do this anymore. 323 | if (!encodeDone) { 324 | Log.d(LOG_TAG, "dequeuing Output buffers..."); 325 | int encoderStatus = encoder.dequeueOutputBuffer(info, TIME_OUT); 326 | if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { 327 | Log.i(LOG_TAG, "No output from encoder available, try again later."); 328 | } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 329 | Log.i(LOG_TAG, "INFO_OUTPUT_BUFFERS_CHANGED... NOT EXPECTED for and encoder"); 330 | } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 331 | Log.i(LOG_TAG, "INFO_OUTPUT_FORMAT_CHANGED... NOT EXPECTED for and encoder"); 332 | } else if (encoderStatus < 0) { 333 | Log.e(LOG_TAG, "Unexpected result!!!"); 334 | fail("encoder in wrong Status"); 335 | } else { // it's the index of an output buffer that has been successfully decoded 336 | 337 | Log.i(LOG_TAG, "Encode success, encoderStatus = " + encoderStatus); 338 | outputStream = new FileOutputStream(OUTPUT_FILE_NAME_BASE + "_" + outPutIndex); 339 | ByteBuffer encodedData = encoderOutputBuffers[encoderStatus]; 340 | if (encodedData == null) { 341 | fail("encoderOuputBuffer " + encoderStatus + " was null"); 342 | } 343 | 344 | encodedData.position(info.offset); 345 | encodedData.limit(info.offset + info.size); 346 | 347 | if (outputStream != null) { 348 | byte[] data = new byte[info.size]; 349 | encodedData.get(data); //Store input buffer into data 350 | encodedData.position(info.offset); 351 | 352 | Log.i(LOG_TAG, "outputStream writing data!"); 353 | outputStream.write(data); 354 | outPutIndex++; 355 | } 356 | 357 | encoder.releaseOutputBuffer(encoderStatus, false); 358 | 359 | //FIXME: Check info.flags is not working ??? 360 | if (outPutIndex >= NUM_FRAMES) { 361 | outputDone = true; 362 | encodeDone = true; 363 | } 364 | 365 | } 366 | } 367 | } 368 | } 369 | 370 | /** 371 | * Do the actual work for decoding frames from buffers of byte[]. 372 | * @param decoder 373 | * @param decoderColorFormat 374 | * 375 | */ 376 | private void doDecodeVideoFromBuffer(MediaCodec decoder, int decoderColorFormat) 377 | throws IOException { 378 | Log.i(LOG_TAG, "In doDecodeVideoFromBuffer ..."); 379 | 380 | // As for decoder, the input file is video.jpeg the output file is video.YUY2 381 | // which is oppsite with encoder 382 | String INPUT = OUTPUT_FILE_NAME_BASE; 383 | String OUTPUT = INPUT_FILE_NAME_BASE; 384 | 385 | int generateIndex = 0; 386 | int outPutIndex = 0; 387 | 388 | //File input = new File(INPUT + "_" + generateIndex); 389 | File input = null; 390 | byte[] frameData = null; 391 | 392 | ByteBuffer[] decoderInputBuffers = decoder.getInputBuffers(); 393 | 394 | MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 395 | 396 | // Decoded outputStream 397 | FileOutputStream outputStream = null; 398 | 399 | boolean inputDone = false; 400 | boolean decodeDone = false; 401 | boolean outputDone = false; 402 | 403 | while (!outputDone) { 404 | Log.i(LOG_TAG, "Loopping..."); 405 | 406 | // If we're not done submitting frames, generate a new one and submit it. By 407 | // doing this on every loop we're working to ensure that the encoder always has 408 | // work to do. 409 | // 410 | // We don't really want a timeout here, but sometimes there's a delay opening 411 | // the encoder device, so a short timeout can keep us from spinning hard. 412 | if (!inputDone) { 413 | int inputBufIndex = decoder.dequeueInputBuffer(TIME_OUT); 414 | Log.i(LOG_TAG, "inputBufIndex = " + inputBufIndex); 415 | 416 | if (inputBufIndex >= 0) { 417 | long ptsUsec = computePTS(generateIndex); 418 | 419 | input = new File(INPUT + "_" + generateIndex); 420 | frameData = new byte[(int)input.length()]; 421 | // Store input data into frameData 422 | generateFrame(generateIndex, decoderColorFormat, frameData, true, false); 423 | 424 | ByteBuffer inputBuf = decoderInputBuffers[inputBufIndex]; 425 | // Buf capacity check 426 | Log.i(LOG_TAG, "inputBuf.capacity() = " + inputBuf.capacity() 427 | + "frameData.length = " + frameData.length); 428 | assertTrue(inputBuf.capacity() >= frameData.length); 429 | inputBuf.clear(); 430 | inputBuf.put(frameData); 431 | 432 | decoder.queueInputBuffer(inputBufIndex, 0, frameData.length, ptsUsec, 0); 433 | Log.i(LOG_TAG, "Buffer " + inputBufIndex + " of len = " + frameData.length + " submitted to decode"); 434 | generateIndex++; 435 | 436 | } else { 437 | Log.w(LOG_TAG, "Input buffer not available"); 438 | } 439 | 440 | if(generateIndex >= NUM_FRAMES) { 441 | // We have enough frames, so send an empty one with EOS flag 442 | Log.d(LOG_TAG, "generateIndex = " + generateIndex + "Queuing EOS in InputBuffer"); 443 | //decoder.queueInputBuffer(inputBufIndex, 0, 0, 0, 444 | // MediaCodec.BUFFER_FLAG_END_OF_STREAM); 445 | inputDone = true; 446 | } 447 | } 448 | 449 | // Check for output from the decoder. We want to do this on every loop to avoid 450 | // the possibility of stalling the pipeline. We use a short timeout to avoid 451 | // burning CPU if the decoder is hard at work but the next frame isn't quite ready. 452 | // 453 | // If we're decoding to a Surface, we'll get notified here as usual but the 454 | // ByteBuffer references will be null. The data is sent to Surface instead. 455 | if (!decodeDone) { 456 | Log.d(LOG_TAG, "dequeuing Output buffers..."); 457 | int decoderStatus = decoder.dequeueOutputBuffer(info, TIME_OUT); 458 | if (decoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { 459 | Log.i(LOG_TAG, "No output from decoder available, try again later."); 460 | } else if (decoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 461 | Log.i(LOG_TAG, "INFO_OUTPUT_BUFFERS_CHANGED... NOT EXPECTED for and decoder"); 462 | } else if (decoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 463 | Log.i(LOG_TAG, "INFO_OUTPUT_FORMAT_CHANGED... NOT EXPECTED for and decoder"); 464 | } else if (decoderStatus < 0) { 465 | Log.e(LOG_TAG, "Unexpected result!!!"); 466 | fail("decoder in wrong Status"); 467 | } else { // it's the index of an output buffer that has been successfully decoded 468 | Log.i(LOG_TAG, "Decode success, decoderStatus = " + decoderStatus + " info.size = " + info.size); 469 | 470 | ByteBuffer[] decoderOutputBuffers = decoder.getOutputBuffers(); 471 | ByteBuffer decodedData = decoderOutputBuffers[decoderStatus]; 472 | 473 | if (decodedData == null) { 474 | fail("decoderOuputBuffer " + decoderStatus + " was null"); 475 | } 476 | 477 | outputStream = new FileOutputStream(OUTPUT + "_" + outPutIndex); 478 | 479 | decodedData.position(info.offset); 480 | decodedData.limit(info.offset + info.size); 481 | 482 | if (outputStream != null) { 483 | byte[] data = new byte[info.size]; 484 | decodedData.get(data); //Store input buffer into data 485 | decodedData.position(info.offset); 486 | 487 | if (info.size > 0) { 488 | Log.i(LOG_TAG, "outputStream writing data! size = " + info.size); 489 | outputStream.write(data); 490 | } 491 | outPutIndex++; 492 | } 493 | 494 | if (outPutIndex >= NUM_FRAMES) { 495 | outputDone = true; 496 | decodeDone = true; 497 | } 498 | 499 | decoder.releaseOutputBuffer(decoderStatus, false); 500 | 501 | } 502 | } 503 | } 504 | 505 | } 506 | 507 | private long computePTS(int generateIndex) { 508 | return 132 + generateIndex * 1000000 / FRAME_RATE; 509 | } 510 | 511 | /** 512 | * 513 | * @param frameIndex 514 | * @param colorFormat 515 | * @param frameData 516 | * @param readFromDisk true: the data is read file the yuv file 517 | * [WIP]false: the data is generated like: 518 | * 519 | * Generates data for frame N into the supplied buffer. We have an 8-frame animation 520 | * sequence that wraps around. It looks like this: 521 | *
522 |      *   0 1 2 3
523 |      *   7 6 5 4
524 |      * 
525 | * We draw one of the eight rectangles and leave the rest set to the zero-fill color. 526 | */ 527 | private void generateFrame(int frameIndex, int colorFormat, 528 | byte[] frameData, boolean readFromDisk, 529 | boolean encoder) { 530 | 531 | if (frameIndex >= NUM_FRAMES) { 532 | fail("frameIndex is overflow..."); 533 | } 534 | 535 | FileInputStream fileInputStream = null; 536 | File file = null; 537 | 538 | // If it's decoder the OUTPUT_FILE name is as INPUT 539 | if (encoder) { 540 | file = new File(INPUT_FILE_NAME_BASE + "_" + frameIndex); 541 | } else { 542 | file = new File(OUTPUT_FILE_NAME_BASE + "_" + frameIndex); 543 | } 544 | 545 | Log.i(LOG_TAG, "file.length = " + file.length()); 546 | 547 | 548 | if (readFromDisk) { 549 | 550 | try { 551 | //convert file into array of bytes 552 | fileInputStream = new FileInputStream(file); 553 | fileInputStream.read(frameData); 554 | fileInputStream.close(); 555 | 556 | } catch (Exception e) { 557 | e.printStackTrace(); 558 | } 559 | 560 | } else { 561 | //WIP: Read from surface 562 | Log.w(LOG_TAG, "Not implement so far..."); 563 | } 564 | 565 | 566 | } 567 | 568 | 569 | } 570 | --------------------------------------------------------------------------------