1 /* 2 * Copyright 2018 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 18 #define LOG_TAG "MediaHelper" 19 20 #include "MediaTestHelpers.h" 21 22 #include <android/asset_manager.h> 23 #include <android/asset_manager_jni.h> 24 #include <android/log.h> 25 #include <string.h> 26 #include <unistd.h> 27 28 #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 29 30 #define ASSERT(a) \ 31 if (!(a)) { \ 32 ALOGE("Failure: " #a " at " __FILE__ ":%d", __LINE__); \ 33 return false; \ 34 } 35 36 #define MEDIA_CALL(a) ASSERT(AMEDIA_OK == (a)) 37 38 MediaHelper::~MediaHelper() { 39 if (mCodec != nullptr) { 40 AMediaCodec_stop(mCodec); 41 AMediaCodec_delete(mCodec); 42 mCodec = nullptr; 43 } 44 if (mExtractor != nullptr) { 45 AMediaExtractor_delete(mExtractor); 46 mExtractor = nullptr; 47 } 48 } 49 50 bool MediaHelper::init(JNIEnv *env, jobject assetMgr, jstring jfilename, 51 ANativeWindow *window) { 52 // Create a media extractor which will provide codec data from jfilename. 53 ASSERT(createExtractor(env, assetMgr, jfilename)); 54 // Create a media codec and image reader which receives frames from the 55 // codec. 56 ASSERT(createMediaCodec(window)); 57 58 return true; 59 } 60 61 bool MediaHelper::processOneFrame() { 62 ASSERT(mCodec); 63 64 // Wait until the codec produces an output buffer, which we can read as 65 // an image. 66 bool gotOutputBuffer = false; 67 do { 68 ASSERT(processOneInputBuffer()); 69 ASSERT(processOneOutputBuffer(&gotOutputBuffer)); 70 } while (!gotOutputBuffer); 71 72 return true; 73 } 74 75 bool MediaHelper::createExtractor(JNIEnv *env, jobject assetMgr, 76 jstring jfilename) { 77 ASSERT(nullptr == mExtractor); 78 const char *filename = env->GetStringUTFChars(jfilename, NULL); 79 ASSERT(filename); 80 81 struct ReleaseFd { 82 ~ReleaseFd() { 83 if (fd >= 0) 84 close(fd); 85 } 86 87 int fd = -1; 88 } releaseFd; 89 90 off_t outStart, outLen; 91 releaseFd.fd = AAsset_openFileDescriptor( 92 AAssetManager_open(AAssetManager_fromJava(env, assetMgr), filename, 0), 93 &outStart, &outLen); 94 env->ReleaseStringUTFChars(jfilename, filename); 95 ASSERT(releaseFd.fd >= 0); 96 97 mExtractor = AMediaExtractor_new(); 98 MEDIA_CALL(AMediaExtractor_setDataSourceFd(mExtractor, releaseFd.fd, 99 static_cast<off64_t>(outStart), 100 static_cast<off64_t>(outLen))); 101 return true; 102 } 103 104 bool MediaHelper::createMediaCodec(ANativeWindow *window) { 105 ASSERT(mExtractor); 106 ASSERT(nullptr == mCodec); 107 108 // Create a mCodec from the video track. 109 int numTracks = AMediaExtractor_getTrackCount(mExtractor); 110 AMediaFormat *format; 111 const char *mime; 112 for (int i = 0; i < numTracks; ++i) { 113 format = AMediaExtractor_getTrackFormat(mExtractor, i); 114 ASSERT(AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)); 115 116 if (!strncmp(mime, "video/", 6)) { 117 MEDIA_CALL(AMediaExtractor_selectTrack(mExtractor, i)); 118 break; 119 } 120 121 AMediaFormat_delete(format); 122 } 123 124 mCodec = AMediaCodec_createDecoderByType(mime); 125 ASSERT(mCodec); 126 MEDIA_CALL(AMediaCodec_configure(mCodec, format, window, NULL, 0)); 127 AMediaFormat_delete(format); 128 MEDIA_CALL(AMediaCodec_start(mCodec)); 129 130 return true; 131 } 132 133 bool MediaHelper::processOneInputBuffer() { 134 ssize_t bufferIndex = AMediaCodec_dequeueInputBuffer(mCodec, 2000); 135 // No input buffer ready, just return, we'll try again. 136 if (bufferIndex < 0) 137 return true; 138 139 size_t bufferSize = 0; 140 uint8_t *buffer = 141 AMediaCodec_getInputBuffer(mCodec, bufferIndex, &bufferSize); 142 ASSERT(buffer); 143 144 ssize_t sampleSize = 145 AMediaExtractor_readSampleData(mExtractor, buffer, bufferSize); 146 ASSERT(sampleSize >= 0); 147 148 auto presentationTimeUs = AMediaExtractor_getSampleTime(mExtractor); 149 MEDIA_CALL(AMediaCodec_queueInputBuffer(mCodec, bufferIndex, 0, sampleSize, 150 presentationTimeUs, 0)); 151 152 ASSERT(AMediaExtractor_advance(mExtractor)); 153 154 return true; 155 } 156 157 // Returns true if we successfully got an output buffer. 158 bool MediaHelper::processOneOutputBuffer(bool *didProcessBuffer) { 159 *didProcessBuffer = false; 160 161 AMediaCodecBufferInfo info; 162 ssize_t index = AMediaCodec_dequeueOutputBuffer(mCodec, &info, 0); 163 if (index < 0) { 164 switch (index) { 165 case AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED: 166 case AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED: 167 case AMEDIACODEC_INFO_TRY_AGAIN_LATER: 168 return true; 169 default: 170 return false; 171 } 172 } 173 174 *didProcessBuffer = true; 175 MEDIA_CALL(AMediaCodec_releaseOutputBuffer(mCodec, index, info.size != 0)); 176 return true; 177 } 178