Andreas Huber | 35213f1 | 2012-08-29 11:41:50 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2012, 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 | //#define LOG_NDEBUG 0 |
| 18 | #define LOG_TAG "Converter" |
| 19 | #include <utils/Log.h> |
| 20 | |
| 21 | #include "Converter.h" |
| 22 | |
Andreas Huber | 13da4ed | 2012-09-12 14:06:17 -0700 | [diff] [blame] | 23 | #include <cutils/properties.h> |
Andreas Huber | 35213f1 | 2012-08-29 11:41:50 -0700 | [diff] [blame] | 24 | #include <gui/SurfaceTextureClient.h> |
| 25 | #include <media/ICrypto.h> |
| 26 | #include <media/stagefright/foundation/ABuffer.h> |
| 27 | #include <media/stagefright/foundation/ADebug.h> |
| 28 | #include <media/stagefright/foundation/AMessage.h> |
| 29 | #include <media/stagefright/MediaCodec.h> |
| 30 | #include <media/stagefright/MediaDefs.h> |
| 31 | #include <media/stagefright/MediaErrors.h> |
| 32 | |
| 33 | namespace android { |
| 34 | |
| 35 | Converter::Converter( |
| 36 | const sp<AMessage> ¬ify, |
| 37 | const sp<ALooper> &codecLooper, |
| 38 | const sp<AMessage> &format) |
| 39 | : mInitCheck(NO_INIT), |
| 40 | mNotify(notify), |
| 41 | mCodecLooper(codecLooper), |
| 42 | mInputFormat(format), |
Andreas Huber | 03e2ffa | 2012-09-13 16:43:51 -0700 | [diff] [blame^] | 43 | mIsVideo(false), |
Andreas Huber | 35213f1 | 2012-08-29 11:41:50 -0700 | [diff] [blame] | 44 | mDoMoreWorkPending(false) { |
Andreas Huber | 03e2ffa | 2012-09-13 16:43:51 -0700 | [diff] [blame^] | 45 | AString mime; |
| 46 | CHECK(mInputFormat->findString("mime", &mime)); |
| 47 | |
| 48 | if (!strncasecmp("video/", mime.c_str(), 6)) { |
| 49 | mIsVideo = true; |
| 50 | } |
| 51 | |
Andreas Huber | 35213f1 | 2012-08-29 11:41:50 -0700 | [diff] [blame] | 52 | mInitCheck = initEncoder(); |
| 53 | } |
| 54 | |
| 55 | Converter::~Converter() { |
| 56 | if (mEncoder != NULL) { |
| 57 | mEncoder->release(); |
| 58 | mEncoder.clear(); |
| 59 | } |
Andreas Huber | 596b4cd | 2012-09-12 16:25:14 -0700 | [diff] [blame] | 60 | |
| 61 | AString mime; |
| 62 | CHECK(mInputFormat->findString("mime", &mime)); |
| 63 | ALOGI("encoder (%s) shut down.", mime.c_str()); |
Andreas Huber | 35213f1 | 2012-08-29 11:41:50 -0700 | [diff] [blame] | 64 | } |
| 65 | |
| 66 | status_t Converter::initCheck() const { |
| 67 | return mInitCheck; |
| 68 | } |
| 69 | |
Andreas Huber | 7f06639 | 2012-09-04 16:30:49 -0700 | [diff] [blame] | 70 | size_t Converter::getInputBufferCount() const { |
| 71 | return mEncoderInputBuffers.size(); |
| 72 | } |
| 73 | |
Andreas Huber | 35213f1 | 2012-08-29 11:41:50 -0700 | [diff] [blame] | 74 | sp<AMessage> Converter::getOutputFormat() const { |
| 75 | return mOutputFormat; |
| 76 | } |
| 77 | |
Andreas Huber | 13da4ed | 2012-09-12 14:06:17 -0700 | [diff] [blame] | 78 | static int32_t getBitrate(const char *propName, int32_t defaultValue) { |
| 79 | char val[PROPERTY_VALUE_MAX]; |
| 80 | if (property_get(propName, val, NULL)) { |
| 81 | char *end; |
| 82 | unsigned long x = strtoul(val, &end, 10); |
| 83 | |
| 84 | if (*end == '\0' && end > val && x > 0) { |
| 85 | return x; |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | return defaultValue; |
| 90 | } |
| 91 | |
Andreas Huber | 35213f1 | 2012-08-29 11:41:50 -0700 | [diff] [blame] | 92 | status_t Converter::initEncoder() { |
| 93 | AString inputMIME; |
| 94 | CHECK(mInputFormat->findString("mime", &inputMIME)); |
| 95 | |
| 96 | AString outputMIME; |
| 97 | bool isAudio = false; |
| 98 | if (!strcasecmp(inputMIME.c_str(), MEDIA_MIMETYPE_AUDIO_RAW)) { |
| 99 | outputMIME = MEDIA_MIMETYPE_AUDIO_AAC; |
| 100 | isAudio = true; |
| 101 | } else if (!strcasecmp(inputMIME.c_str(), MEDIA_MIMETYPE_VIDEO_RAW)) { |
| 102 | outputMIME = MEDIA_MIMETYPE_VIDEO_AVC; |
| 103 | } else { |
| 104 | TRESPASS(); |
| 105 | } |
| 106 | |
| 107 | mEncoder = MediaCodec::CreateByType( |
| 108 | mCodecLooper, outputMIME.c_str(), true /* encoder */); |
| 109 | |
| 110 | if (mEncoder == NULL) { |
| 111 | return ERROR_UNSUPPORTED; |
| 112 | } |
| 113 | |
| 114 | mOutputFormat = mInputFormat->dup(); |
| 115 | mOutputFormat->setString("mime", outputMIME.c_str()); |
| 116 | |
Andreas Huber | 13da4ed | 2012-09-12 14:06:17 -0700 | [diff] [blame] | 117 | int32_t audioBitrate = getBitrate("media.wfd.audio-bitrate", 64000); |
| 118 | int32_t videoBitrate = getBitrate("media.wfd.video-bitrate", 10000000); |
| 119 | |
| 120 | ALOGI("using audio bitrate of %d bps, video bitrate of %d bps", |
| 121 | audioBitrate, videoBitrate); |
| 122 | |
Andreas Huber | 35213f1 | 2012-08-29 11:41:50 -0700 | [diff] [blame] | 123 | if (isAudio) { |
Andreas Huber | 13da4ed | 2012-09-12 14:06:17 -0700 | [diff] [blame] | 124 | mOutputFormat->setInt32("bitrate", audioBitrate); |
Andreas Huber | 35213f1 | 2012-08-29 11:41:50 -0700 | [diff] [blame] | 125 | } else { |
Andreas Huber | 13da4ed | 2012-09-12 14:06:17 -0700 | [diff] [blame] | 126 | mOutputFormat->setInt32("bitrate", videoBitrate); |
Andreas Huber | b5d99d1 | 2012-09-12 16:48:23 -0700 | [diff] [blame] | 127 | mOutputFormat->setInt32("frame-rate", 30); |
Andreas Huber | 35213f1 | 2012-08-29 11:41:50 -0700 | [diff] [blame] | 128 | mOutputFormat->setInt32("i-frame-interval", 3); // Iframes every 3 secs |
| 129 | } |
| 130 | |
| 131 | ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str()); |
| 132 | |
| 133 | status_t err = mEncoder->configure( |
| 134 | mOutputFormat, |
| 135 | NULL /* nativeWindow */, |
| 136 | NULL /* crypto */, |
| 137 | MediaCodec::CONFIGURE_FLAG_ENCODE); |
| 138 | |
| 139 | if (err != OK) { |
| 140 | return err; |
| 141 | } |
| 142 | |
| 143 | err = mEncoder->start(); |
| 144 | |
| 145 | if (err != OK) { |
| 146 | return err; |
| 147 | } |
| 148 | |
| 149 | err = mEncoder->getInputBuffers(&mEncoderInputBuffers); |
| 150 | |
| 151 | if (err != OK) { |
| 152 | return err; |
| 153 | } |
| 154 | |
| 155 | return mEncoder->getOutputBuffers(&mEncoderOutputBuffers); |
| 156 | } |
| 157 | |
| 158 | void Converter::feedAccessUnit(const sp<ABuffer> &accessUnit) { |
| 159 | sp<AMessage> msg = new AMessage(kWhatFeedAccessUnit, id()); |
| 160 | msg->setBuffer("accessUnit", accessUnit); |
| 161 | msg->post(); |
| 162 | } |
| 163 | |
| 164 | void Converter::signalEOS() { |
| 165 | (new AMessage(kWhatInputEOS, id()))->post(); |
| 166 | } |
| 167 | |
| 168 | void Converter::notifyError(status_t err) { |
| 169 | sp<AMessage> notify = mNotify->dup(); |
| 170 | notify->setInt32("what", kWhatError); |
| 171 | notify->setInt32("err", err); |
| 172 | notify->post(); |
| 173 | } |
| 174 | |
| 175 | void Converter::onMessageReceived(const sp<AMessage> &msg) { |
| 176 | switch (msg->what()) { |
| 177 | case kWhatFeedAccessUnit: |
| 178 | { |
| 179 | sp<ABuffer> accessUnit; |
| 180 | CHECK(msg->findBuffer("accessUnit", &accessUnit)); |
| 181 | |
| 182 | mInputBufferQueue.push_back(accessUnit); |
| 183 | |
| 184 | feedEncoderInputBuffers(); |
| 185 | |
| 186 | scheduleDoMoreWork(); |
| 187 | break; |
| 188 | } |
| 189 | |
| 190 | case kWhatInputEOS: |
| 191 | { |
| 192 | mInputBufferQueue.push_back(NULL); |
| 193 | |
| 194 | feedEncoderInputBuffers(); |
| 195 | |
| 196 | scheduleDoMoreWork(); |
| 197 | break; |
| 198 | } |
| 199 | |
| 200 | case kWhatDoMoreWork: |
| 201 | { |
| 202 | mDoMoreWorkPending = false; |
| 203 | status_t err = doMoreWork(); |
| 204 | |
| 205 | if (err != OK) { |
| 206 | notifyError(err); |
| 207 | } else { |
| 208 | scheduleDoMoreWork(); |
| 209 | } |
| 210 | break; |
| 211 | } |
| 212 | |
Andreas Huber | 03e2ffa | 2012-09-13 16:43:51 -0700 | [diff] [blame^] | 213 | case kWhatRequestIDRFrame: |
| 214 | { |
| 215 | if (mIsVideo) { |
| 216 | ALOGI("requesting IDR frame"); |
| 217 | mEncoder->requestIDRFrame(); |
| 218 | } |
| 219 | break; |
| 220 | } |
| 221 | |
Andreas Huber | 35213f1 | 2012-08-29 11:41:50 -0700 | [diff] [blame] | 222 | default: |
| 223 | TRESPASS(); |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | void Converter::scheduleDoMoreWork() { |
| 228 | if (mDoMoreWorkPending) { |
| 229 | return; |
| 230 | } |
| 231 | |
| 232 | mDoMoreWorkPending = true; |
| 233 | (new AMessage(kWhatDoMoreWork, id()))->post(1000ll); |
| 234 | } |
| 235 | |
| 236 | status_t Converter::feedEncoderInputBuffers() { |
| 237 | while (!mInputBufferQueue.empty() |
| 238 | && !mAvailEncoderInputIndices.empty()) { |
| 239 | sp<ABuffer> buffer = *mInputBufferQueue.begin(); |
| 240 | mInputBufferQueue.erase(mInputBufferQueue.begin()); |
| 241 | |
| 242 | size_t bufferIndex = *mAvailEncoderInputIndices.begin(); |
| 243 | mAvailEncoderInputIndices.erase(mAvailEncoderInputIndices.begin()); |
| 244 | |
| 245 | int64_t timeUs = 0ll; |
| 246 | uint32_t flags = 0; |
| 247 | |
| 248 | if (buffer != NULL) { |
| 249 | CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); |
| 250 | |
| 251 | memcpy(mEncoderInputBuffers.itemAt(bufferIndex)->data(), |
| 252 | buffer->data(), |
| 253 | buffer->size()); |
| 254 | |
| 255 | void *mediaBuffer; |
| 256 | if (buffer->meta()->findPointer("mediaBuffer", &mediaBuffer) |
| 257 | && mediaBuffer != NULL) { |
| 258 | mEncoderInputBuffers.itemAt(bufferIndex)->meta() |
| 259 | ->setPointer("mediaBuffer", mediaBuffer); |
| 260 | |
| 261 | buffer->meta()->setPointer("mediaBuffer", NULL); |
| 262 | } |
| 263 | } else { |
| 264 | flags = MediaCodec::BUFFER_FLAG_EOS; |
| 265 | } |
| 266 | |
| 267 | status_t err = mEncoder->queueInputBuffer( |
| 268 | bufferIndex, 0, (buffer == NULL) ? 0 : buffer->size(), |
| 269 | timeUs, flags); |
| 270 | |
| 271 | if (err != OK) { |
| 272 | return err; |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | return OK; |
| 277 | } |
| 278 | |
| 279 | status_t Converter::doMoreWork() { |
| 280 | size_t bufferIndex; |
| 281 | status_t err = mEncoder->dequeueInputBuffer(&bufferIndex); |
| 282 | |
| 283 | if (err == OK) { |
| 284 | mAvailEncoderInputIndices.push_back(bufferIndex); |
| 285 | feedEncoderInputBuffers(); |
| 286 | } |
| 287 | |
| 288 | size_t offset; |
| 289 | size_t size; |
| 290 | int64_t timeUs; |
| 291 | uint32_t flags; |
| 292 | err = mEncoder->dequeueOutputBuffer( |
| 293 | &bufferIndex, &offset, &size, &timeUs, &flags); |
| 294 | |
| 295 | if (err == OK) { |
| 296 | if (flags & MediaCodec::BUFFER_FLAG_EOS) { |
| 297 | sp<AMessage> notify = mNotify->dup(); |
| 298 | notify->setInt32("what", kWhatEOS); |
| 299 | notify->post(); |
| 300 | } else { |
| 301 | sp<ABuffer> buffer = new ABuffer(size); |
| 302 | buffer->meta()->setInt64("timeUs", timeUs); |
| 303 | |
| 304 | memcpy(buffer->data(), |
| 305 | mEncoderOutputBuffers.itemAt(bufferIndex)->base() + offset, |
| 306 | size); |
| 307 | |
| 308 | if (flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) { |
| 309 | mOutputFormat->setBuffer("csd-0", buffer); |
| 310 | } else { |
| 311 | sp<AMessage> notify = mNotify->dup(); |
| 312 | notify->setInt32("what", kWhatAccessUnit); |
| 313 | notify->setBuffer("accessUnit", buffer); |
| 314 | notify->post(); |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | err = mEncoder->releaseOutputBuffer(bufferIndex); |
| 319 | } else if (err == -EAGAIN) { |
| 320 | err = OK; |
| 321 | } |
| 322 | |
| 323 | return err; |
| 324 | } |
| 325 | |
Andreas Huber | 03e2ffa | 2012-09-13 16:43:51 -0700 | [diff] [blame^] | 326 | void Converter::requestIDRFrame() { |
| 327 | (new AMessage(kWhatRequestIDRFrame, id()))->post(); |
| 328 | } |
| 329 | |
Andreas Huber | 35213f1 | 2012-08-29 11:41:50 -0700 | [diff] [blame] | 330 | } // namespace android |
| 331 | |