content/media/omx/OMXCodecWrapper.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "OMXCodecWrapper.h"
michael@0 7 #include "OMXCodecDescriptorUtil.h"
michael@0 8 #include "TrackEncoder.h"
michael@0 9
michael@0 10 #include <binder/ProcessState.h>
michael@0 11 #include <cutils/properties.h>
michael@0 12 #include <media/ICrypto.h>
michael@0 13 #include <media/IOMX.h>
michael@0 14 #include <OMX_Component.h>
michael@0 15 #include <stagefright/MediaDefs.h>
michael@0 16 #include <stagefright/MediaErrors.h>
michael@0 17
michael@0 18 #include "AudioChannelFormat.h"
michael@0 19 #include <mozilla/Monitor.h>
michael@0 20 #include "mozilla/layers/GrallocTextureClient.h"
michael@0 21
michael@0 22 using namespace mozilla;
michael@0 23 using namespace mozilla::gfx;
michael@0 24 using namespace mozilla::layers;
michael@0 25
michael@0 26 #define ENCODER_CONFIG_BITRATE 2000000 // bps
michael@0 27 // How many seconds between I-frames.
michael@0 28 #define ENCODER_CONFIG_I_FRAME_INTERVAL 1
michael@0 29 // Wait up to 5ms for input buffers.
michael@0 30 #define INPUT_BUFFER_TIMEOUT_US (5 * 1000ll)
michael@0 31 // AMR NB kbps
michael@0 32 #define AMRNB_BITRATE 12200
michael@0 33
michael@0 34 #define CODEC_ERROR(args...) \
michael@0 35 do { \
michael@0 36 __android_log_print(ANDROID_LOG_ERROR, "OMXCodecWrapper", ##args); \
michael@0 37 } while (0)
michael@0 38
michael@0 39 namespace android {
michael@0 40
michael@0 41 OMXAudioEncoder*
michael@0 42 OMXCodecWrapper::CreateAACEncoder()
michael@0 43 {
michael@0 44 nsAutoPtr<OMXAudioEncoder> aac(new OMXAudioEncoder(CodecType::AAC_ENC));
michael@0 45 // Return the object only when media codec is valid.
michael@0 46 NS_ENSURE_TRUE(aac->IsValid(), nullptr);
michael@0 47
michael@0 48 return aac.forget();
michael@0 49 }
michael@0 50
michael@0 51 OMXAudioEncoder*
michael@0 52 OMXCodecWrapper::CreateAMRNBEncoder()
michael@0 53 {
michael@0 54 nsAutoPtr<OMXAudioEncoder> amr(new OMXAudioEncoder(CodecType::AMR_NB_ENC));
michael@0 55 // Return the object only when media codec is valid.
michael@0 56 NS_ENSURE_TRUE(amr->IsValid(), nullptr);
michael@0 57
michael@0 58 return amr.forget();
michael@0 59 }
michael@0 60
michael@0 61 OMXVideoEncoder*
michael@0 62 OMXCodecWrapper::CreateAVCEncoder()
michael@0 63 {
michael@0 64 nsAutoPtr<OMXVideoEncoder> avc(new OMXVideoEncoder(CodecType::AVC_ENC));
michael@0 65 // Return the object only when media codec is valid.
michael@0 66 NS_ENSURE_TRUE(avc->IsValid(), nullptr);
michael@0 67
michael@0 68 return avc.forget();
michael@0 69 }
michael@0 70
michael@0 71 OMXCodecWrapper::OMXCodecWrapper(CodecType aCodecType)
michael@0 72 : mCodecType(aCodecType)
michael@0 73 , mStarted(false)
michael@0 74 , mAMRCSDProvided(false)
michael@0 75 {
michael@0 76 ProcessState::self()->startThreadPool();
michael@0 77
michael@0 78 mLooper = new ALooper();
michael@0 79 mLooper->start();
michael@0 80
michael@0 81 if (aCodecType == CodecType::AVC_ENC) {
michael@0 82 mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_VIDEO_AVC, true);
michael@0 83 } else if (aCodecType == CodecType::AMR_NB_ENC) {
michael@0 84 mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_AUDIO_AMR_NB, true);
michael@0 85 } else if (aCodecType == CodecType::AAC_ENC) {
michael@0 86 mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_AUDIO_AAC, true);
michael@0 87 } else {
michael@0 88 NS_ERROR("Unknown codec type.");
michael@0 89 }
michael@0 90 }
michael@0 91
michael@0 92 OMXCodecWrapper::~OMXCodecWrapper()
michael@0 93 {
michael@0 94 if (mCodec.get()) {
michael@0 95 Stop();
michael@0 96 mCodec->release();
michael@0 97 }
michael@0 98 mLooper->stop();
michael@0 99 }
michael@0 100
michael@0 101 status_t
michael@0 102 OMXCodecWrapper::Start()
michael@0 103 {
michael@0 104 // Already started.
michael@0 105 NS_ENSURE_FALSE(mStarted, OK);
michael@0 106
michael@0 107 status_t result = mCodec->start();
michael@0 108 mStarted = (result == OK);
michael@0 109
michael@0 110 // Get references to MediaCodec buffers.
michael@0 111 if (result == OK) {
michael@0 112 mCodec->getInputBuffers(&mInputBufs);
michael@0 113 mCodec->getOutputBuffers(&mOutputBufs);
michael@0 114 }
michael@0 115
michael@0 116 return result;
michael@0 117 }
michael@0 118
michael@0 119 status_t
michael@0 120 OMXCodecWrapper::Stop()
michael@0 121 {
michael@0 122 // Already stopped.
michael@0 123 NS_ENSURE_TRUE(mStarted, OK);
michael@0 124
michael@0 125 status_t result = mCodec->stop();
michael@0 126 mStarted = !(result == OK);
michael@0 127
michael@0 128 return result;
michael@0 129 }
michael@0 130
michael@0 131 // Check system property to see if we're running on emulator.
michael@0 132 static bool
michael@0 133 IsRunningOnEmulator()
michael@0 134 {
michael@0 135 char qemu[PROPERTY_VALUE_MAX];
michael@0 136 property_get("ro.kernel.qemu", qemu, "");
michael@0 137 return strncmp(qemu, "1", 1) == 0;
michael@0 138 }
michael@0 139
michael@0 140 nsresult
michael@0 141 OMXVideoEncoder::Configure(int aWidth, int aHeight, int aFrameRate,
michael@0 142 BlobFormat aBlobFormat)
michael@0 143 {
michael@0 144 MOZ_ASSERT(!mStarted, "Configure() was called already.");
michael@0 145
michael@0 146 NS_ENSURE_TRUE(aWidth > 0 && aHeight > 0 && aFrameRate > 0,
michael@0 147 NS_ERROR_INVALID_ARG);
michael@0 148
michael@0 149 OMX_VIDEO_AVCLEVELTYPE level = OMX_VIDEO_AVCLevel3;
michael@0 150 OMX_VIDEO_CONTROLRATETYPE bitrateMode = OMX_Video_ControlRateConstant;
michael@0 151 // Limitation of soft AVC/H.264 encoder running on emulator in stagefright.
michael@0 152 static bool emu = IsRunningOnEmulator();
michael@0 153 if (emu) {
michael@0 154 if (aWidth > 352 || aHeight > 288) {
michael@0 155 CODEC_ERROR("SoftAVCEncoder doesn't support resolution larger than CIF");
michael@0 156 return NS_ERROR_INVALID_ARG;
michael@0 157 }
michael@0 158 level = OMX_VIDEO_AVCLevel2;
michael@0 159 bitrateMode = OMX_Video_ControlRateVariable;
michael@0 160 }
michael@0 161
michael@0 162 // Set up configuration parameters for AVC/H.264 encoder.
michael@0 163 sp<AMessage> format = new AMessage;
michael@0 164 // Fixed values
michael@0 165 format->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC);
michael@0 166 format->setInt32("bitrate", ENCODER_CONFIG_BITRATE);
michael@0 167 format->setInt32("i-frame-interval", ENCODER_CONFIG_I_FRAME_INTERVAL);
michael@0 168 // See mozilla::layers::GrallocImage, supports YUV 4:2:0, CbCr width and
michael@0 169 // height is half that of Y
michael@0 170 format->setInt32("color-format", OMX_COLOR_FormatYUV420SemiPlanar);
michael@0 171 format->setInt32("profile", OMX_VIDEO_AVCProfileBaseline);
michael@0 172 format->setInt32("level", level);
michael@0 173 format->setInt32("bitrate-mode", bitrateMode);
michael@0 174 format->setInt32("store-metadata-in-buffers", 0);
michael@0 175 format->setInt32("prepend-sps-pps-to-idr-frames", 0);
michael@0 176 // Input values.
michael@0 177 format->setInt32("width", aWidth);
michael@0 178 format->setInt32("height", aHeight);
michael@0 179 format->setInt32("stride", aWidth);
michael@0 180 format->setInt32("slice-height", aHeight);
michael@0 181 format->setInt32("frame-rate", aFrameRate);
michael@0 182
michael@0 183 status_t result = mCodec->configure(format, nullptr, nullptr,
michael@0 184 MediaCodec::CONFIGURE_FLAG_ENCODE);
michael@0 185 NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
michael@0 186
michael@0 187 mWidth = aWidth;
michael@0 188 mHeight = aHeight;
michael@0 189 mBlobFormat = aBlobFormat;
michael@0 190
michael@0 191 result = Start();
michael@0 192
michael@0 193 return result == OK ? NS_OK : NS_ERROR_FAILURE;
michael@0 194 }
michael@0 195
michael@0 196 // Copy pixels from planar YUV (4:4:4/4:2:2/4:2:0) or NV21 (semi-planar 4:2:0)
michael@0 197 // format to NV12 (semi-planar 4:2:0) format for QCOM HW encoder.
michael@0 198 // Planar YUV: YYY...UUU...VVV...
michael@0 199 // NV21: YYY...VUVU...
michael@0 200 // NV12: YYY...UVUV...
michael@0 201 // For 4:4:4/4:2:2 -> 4:2:0, subsample using odd row/column without
michael@0 202 // interpolation.
michael@0 203 // aSource contains info about source image data, and the result will be stored
michael@0 204 // in aDestination, whose size needs to be >= Y plane size * 3 / 2.
michael@0 205 static void
michael@0 206 ConvertPlanarYCbCrToNV12(const PlanarYCbCrData* aSource, uint8_t* aDestination)
michael@0 207 {
michael@0 208 // Fill Y plane.
michael@0 209 uint8_t* y = aSource->mYChannel;
michael@0 210 IntSize ySize = aSource->mYSize;
michael@0 211
michael@0 212 // Y plane.
michael@0 213 for (int i = 0; i < ySize.height; i++) {
michael@0 214 memcpy(aDestination, y, ySize.width);
michael@0 215 aDestination += ySize.width;
michael@0 216 y += aSource->mYStride;
michael@0 217 }
michael@0 218
michael@0 219 // Fill interleaved UV plane.
michael@0 220 uint8_t* u = aSource->mCbChannel;
michael@0 221 uint8_t* v = aSource->mCrChannel;
michael@0 222 IntSize uvSize = aSource->mCbCrSize;
michael@0 223 // Subsample to 4:2:0 if source is 4:4:4 or 4:2:2.
michael@0 224 // Y plane width & height should be multiple of U/V plane width & height.
michael@0 225 MOZ_ASSERT(ySize.width % uvSize.width == 0 &&
michael@0 226 ySize.height % uvSize.height == 0);
michael@0 227 size_t uvWidth = ySize.width / 2;
michael@0 228 size_t uvHeight = ySize.height / 2;
michael@0 229 size_t horiSubsample = uvSize.width / uvWidth;
michael@0 230 size_t uPixStride = horiSubsample * (1 + aSource->mCbSkip);
michael@0 231 size_t vPixStride = horiSubsample * (1 + aSource->mCrSkip);
michael@0 232 size_t lineStride = uvSize.height / uvHeight * aSource->mCbCrStride;
michael@0 233
michael@0 234 for (int i = 0; i < uvHeight; i++) {
michael@0 235 // 1st pixel per line.
michael@0 236 uint8_t* uSrc = u;
michael@0 237 uint8_t* vSrc = v;
michael@0 238 for (int j = 0; j < uvWidth; j++) {
michael@0 239 *aDestination++ = *uSrc;
michael@0 240 *aDestination++ = *vSrc;
michael@0 241 // Pick next source pixel.
michael@0 242 uSrc += uPixStride;
michael@0 243 vSrc += vPixStride;
michael@0 244 }
michael@0 245 // Pick next source line.
michael@0 246 u += lineStride;
michael@0 247 v += lineStride;
michael@0 248 }
michael@0 249 }
michael@0 250
michael@0 251 // Convert pixels in graphic buffer to NV12 format. aSource is the layer image
michael@0 252 // containing source graphic buffer, and aDestination is the destination of
michael@0 253 // conversion. Currently only 2 source format are supported:
michael@0 254 // - NV21/HAL_PIXEL_FORMAT_YCrCb_420_SP (from camera preview window).
michael@0 255 // - YV12/HAL_PIXEL_FORMAT_YV12 (from video decoder).
michael@0 256 static void
michael@0 257 ConvertGrallocImageToNV12(GrallocImage* aSource, uint8_t* aDestination)
michael@0 258 {
michael@0 259 // Get graphic buffer.
michael@0 260 sp<GraphicBuffer> graphicBuffer = aSource->GetGraphicBuffer();
michael@0 261
michael@0 262 int pixelFormat = graphicBuffer->getPixelFormat();
michael@0 263 // Only support NV21 (from camera) or YV12 (from HW decoder output) for now.
michael@0 264 NS_ENSURE_TRUE_VOID(pixelFormat == HAL_PIXEL_FORMAT_YCrCb_420_SP ||
michael@0 265 pixelFormat == HAL_PIXEL_FORMAT_YV12);
michael@0 266
michael@0 267 void* imgPtr = nullptr;
michael@0 268 graphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_MASK, &imgPtr);
michael@0 269 // Build PlanarYCbCrData for NV21 or YV12 buffer.
michael@0 270 PlanarYCbCrData yuv;
michael@0 271 switch (pixelFormat) {
michael@0 272 case HAL_PIXEL_FORMAT_YCrCb_420_SP: // From camera.
michael@0 273 yuv.mYChannel = static_cast<uint8_t*>(imgPtr);
michael@0 274 yuv.mYSkip = 0;
michael@0 275 yuv.mYSize.width = graphicBuffer->getWidth();
michael@0 276 yuv.mYSize.height = graphicBuffer->getHeight();
michael@0 277 yuv.mYStride = graphicBuffer->getStride();
michael@0 278 // 4:2:0.
michael@0 279 yuv.mCbCrSize.width = yuv.mYSize.width / 2;
michael@0 280 yuv.mCbCrSize.height = yuv.mYSize.height / 2;
michael@0 281 // Interleaved VU plane.
michael@0 282 yuv.mCrChannel = yuv.mYChannel + (yuv.mYStride * yuv.mYSize.height);
michael@0 283 yuv.mCrSkip = 1;
michael@0 284 yuv.mCbChannel = yuv.mCrChannel + 1;
michael@0 285 yuv.mCbSkip = 1;
michael@0 286 yuv.mCbCrStride = yuv.mYStride;
michael@0 287 ConvertPlanarYCbCrToNV12(&yuv, aDestination);
michael@0 288 break;
michael@0 289 case HAL_PIXEL_FORMAT_YV12: // From video decoder.
michael@0 290 // Android YV12 format is defined in system/core/include/system/graphics.h
michael@0 291 yuv.mYChannel = static_cast<uint8_t*>(imgPtr);
michael@0 292 yuv.mYSkip = 0;
michael@0 293 yuv.mYSize.width = graphicBuffer->getWidth();
michael@0 294 yuv.mYSize.height = graphicBuffer->getHeight();
michael@0 295 yuv.mYStride = graphicBuffer->getStride();
michael@0 296 // 4:2:0.
michael@0 297 yuv.mCbCrSize.width = yuv.mYSize.width / 2;
michael@0 298 yuv.mCbCrSize.height = yuv.mYSize.height / 2;
michael@0 299 yuv.mCrChannel = yuv.mYChannel + (yuv.mYStride * yuv.mYSize.height);
michael@0 300 // Aligned to 16 bytes boundary.
michael@0 301 yuv.mCbCrStride = (yuv.mYStride / 2 + 15) & ~0x0F;
michael@0 302 yuv.mCrSkip = 0;
michael@0 303 yuv.mCbChannel = yuv.mCrChannel + (yuv.mCbCrStride * yuv.mCbCrSize.height);
michael@0 304 yuv.mCbSkip = 0;
michael@0 305 ConvertPlanarYCbCrToNV12(&yuv, aDestination);
michael@0 306 break;
michael@0 307 default:
michael@0 308 NS_ERROR("Unsupported input gralloc image type. Should never be here.");
michael@0 309 }
michael@0 310
michael@0 311 graphicBuffer->unlock();
michael@0 312 }
michael@0 313
michael@0 314 nsresult
michael@0 315 OMXVideoEncoder::Encode(const Image* aImage, int aWidth, int aHeight,
michael@0 316 int64_t aTimestamp, int aInputFlags)
michael@0 317 {
michael@0 318 MOZ_ASSERT(mStarted, "Configure() should be called before Encode().");
michael@0 319
michael@0 320 NS_ENSURE_TRUE(aWidth == mWidth && aHeight == mHeight && aTimestamp >= 0,
michael@0 321 NS_ERROR_INVALID_ARG);
michael@0 322
michael@0 323 status_t result;
michael@0 324
michael@0 325 // Dequeue an input buffer.
michael@0 326 uint32_t index;
michael@0 327 result = mCodec->dequeueInputBuffer(&index, INPUT_BUFFER_TIMEOUT_US);
michael@0 328 NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
michael@0 329
michael@0 330 const sp<ABuffer>& inBuf = mInputBufs.itemAt(index);
michael@0 331 uint8_t* dst = inBuf->data();
michael@0 332 size_t dstSize = inBuf->capacity();
michael@0 333
michael@0 334 size_t yLen = aWidth * aHeight;
michael@0 335 size_t uvLen = yLen / 2;
michael@0 336 // Buffer should be large enough to hold input image data.
michael@0 337 MOZ_ASSERT(dstSize >= yLen + uvLen);
michael@0 338
michael@0 339 inBuf->setRange(0, yLen + uvLen);
michael@0 340
michael@0 341 if (!aImage) {
michael@0 342 // Generate muted/black image directly in buffer.
michael@0 343 dstSize = yLen + uvLen;
michael@0 344 // Fill Y plane.
michael@0 345 memset(dst, 0x10, yLen);
michael@0 346 // Fill UV plane.
michael@0 347 memset(dst + yLen, 0x80, uvLen);
michael@0 348 } else {
michael@0 349 Image* img = const_cast<Image*>(aImage);
michael@0 350 ImageFormat format = img->GetFormat();
michael@0 351
michael@0 352 MOZ_ASSERT(aWidth == img->GetSize().width &&
michael@0 353 aHeight == img->GetSize().height);
michael@0 354
michael@0 355 if (format == ImageFormat::GRALLOC_PLANAR_YCBCR) {
michael@0 356 ConvertGrallocImageToNV12(static_cast<GrallocImage*>(img), dst);
michael@0 357 } else if (format == ImageFormat::PLANAR_YCBCR) {
michael@0 358 ConvertPlanarYCbCrToNV12(static_cast<PlanarYCbCrImage*>(img)->GetData(),
michael@0 359 dst);
michael@0 360 } else {
michael@0 361 // TODO: support RGB to YUV color conversion.
michael@0 362 NS_ERROR("Unsupported input image type.");
michael@0 363 }
michael@0 364 }
michael@0 365
michael@0 366 // Queue this input buffer.
michael@0 367 result = mCodec->queueInputBuffer(index, 0, dstSize, aTimestamp, aInputFlags);
michael@0 368
michael@0 369 return result == OK ? NS_OK : NS_ERROR_FAILURE;
michael@0 370 }
michael@0 371
michael@0 372 status_t
michael@0 373 OMXVideoEncoder::AppendDecoderConfig(nsTArray<uint8_t>* aOutputBuf,
michael@0 374 ABuffer* aData)
michael@0 375 {
michael@0 376 // Codec already parsed aData. Using its result makes generating config blob
michael@0 377 // much easier.
michael@0 378 sp<AMessage> format;
michael@0 379 mCodec->getOutputFormat(&format);
michael@0 380
michael@0 381 // NAL unit format is needed by WebRTC for RTP packets; AVC/H.264 decoder
michael@0 382 // config descriptor is needed to construct MP4 'avcC' box.
michael@0 383 status_t result = GenerateAVCDescriptorBlob(format, aOutputBuf, mBlobFormat);
michael@0 384 mHasConfigBlob = (result == OK);
michael@0 385
michael@0 386 return result;
michael@0 387 }
michael@0 388
michael@0 389 // Override to replace NAL unit start code with 4-bytes unit length.
michael@0 390 // See ISO/IEC 14496-15 5.2.3.
michael@0 391 void
michael@0 392 OMXVideoEncoder::AppendFrame(nsTArray<uint8_t>* aOutputBuf,
michael@0 393 const uint8_t* aData, size_t aSize)
michael@0 394 {
michael@0 395 aOutputBuf->SetCapacity(aSize);
michael@0 396
michael@0 397 if (mBlobFormat == BlobFormat::AVC_NAL) {
michael@0 398 // Append NAL format data without modification.
michael@0 399 aOutputBuf->AppendElements(aData, aSize);
michael@0 400 return;
michael@0 401 }
michael@0 402 // Replace start code with data length.
michael@0 403 uint8_t length[] = {
michael@0 404 (aSize >> 24) & 0xFF,
michael@0 405 (aSize >> 16) & 0xFF,
michael@0 406 (aSize >> 8) & 0xFF,
michael@0 407 aSize & 0xFF,
michael@0 408 };
michael@0 409 aOutputBuf->AppendElements(length, sizeof(length));
michael@0 410 aOutputBuf->AppendElements(aData + sizeof(length), aSize);
michael@0 411 }
michael@0 412
michael@0 413 nsresult
michael@0 414 OMXVideoEncoder::GetCodecConfig(nsTArray<uint8_t>* aOutputBuf)
michael@0 415 {
michael@0 416 MOZ_ASSERT(mHasConfigBlob, "Haven't received codec config yet.");
michael@0 417
michael@0 418 return AppendDecoderConfig(aOutputBuf, nullptr) == OK ? NS_OK : NS_ERROR_FAILURE;
michael@0 419 }
michael@0 420
michael@0 421 // MediaCodec::setParameters() is available only after API level 18.
michael@0 422 #if ANDROID_VERSION >= 18
michael@0 423 nsresult
michael@0 424 OMXVideoEncoder::SetBitrate(int32_t aKbps)
michael@0 425 {
michael@0 426 sp<AMessage> msg = new AMessage();
michael@0 427 msg->setInt32("videoBitrate", aKbps * 1000 /* kbps -> bps */);
michael@0 428 status_t result = mCodec->setParameters(msg);
michael@0 429 MOZ_ASSERT(result == OK);
michael@0 430 return result == OK ? NS_OK : NS_ERROR_FAILURE;
michael@0 431 }
michael@0 432 #endif
michael@0 433
michael@0 434 nsresult
michael@0 435 OMXAudioEncoder::Configure(int aChannels, int aInputSampleRate,
michael@0 436 int aEncodedSampleRate)
michael@0 437 {
michael@0 438 MOZ_ASSERT(!mStarted);
michael@0 439
michael@0 440 NS_ENSURE_TRUE(aChannels > 0 && aInputSampleRate > 0 && aEncodedSampleRate >= 0,
michael@0 441 NS_ERROR_INVALID_ARG);
michael@0 442
michael@0 443 if (aInputSampleRate != aEncodedSampleRate) {
michael@0 444 int error;
michael@0 445 mResampler = speex_resampler_init(aChannels,
michael@0 446 aInputSampleRate,
michael@0 447 aEncodedSampleRate,
michael@0 448 SPEEX_RESAMPLER_QUALITY_DEFAULT,
michael@0 449 &error);
michael@0 450
michael@0 451 if (error != RESAMPLER_ERR_SUCCESS) {
michael@0 452 return NS_ERROR_FAILURE;
michael@0 453 }
michael@0 454 speex_resampler_skip_zeros(mResampler);
michael@0 455 }
michael@0 456 // Set up configuration parameters for AAC encoder.
michael@0 457 sp<AMessage> format = new AMessage;
michael@0 458 // Fixed values.
michael@0 459 if (mCodecType == AAC_ENC) {
michael@0 460 format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC);
michael@0 461 format->setInt32("aac-profile", OMX_AUDIO_AACObjectLC);
michael@0 462 format->setInt32("bitrate", kAACBitrate);
michael@0 463 format->setInt32("sample-rate", aInputSampleRate);
michael@0 464 } else if (mCodecType == AMR_NB_ENC) {
michael@0 465 format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_NB);
michael@0 466 format->setInt32("bitrate", AMRNB_BITRATE);
michael@0 467 format->setInt32("sample-rate", aEncodedSampleRate);
michael@0 468 } else {
michael@0 469 MOZ_ASSERT(false, "Can't support this codec type!!");
michael@0 470 }
michael@0 471 // Input values.
michael@0 472 format->setInt32("channel-count", aChannels);
michael@0 473
michael@0 474 status_t result = mCodec->configure(format, nullptr, nullptr,
michael@0 475 MediaCodec::CONFIGURE_FLAG_ENCODE);
michael@0 476 NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
michael@0 477
michael@0 478 mChannels = aChannels;
michael@0 479 mSampleDuration = 1000000 / aInputSampleRate;
michael@0 480 mResamplingRatio = aEncodedSampleRate > 0 ? 1.0 *
michael@0 481 aEncodedSampleRate / aInputSampleRate : 1.0;
michael@0 482 result = Start();
michael@0 483
michael@0 484 return result == OK ? NS_OK : NS_ERROR_FAILURE;
michael@0 485 }
michael@0 486
michael@0 487 class InputBufferHelper MOZ_FINAL {
michael@0 488 public:
michael@0 489 InputBufferHelper(sp<MediaCodec>& aCodec, Vector<sp<ABuffer> >& aBuffers)
michael@0 490 : mCodec(aCodec)
michael@0 491 , mBuffers(aBuffers)
michael@0 492 , mIndex(0)
michael@0 493 , mData(nullptr)
michael@0 494 , mOffset(0)
michael@0 495 , mCapicity(0)
michael@0 496 {}
michael@0 497
michael@0 498 ~InputBufferHelper()
michael@0 499 {
michael@0 500 // Unflushed data in buffer.
michael@0 501 MOZ_ASSERT(!mData);
michael@0 502 }
michael@0 503
michael@0 504 status_t Dequeue()
michael@0 505 {
michael@0 506 // Shouldn't have dequeued buffer.
michael@0 507 MOZ_ASSERT(!mData);
michael@0 508
michael@0 509 status_t result = mCodec->dequeueInputBuffer(&mIndex,
michael@0 510 INPUT_BUFFER_TIMEOUT_US);
michael@0 511 NS_ENSURE_TRUE(result == OK, result);
michael@0 512 sp<ABuffer> inBuf = mBuffers.itemAt(mIndex);
michael@0 513 mData = inBuf->data();
michael@0 514 mCapicity = inBuf->capacity();
michael@0 515 mOffset = 0;
michael@0 516
michael@0 517 return OK;
michael@0 518 }
michael@0 519
michael@0 520 uint8_t* GetPointer() { return mData + mOffset; }
michael@0 521
michael@0 522 const size_t AvailableSize() { return mCapicity - mOffset; }
michael@0 523
michael@0 524 void IncreaseOffset(size_t aValue)
michael@0 525 {
michael@0 526 // Should never out of bound.
michael@0 527 MOZ_ASSERT(mOffset + aValue <= mCapicity);
michael@0 528 mOffset += aValue;
michael@0 529 }
michael@0 530
michael@0 531 status_t Enqueue(int64_t aTimestamp, int aFlags)
michael@0 532 {
michael@0 533 // Should have dequeued buffer.
michael@0 534 MOZ_ASSERT(mData);
michael@0 535
michael@0 536 // Queue this buffer.
michael@0 537 status_t result = mCodec->queueInputBuffer(mIndex, 0, mOffset, aTimestamp,
michael@0 538 aFlags);
michael@0 539 NS_ENSURE_TRUE(result == OK, result);
michael@0 540 mData = nullptr;
michael@0 541
michael@0 542 return OK;
michael@0 543 }
michael@0 544
michael@0 545 private:
michael@0 546 sp<MediaCodec>& mCodec;
michael@0 547 Vector<sp<ABuffer> >& mBuffers;
michael@0 548 size_t mIndex;
michael@0 549 uint8_t* mData;
michael@0 550 size_t mCapicity;
michael@0 551 size_t mOffset;
michael@0 552 };
michael@0 553
michael@0 554 OMXAudioEncoder::~OMXAudioEncoder()
michael@0 555 {
michael@0 556 if (mResampler) {
michael@0 557 speex_resampler_destroy(mResampler);
michael@0 558 mResampler = nullptr;
michael@0 559 }
michael@0 560 }
michael@0 561
michael@0 562 nsresult
michael@0 563 OMXAudioEncoder::Encode(AudioSegment& aSegment, int aInputFlags)
michael@0 564 {
michael@0 565 #ifndef MOZ_SAMPLE_TYPE_S16
michael@0 566 #error MediaCodec accepts only 16-bit PCM data.
michael@0 567 #endif
michael@0 568
michael@0 569 MOZ_ASSERT(mStarted, "Configure() should be called before Encode().");
michael@0 570
michael@0 571 size_t numSamples = aSegment.GetDuration();
michael@0 572
michael@0 573 // Get input buffer.
michael@0 574 InputBufferHelper buffer(mCodec, mInputBufs);
michael@0 575 status_t result = buffer.Dequeue();
michael@0 576 if (result == -EAGAIN) {
michael@0 577 // All input buffers are full. Caller can try again later after consuming
michael@0 578 // some output buffers.
michael@0 579 return NS_OK;
michael@0 580 }
michael@0 581 NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
michael@0 582
michael@0 583 size_t sourceSamplesCopied = 0; // Number of copied samples.
michael@0 584
michael@0 585 if (numSamples > 0) {
michael@0 586 // Copy input PCM data to input buffer until queue is empty.
michael@0 587 AudioSegment::ChunkIterator iter(const_cast<AudioSegment&>(aSegment));
michael@0 588 while (!iter.IsEnded()) {
michael@0 589 AudioChunk chunk = *iter;
michael@0 590 size_t sourceSamplesToCopy = chunk.GetDuration(); // Number of samples to copy.
michael@0 591 size_t bytesToCopy = sourceSamplesToCopy * mChannels *
michael@0 592 sizeof(AudioDataValue) * mResamplingRatio;
michael@0 593 if (bytesToCopy > buffer.AvailableSize()) {
michael@0 594 // Not enough space left in input buffer. Send it to encoder and get a
michael@0 595 // new one.
michael@0 596 result = buffer.Enqueue(mTimestamp, aInputFlags & ~BUFFER_EOS);
michael@0 597 NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
michael@0 598
michael@0 599 result = buffer.Dequeue();
michael@0 600 if (result == -EAGAIN) {
michael@0 601 // All input buffers are full. Caller can try again later after
michael@0 602 // consuming some output buffers.
michael@0 603 aSegment.RemoveLeading(sourceSamplesCopied);
michael@0 604 return NS_OK;
michael@0 605 }
michael@0 606
michael@0 607 mTimestamp += sourceSamplesCopied * mSampleDuration;
michael@0 608 sourceSamplesCopied = 0;
michael@0 609
michael@0 610 NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
michael@0 611 }
michael@0 612
michael@0 613 AudioDataValue* dst = reinterpret_cast<AudioDataValue*>(buffer.GetPointer());
michael@0 614 uint32_t dstSamplesCopied = sourceSamplesToCopy;
michael@0 615 if (!chunk.IsNull()) {
michael@0 616 if (mResampler) {
michael@0 617 nsAutoTArray<AudioDataValue, 9600> pcm;
michael@0 618 pcm.SetLength(bytesToCopy);
michael@0 619 // Append the interleaved data to input buffer.
michael@0 620 AudioTrackEncoder::InterleaveTrackData(chunk, sourceSamplesToCopy,
michael@0 621 mChannels,
michael@0 622 pcm.Elements());
michael@0 623 uint32_t inframes = sourceSamplesToCopy;
michael@0 624 short* in = reinterpret_cast<short*>(pcm.Elements());
michael@0 625 speex_resampler_process_interleaved_int(mResampler, in, &inframes,
michael@0 626 dst, &dstSamplesCopied);
michael@0 627 } else {
michael@0 628 AudioTrackEncoder::InterleaveTrackData(chunk, sourceSamplesToCopy,
michael@0 629 mChannels,
michael@0 630 dst);
michael@0 631 dstSamplesCopied = sourceSamplesToCopy * mChannels;
michael@0 632 }
michael@0 633 } else {
michael@0 634 // Silence.
michael@0 635 memset(dst, 0, mResamplingRatio * sourceSamplesToCopy * sizeof(AudioDataValue));
michael@0 636 }
michael@0 637
michael@0 638 sourceSamplesCopied += sourceSamplesToCopy;
michael@0 639 buffer.IncreaseOffset(dstSamplesCopied * sizeof(AudioDataValue));
michael@0 640 iter.Next();
michael@0 641 }
michael@0 642 if (sourceSamplesCopied > 0) {
michael@0 643 aSegment.RemoveLeading(sourceSamplesCopied);
michael@0 644 }
michael@0 645 } else if (aInputFlags & BUFFER_EOS) {
michael@0 646 // No audio data left in segment but we still have to feed something to
michael@0 647 // MediaCodec in order to notify EOS.
michael@0 648 size_t bytesToCopy = mChannels * sizeof(AudioDataValue);
michael@0 649 memset(buffer.GetPointer(), 0, bytesToCopy);
michael@0 650 buffer.IncreaseOffset(bytesToCopy);
michael@0 651 sourceSamplesCopied = 1;
michael@0 652 }
michael@0 653
michael@0 654 if (sourceSamplesCopied > 0) {
michael@0 655 int flags = aInputFlags;
michael@0 656 if (aSegment.GetDuration() > 0) {
michael@0 657 // Don't signal EOS until source segment is empty.
michael@0 658 flags &= ~BUFFER_EOS;
michael@0 659 }
michael@0 660 result = buffer.Enqueue(mTimestamp, flags);
michael@0 661 NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
michael@0 662
michael@0 663 mTimestamp += sourceSamplesCopied * mSampleDuration;
michael@0 664 }
michael@0 665
michael@0 666 return NS_OK;
michael@0 667 }
michael@0 668
michael@0 669 // Generate decoder config descriptor (defined in ISO/IEC 14496-1 8.3.4.1) for
michael@0 670 // AAC. The hard-coded bytes are copied from
michael@0 671 // MPEG4Writer::Track::writeMp4aEsdsBox() implementation in libstagefright.
michael@0 672 status_t
michael@0 673 OMXAudioEncoder::AppendDecoderConfig(nsTArray<uint8_t>* aOutputBuf,
michael@0 674 ABuffer* aData)
michael@0 675 {
michael@0 676 MOZ_ASSERT(aData);
michael@0 677
michael@0 678 const size_t csdSize = aData->size();
michael@0 679
michael@0 680 // See
michael@0 681 // http://wiki.multimedia.cx/index.php?title=Understanding_AAC#Packaging.2FEncapsulation_And_Setup_Data
michael@0 682 // AAC decoder specific descriptor contains 2 bytes.
michael@0 683 NS_ENSURE_TRUE(csdSize == 2, ERROR_MALFORMED);
michael@0 684 // Encoder output must be consistent with kAACFrameDuration:
michael@0 685 // 14th bit (frame length flag) == 0 => 1024 (kAACFrameDuration) samples.
michael@0 686 NS_ENSURE_TRUE((aData->data()[1] & 0x04) == 0, ERROR_MALFORMED);
michael@0 687
michael@0 688 // Decoder config descriptor
michael@0 689 const uint8_t decConfig[] = {
michael@0 690 0x04, // Decoder config descriptor tag.
michael@0 691 15 + csdSize, // Size: following bytes + csd size.
michael@0 692 0x40, // Object type: MPEG-4 audio.
michael@0 693 0x15, // Stream type: audio, reserved: 1.
michael@0 694 0x00, 0x03, 0x00, // Buffer size: 768 (kAACFrameSize).
michael@0 695 0x00, 0x01, 0x77, 0x00, // Max bitrate: 96000 (kAACBitrate).
michael@0 696 0x00, 0x01, 0x77, 0x00, // Avg bitrate: 96000 (kAACBitrate).
michael@0 697 0x05, // Decoder specific descriptor tag.
michael@0 698 csdSize, // Data size.
michael@0 699 };
michael@0 700 // SL config descriptor.
michael@0 701 const uint8_t slConfig[] = {
michael@0 702 0x06, // SL config descriptor tag.
michael@0 703 0x01, // Size.
michael@0 704 0x02, // Fixed value.
michael@0 705 };
michael@0 706
michael@0 707 aOutputBuf->SetCapacity(sizeof(decConfig) + csdSize + sizeof(slConfig));
michael@0 708 aOutputBuf->AppendElements(decConfig, sizeof(decConfig));
michael@0 709 aOutputBuf->AppendElements(aData->data(), csdSize);
michael@0 710 aOutputBuf->AppendElements(slConfig, sizeof(slConfig));
michael@0 711
michael@0 712 return OK;
michael@0 713 }
michael@0 714
michael@0 715 nsresult
michael@0 716 OMXCodecWrapper::GetNextEncodedFrame(nsTArray<uint8_t>* aOutputBuf,
michael@0 717 int64_t* aOutputTimestamp,
michael@0 718 int* aOutputFlags, int64_t aTimeOut)
michael@0 719 {
michael@0 720 MOZ_ASSERT(mStarted,
michael@0 721 "Configure() should be called before GetNextEncodedFrame().");
michael@0 722
michael@0 723 // Dequeue a buffer from output buffers.
michael@0 724 size_t index = 0;
michael@0 725 size_t outOffset = 0;
michael@0 726 size_t outSize = 0;
michael@0 727 int64_t outTimeUs = 0;
michael@0 728 uint32_t outFlags = 0;
michael@0 729 bool retry = false;
michael@0 730 do {
michael@0 731 status_t result = mCodec->dequeueOutputBuffer(&index, &outOffset, &outSize,
michael@0 732 &outTimeUs, &outFlags,
michael@0 733 aTimeOut);
michael@0 734 switch (result) {
michael@0 735 case OK:
michael@0 736 break;
michael@0 737 case INFO_OUTPUT_BUFFERS_CHANGED:
michael@0 738 // Update our references to new buffers.
michael@0 739 result = mCodec->getOutputBuffers(&mOutputBufs);
michael@0 740 // Get output from a new buffer.
michael@0 741 retry = true;
michael@0 742 break;
michael@0 743 case INFO_FORMAT_CHANGED:
michael@0 744 // It's okay: for encoder, MediaCodec reports this only to inform caller
michael@0 745 // that there will be a codec config buffer next.
michael@0 746 return NS_OK;
michael@0 747 case -EAGAIN:
michael@0 748 // Output buffer not available. Caller can try again later.
michael@0 749 return NS_OK;
michael@0 750 default:
michael@0 751 CODEC_ERROR("MediaCodec error:%d", result);
michael@0 752 MOZ_ASSERT(false, "MediaCodec error.");
michael@0 753 return NS_ERROR_FAILURE;
michael@0 754 }
michael@0 755 } while (retry);
michael@0 756
michael@0 757 if (aOutputBuf) {
michael@0 758 aOutputBuf->Clear();
michael@0 759 const sp<ABuffer> omxBuf = mOutputBufs.itemAt(index);
michael@0 760 if (outFlags & MediaCodec::BUFFER_FLAG_CODECCONFIG) {
michael@0 761 // Codec specific data.
michael@0 762 if (AppendDecoderConfig(aOutputBuf, omxBuf.get()) != OK) {
michael@0 763 mCodec->releaseOutputBuffer(index);
michael@0 764 return NS_ERROR_FAILURE;
michael@0 765 }
michael@0 766 } else if ((mCodecType == AMR_NB_ENC) && !mAMRCSDProvided){
michael@0 767 // OMX AMR codec won't provide csd data, need to generate a fake one.
michael@0 768 nsRefPtr<EncodedFrame> audiodata = new EncodedFrame();
michael@0 769 // Decoder config descriptor
michael@0 770 const uint8_t decConfig[] = {
michael@0 771 0x0, 0x0, 0x0, 0x0, // vendor: 4 bytes
michael@0 772 0x0, // decoder version
michael@0 773 0x83, 0xFF, // mode set: all enabled
michael@0 774 0x00, // mode change period
michael@0 775 0x01, // frames per sample
michael@0 776 };
michael@0 777 aOutputBuf->AppendElements(decConfig, sizeof(decConfig));
michael@0 778 outFlags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
michael@0 779 mAMRCSDProvided = true;
michael@0 780 } else {
michael@0 781 AppendFrame(aOutputBuf, omxBuf->data(), omxBuf->size());
michael@0 782 }
michael@0 783 }
michael@0 784 mCodec->releaseOutputBuffer(index);
michael@0 785
michael@0 786 if (aOutputTimestamp) {
michael@0 787 *aOutputTimestamp = outTimeUs;
michael@0 788 }
michael@0 789
michael@0 790 if (aOutputFlags) {
michael@0 791 *aOutputFlags = outFlags;
michael@0 792 }
michael@0 793
michael@0 794 return NS_OK;
michael@0 795 }
michael@0 796
michael@0 797 }

mercurial