content/media/omx/OMXCodecWrapper.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/media/omx/OMXCodecWrapper.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,797 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.7 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "OMXCodecWrapper.h"
    1.10 +#include "OMXCodecDescriptorUtil.h"
    1.11 +#include "TrackEncoder.h"
    1.12 +
    1.13 +#include <binder/ProcessState.h>
    1.14 +#include <cutils/properties.h>
    1.15 +#include <media/ICrypto.h>
    1.16 +#include <media/IOMX.h>
    1.17 +#include <OMX_Component.h>
    1.18 +#include <stagefright/MediaDefs.h>
    1.19 +#include <stagefright/MediaErrors.h>
    1.20 +
    1.21 +#include "AudioChannelFormat.h"
    1.22 +#include <mozilla/Monitor.h>
    1.23 +#include "mozilla/layers/GrallocTextureClient.h"
    1.24 +
    1.25 +using namespace mozilla;
    1.26 +using namespace mozilla::gfx;
    1.27 +using namespace mozilla::layers;
    1.28 +
    1.29 +#define ENCODER_CONFIG_BITRATE 2000000 // bps
    1.30 +// How many seconds between I-frames.
    1.31 +#define ENCODER_CONFIG_I_FRAME_INTERVAL 1
    1.32 +// Wait up to 5ms for input buffers.
    1.33 +#define INPUT_BUFFER_TIMEOUT_US (5 * 1000ll)
    1.34 +// AMR NB kbps
    1.35 +#define AMRNB_BITRATE 12200
    1.36 +
    1.37 +#define CODEC_ERROR(args...)                                                   \
    1.38 +  do {                                                                         \
    1.39 +    __android_log_print(ANDROID_LOG_ERROR, "OMXCodecWrapper", ##args);         \
    1.40 +  } while (0)
    1.41 +
    1.42 +namespace android {
    1.43 +
    1.44 +OMXAudioEncoder*
    1.45 +OMXCodecWrapper::CreateAACEncoder()
    1.46 +{
    1.47 +  nsAutoPtr<OMXAudioEncoder> aac(new OMXAudioEncoder(CodecType::AAC_ENC));
    1.48 +  // Return the object only when media codec is valid.
    1.49 +  NS_ENSURE_TRUE(aac->IsValid(), nullptr);
    1.50 +
    1.51 +  return aac.forget();
    1.52 +}
    1.53 +
    1.54 +OMXAudioEncoder*
    1.55 +OMXCodecWrapper::CreateAMRNBEncoder()
    1.56 +{
    1.57 +  nsAutoPtr<OMXAudioEncoder> amr(new OMXAudioEncoder(CodecType::AMR_NB_ENC));
    1.58 +  // Return the object only when media codec is valid.
    1.59 +  NS_ENSURE_TRUE(amr->IsValid(), nullptr);
    1.60 +
    1.61 +  return amr.forget();
    1.62 +}
    1.63 +
    1.64 +OMXVideoEncoder*
    1.65 +OMXCodecWrapper::CreateAVCEncoder()
    1.66 +{
    1.67 +  nsAutoPtr<OMXVideoEncoder> avc(new OMXVideoEncoder(CodecType::AVC_ENC));
    1.68 +  // Return the object only when media codec is valid.
    1.69 +  NS_ENSURE_TRUE(avc->IsValid(), nullptr);
    1.70 +
    1.71 +  return avc.forget();
    1.72 +}
    1.73 +
    1.74 +OMXCodecWrapper::OMXCodecWrapper(CodecType aCodecType)
    1.75 +  : mCodecType(aCodecType)
    1.76 +  , mStarted(false)
    1.77 +  , mAMRCSDProvided(false)
    1.78 +{
    1.79 +  ProcessState::self()->startThreadPool();
    1.80 +
    1.81 +  mLooper = new ALooper();
    1.82 +  mLooper->start();
    1.83 +
    1.84 +  if (aCodecType == CodecType::AVC_ENC) {
    1.85 +    mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_VIDEO_AVC, true);
    1.86 +  } else if (aCodecType == CodecType::AMR_NB_ENC) {
    1.87 +    mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_AUDIO_AMR_NB, true);
    1.88 +  } else if (aCodecType == CodecType::AAC_ENC) {
    1.89 +    mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_AUDIO_AAC, true);
    1.90 +  } else {
    1.91 +    NS_ERROR("Unknown codec type.");
    1.92 +  }
    1.93 +}
    1.94 +
    1.95 +OMXCodecWrapper::~OMXCodecWrapper()
    1.96 +{
    1.97 +  if (mCodec.get()) {
    1.98 +    Stop();
    1.99 +    mCodec->release();
   1.100 +  }
   1.101 +  mLooper->stop();
   1.102 +}
   1.103 +
   1.104 +status_t
   1.105 +OMXCodecWrapper::Start()
   1.106 +{
   1.107 +  // Already started.
   1.108 +  NS_ENSURE_FALSE(mStarted, OK);
   1.109 +
   1.110 +  status_t result = mCodec->start();
   1.111 +  mStarted = (result == OK);
   1.112 +
   1.113 +  // Get references to MediaCodec buffers.
   1.114 +  if (result == OK) {
   1.115 +    mCodec->getInputBuffers(&mInputBufs);
   1.116 +    mCodec->getOutputBuffers(&mOutputBufs);
   1.117 +  }
   1.118 +
   1.119 +  return result;
   1.120 +}
   1.121 +
   1.122 +status_t
   1.123 +OMXCodecWrapper::Stop()
   1.124 +{
   1.125 +  // Already stopped.
   1.126 +  NS_ENSURE_TRUE(mStarted, OK);
   1.127 +
   1.128 +  status_t result = mCodec->stop();
   1.129 +  mStarted = !(result == OK);
   1.130 +
   1.131 +  return result;
   1.132 +}
   1.133 +
   1.134 +// Check system property to see if we're running on emulator.
   1.135 +static bool
   1.136 +IsRunningOnEmulator()
   1.137 +{
   1.138 +  char qemu[PROPERTY_VALUE_MAX];
   1.139 +  property_get("ro.kernel.qemu", qemu, "");
   1.140 +  return strncmp(qemu, "1", 1) == 0;
   1.141 +}
   1.142 +
   1.143 +nsresult
   1.144 +OMXVideoEncoder::Configure(int aWidth, int aHeight, int aFrameRate,
   1.145 +                           BlobFormat aBlobFormat)
   1.146 +{
   1.147 +  MOZ_ASSERT(!mStarted, "Configure() was called already.");
   1.148 +
   1.149 +  NS_ENSURE_TRUE(aWidth > 0 && aHeight > 0 && aFrameRate > 0,
   1.150 +                 NS_ERROR_INVALID_ARG);
   1.151 +
   1.152 +  OMX_VIDEO_AVCLEVELTYPE level = OMX_VIDEO_AVCLevel3;
   1.153 +  OMX_VIDEO_CONTROLRATETYPE bitrateMode = OMX_Video_ControlRateConstant;
   1.154 +  // Limitation of soft AVC/H.264 encoder running on emulator in stagefright.
   1.155 +  static bool emu = IsRunningOnEmulator();
   1.156 +  if (emu) {
   1.157 +    if (aWidth > 352 || aHeight > 288) {
   1.158 +      CODEC_ERROR("SoftAVCEncoder doesn't support resolution larger than CIF");
   1.159 +      return NS_ERROR_INVALID_ARG;
   1.160 +    }
   1.161 +    level = OMX_VIDEO_AVCLevel2;
   1.162 +    bitrateMode = OMX_Video_ControlRateVariable;
   1.163 +  }
   1.164 +
   1.165 +  // Set up configuration parameters for AVC/H.264 encoder.
   1.166 +  sp<AMessage> format = new AMessage;
   1.167 +  // Fixed values
   1.168 +  format->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC);
   1.169 +  format->setInt32("bitrate", ENCODER_CONFIG_BITRATE);
   1.170 +  format->setInt32("i-frame-interval", ENCODER_CONFIG_I_FRAME_INTERVAL);
   1.171 +  // See mozilla::layers::GrallocImage, supports YUV 4:2:0, CbCr width and
   1.172 +  // height is half that of Y
   1.173 +  format->setInt32("color-format", OMX_COLOR_FormatYUV420SemiPlanar);
   1.174 +  format->setInt32("profile", OMX_VIDEO_AVCProfileBaseline);
   1.175 +  format->setInt32("level", level);
   1.176 +  format->setInt32("bitrate-mode", bitrateMode);
   1.177 +  format->setInt32("store-metadata-in-buffers", 0);
   1.178 +  format->setInt32("prepend-sps-pps-to-idr-frames", 0);
   1.179 +  // Input values.
   1.180 +  format->setInt32("width", aWidth);
   1.181 +  format->setInt32("height", aHeight);
   1.182 +  format->setInt32("stride", aWidth);
   1.183 +  format->setInt32("slice-height", aHeight);
   1.184 +  format->setInt32("frame-rate", aFrameRate);
   1.185 +
   1.186 +  status_t result = mCodec->configure(format, nullptr, nullptr,
   1.187 +                                      MediaCodec::CONFIGURE_FLAG_ENCODE);
   1.188 +  NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
   1.189 +
   1.190 +  mWidth = aWidth;
   1.191 +  mHeight = aHeight;
   1.192 +  mBlobFormat = aBlobFormat;
   1.193 +
   1.194 +  result = Start();
   1.195 +
   1.196 +  return result == OK ? NS_OK : NS_ERROR_FAILURE;
   1.197 +}
   1.198 +
   1.199 +// Copy pixels from planar YUV (4:4:4/4:2:2/4:2:0) or NV21 (semi-planar 4:2:0)
   1.200 +// format to NV12 (semi-planar 4:2:0) format for QCOM HW encoder.
   1.201 +// Planar YUV:  YYY...UUU...VVV...
   1.202 +// NV21:        YYY...VUVU...
   1.203 +// NV12:        YYY...UVUV...
   1.204 +// For 4:4:4/4:2:2 -> 4:2:0, subsample using odd row/column without
   1.205 +// interpolation.
   1.206 +// aSource contains info about source image data, and the result will be stored
   1.207 +// in aDestination, whose size needs to be >= Y plane size * 3 / 2.
   1.208 +static void
   1.209 +ConvertPlanarYCbCrToNV12(const PlanarYCbCrData* aSource, uint8_t* aDestination)
   1.210 +{
   1.211 +  // Fill Y plane.
   1.212 +  uint8_t* y = aSource->mYChannel;
   1.213 +  IntSize ySize = aSource->mYSize;
   1.214 +
   1.215 +  // Y plane.
   1.216 +  for (int i = 0; i < ySize.height; i++) {
   1.217 +    memcpy(aDestination, y, ySize.width);
   1.218 +    aDestination += ySize.width;
   1.219 +    y += aSource->mYStride;
   1.220 +  }
   1.221 +
   1.222 +  // Fill interleaved UV plane.
   1.223 +  uint8_t* u = aSource->mCbChannel;
   1.224 +  uint8_t* v = aSource->mCrChannel;
   1.225 +  IntSize uvSize = aSource->mCbCrSize;
   1.226 +  // Subsample to 4:2:0 if source is 4:4:4 or 4:2:2.
   1.227 +  // Y plane width & height should be multiple of U/V plane width & height.
   1.228 +  MOZ_ASSERT(ySize.width % uvSize.width == 0 &&
   1.229 +             ySize.height % uvSize.height == 0);
   1.230 +  size_t uvWidth = ySize.width / 2;
   1.231 +  size_t uvHeight = ySize.height / 2;
   1.232 +  size_t horiSubsample = uvSize.width / uvWidth;
   1.233 +  size_t uPixStride = horiSubsample * (1 + aSource->mCbSkip);
   1.234 +  size_t vPixStride = horiSubsample * (1 + aSource->mCrSkip);
   1.235 +  size_t lineStride = uvSize.height / uvHeight * aSource->mCbCrStride;
   1.236 +
   1.237 +  for (int i = 0; i < uvHeight; i++) {
   1.238 +    // 1st pixel per line.
   1.239 +    uint8_t* uSrc = u;
   1.240 +    uint8_t* vSrc = v;
   1.241 +    for (int j = 0; j < uvWidth; j++) {
   1.242 +      *aDestination++ = *uSrc;
   1.243 +      *aDestination++ = *vSrc;
   1.244 +      // Pick next source pixel.
   1.245 +      uSrc += uPixStride;
   1.246 +      vSrc += vPixStride;
   1.247 +    }
   1.248 +    // Pick next source line.
   1.249 +    u += lineStride;
   1.250 +    v += lineStride;
   1.251 +  }
   1.252 +}
   1.253 +
   1.254 +// Convert pixels in graphic buffer to NV12 format. aSource is the layer image
   1.255 +// containing source graphic buffer, and aDestination is the destination of
   1.256 +// conversion. Currently only 2 source format are supported:
   1.257 +// - NV21/HAL_PIXEL_FORMAT_YCrCb_420_SP (from camera preview window).
   1.258 +// - YV12/HAL_PIXEL_FORMAT_YV12 (from video decoder).
   1.259 +static void
   1.260 +ConvertGrallocImageToNV12(GrallocImage* aSource, uint8_t* aDestination)
   1.261 +{
   1.262 +  // Get graphic buffer.
   1.263 +  sp<GraphicBuffer> graphicBuffer = aSource->GetGraphicBuffer();
   1.264 +
   1.265 +  int pixelFormat = graphicBuffer->getPixelFormat();
   1.266 +  // Only support NV21 (from camera) or YV12 (from HW decoder output) for now.
   1.267 +  NS_ENSURE_TRUE_VOID(pixelFormat == HAL_PIXEL_FORMAT_YCrCb_420_SP ||
   1.268 +                      pixelFormat == HAL_PIXEL_FORMAT_YV12);
   1.269 +
   1.270 +  void* imgPtr = nullptr;
   1.271 +  graphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_MASK, &imgPtr);
   1.272 +  // Build PlanarYCbCrData for NV21 or YV12 buffer.
   1.273 +  PlanarYCbCrData yuv;
   1.274 +  switch (pixelFormat) {
   1.275 +    case HAL_PIXEL_FORMAT_YCrCb_420_SP: // From camera.
   1.276 +      yuv.mYChannel = static_cast<uint8_t*>(imgPtr);
   1.277 +      yuv.mYSkip = 0;
   1.278 +      yuv.mYSize.width = graphicBuffer->getWidth();
   1.279 +      yuv.mYSize.height = graphicBuffer->getHeight();
   1.280 +      yuv.mYStride = graphicBuffer->getStride();
   1.281 +      // 4:2:0.
   1.282 +      yuv.mCbCrSize.width = yuv.mYSize.width / 2;
   1.283 +      yuv.mCbCrSize.height = yuv.mYSize.height / 2;
   1.284 +      // Interleaved VU plane.
   1.285 +      yuv.mCrChannel = yuv.mYChannel + (yuv.mYStride * yuv.mYSize.height);
   1.286 +      yuv.mCrSkip = 1;
   1.287 +      yuv.mCbChannel = yuv.mCrChannel + 1;
   1.288 +      yuv.mCbSkip = 1;
   1.289 +      yuv.mCbCrStride = yuv.mYStride;
   1.290 +      ConvertPlanarYCbCrToNV12(&yuv, aDestination);
   1.291 +      break;
   1.292 +    case HAL_PIXEL_FORMAT_YV12: // From video decoder.
   1.293 +      // Android YV12 format is defined in system/core/include/system/graphics.h
   1.294 +      yuv.mYChannel = static_cast<uint8_t*>(imgPtr);
   1.295 +      yuv.mYSkip = 0;
   1.296 +      yuv.mYSize.width = graphicBuffer->getWidth();
   1.297 +      yuv.mYSize.height = graphicBuffer->getHeight();
   1.298 +      yuv.mYStride = graphicBuffer->getStride();
   1.299 +      // 4:2:0.
   1.300 +      yuv.mCbCrSize.width = yuv.mYSize.width / 2;
   1.301 +      yuv.mCbCrSize.height = yuv.mYSize.height / 2;
   1.302 +      yuv.mCrChannel = yuv.mYChannel + (yuv.mYStride * yuv.mYSize.height);
   1.303 +      // Aligned to 16 bytes boundary.
   1.304 +      yuv.mCbCrStride = (yuv.mYStride / 2 + 15) & ~0x0F;
   1.305 +      yuv.mCrSkip = 0;
   1.306 +      yuv.mCbChannel = yuv.mCrChannel + (yuv.mCbCrStride * yuv.mCbCrSize.height);
   1.307 +      yuv.mCbSkip = 0;
   1.308 +      ConvertPlanarYCbCrToNV12(&yuv, aDestination);
   1.309 +      break;
   1.310 +    default:
   1.311 +      NS_ERROR("Unsupported input gralloc image type. Should never be here.");
   1.312 +  }
   1.313 +
   1.314 +  graphicBuffer->unlock();
   1.315 +}
   1.316 +
   1.317 +nsresult
   1.318 +OMXVideoEncoder::Encode(const Image* aImage, int aWidth, int aHeight,
   1.319 +                        int64_t aTimestamp, int aInputFlags)
   1.320 +{
   1.321 +  MOZ_ASSERT(mStarted, "Configure() should be called before Encode().");
   1.322 +
   1.323 +  NS_ENSURE_TRUE(aWidth == mWidth && aHeight == mHeight && aTimestamp >= 0,
   1.324 +                 NS_ERROR_INVALID_ARG);
   1.325 +
   1.326 +  status_t result;
   1.327 +
   1.328 +  // Dequeue an input buffer.
   1.329 +  uint32_t index;
   1.330 +  result = mCodec->dequeueInputBuffer(&index, INPUT_BUFFER_TIMEOUT_US);
   1.331 +  NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
   1.332 +
   1.333 +  const sp<ABuffer>& inBuf = mInputBufs.itemAt(index);
   1.334 +  uint8_t* dst = inBuf->data();
   1.335 +  size_t dstSize = inBuf->capacity();
   1.336 +
   1.337 +  size_t yLen = aWidth * aHeight;
   1.338 +  size_t uvLen = yLen / 2;
   1.339 +  // Buffer should be large enough to hold input image data.
   1.340 +  MOZ_ASSERT(dstSize >= yLen + uvLen);
   1.341 +
   1.342 +  inBuf->setRange(0, yLen + uvLen);
   1.343 +
   1.344 +  if (!aImage) {
   1.345 +    // Generate muted/black image directly in buffer.
   1.346 +    dstSize = yLen + uvLen;
   1.347 +    // Fill Y plane.
   1.348 +    memset(dst, 0x10, yLen);
   1.349 +    // Fill UV plane.
   1.350 +    memset(dst + yLen, 0x80, uvLen);
   1.351 +  } else {
   1.352 +    Image* img = const_cast<Image*>(aImage);
   1.353 +    ImageFormat format = img->GetFormat();
   1.354 +
   1.355 +    MOZ_ASSERT(aWidth == img->GetSize().width &&
   1.356 +               aHeight == img->GetSize().height);
   1.357 +
   1.358 +    if (format == ImageFormat::GRALLOC_PLANAR_YCBCR) {
   1.359 +      ConvertGrallocImageToNV12(static_cast<GrallocImage*>(img), dst);
   1.360 +    } else if (format == ImageFormat::PLANAR_YCBCR) {
   1.361 +      ConvertPlanarYCbCrToNV12(static_cast<PlanarYCbCrImage*>(img)->GetData(),
   1.362 +                             dst);
   1.363 +    } else {
   1.364 +      // TODO: support RGB to YUV color conversion.
   1.365 +      NS_ERROR("Unsupported input image type.");
   1.366 +    }
   1.367 +  }
   1.368 +
   1.369 +  // Queue this input buffer.
   1.370 +  result = mCodec->queueInputBuffer(index, 0, dstSize, aTimestamp, aInputFlags);
   1.371 +
   1.372 +  return result == OK ? NS_OK : NS_ERROR_FAILURE;
   1.373 +}
   1.374 +
   1.375 +status_t
   1.376 +OMXVideoEncoder::AppendDecoderConfig(nsTArray<uint8_t>* aOutputBuf,
   1.377 +                                     ABuffer* aData)
   1.378 +{
   1.379 +  // Codec already parsed aData. Using its result makes generating config blob
   1.380 +  // much easier.
   1.381 +  sp<AMessage> format;
   1.382 +  mCodec->getOutputFormat(&format);
   1.383 +
   1.384 +  // NAL unit format is needed by WebRTC for RTP packets; AVC/H.264 decoder
   1.385 +  // config descriptor is needed to construct MP4 'avcC' box.
   1.386 +  status_t result = GenerateAVCDescriptorBlob(format, aOutputBuf, mBlobFormat);
   1.387 +  mHasConfigBlob = (result == OK);
   1.388 +
   1.389 +  return result;
   1.390 +}
   1.391 +
   1.392 +// Override to replace NAL unit start code with 4-bytes unit length.
   1.393 +// See ISO/IEC 14496-15 5.2.3.
   1.394 +void
   1.395 +OMXVideoEncoder::AppendFrame(nsTArray<uint8_t>* aOutputBuf,
   1.396 +                             const uint8_t* aData, size_t aSize)
   1.397 +{
   1.398 +  aOutputBuf->SetCapacity(aSize);
   1.399 +
   1.400 +  if (mBlobFormat == BlobFormat::AVC_NAL) {
   1.401 +    // Append NAL format data without modification.
   1.402 +    aOutputBuf->AppendElements(aData, aSize);
   1.403 +    return;
   1.404 +  }
   1.405 +  // Replace start code with data length.
   1.406 +  uint8_t length[] = {
   1.407 +    (aSize >> 24) & 0xFF,
   1.408 +    (aSize >> 16) & 0xFF,
   1.409 +    (aSize >> 8) & 0xFF,
   1.410 +    aSize & 0xFF,
   1.411 +  };
   1.412 +  aOutputBuf->AppendElements(length, sizeof(length));
   1.413 +  aOutputBuf->AppendElements(aData + sizeof(length), aSize);
   1.414 +}
   1.415 +
   1.416 +nsresult
   1.417 +OMXVideoEncoder::GetCodecConfig(nsTArray<uint8_t>* aOutputBuf)
   1.418 +{
   1.419 +  MOZ_ASSERT(mHasConfigBlob, "Haven't received codec config yet.");
   1.420 +
   1.421 +  return AppendDecoderConfig(aOutputBuf, nullptr) == OK ? NS_OK : NS_ERROR_FAILURE;
   1.422 +}
   1.423 +
   1.424 +// MediaCodec::setParameters() is available only after API level 18.
   1.425 +#if ANDROID_VERSION >= 18
   1.426 +nsresult
   1.427 +OMXVideoEncoder::SetBitrate(int32_t aKbps)
   1.428 +{
   1.429 +  sp<AMessage> msg = new AMessage();
   1.430 +  msg->setInt32("videoBitrate", aKbps * 1000 /* kbps -> bps */);
   1.431 +  status_t result = mCodec->setParameters(msg);
   1.432 +  MOZ_ASSERT(result == OK);
   1.433 +  return result == OK ? NS_OK : NS_ERROR_FAILURE;
   1.434 +}
   1.435 +#endif
   1.436 +
   1.437 +nsresult
   1.438 +OMXAudioEncoder::Configure(int aChannels, int aInputSampleRate,
   1.439 +                           int aEncodedSampleRate)
   1.440 +{
   1.441 +  MOZ_ASSERT(!mStarted);
   1.442 +
   1.443 +  NS_ENSURE_TRUE(aChannels > 0 && aInputSampleRate > 0 && aEncodedSampleRate >= 0,
   1.444 +                 NS_ERROR_INVALID_ARG);
   1.445 +
   1.446 +  if (aInputSampleRate != aEncodedSampleRate) {
   1.447 +    int error;
   1.448 +    mResampler = speex_resampler_init(aChannels,
   1.449 +                                      aInputSampleRate,
   1.450 +                                      aEncodedSampleRate,
   1.451 +                                      SPEEX_RESAMPLER_QUALITY_DEFAULT,
   1.452 +                                      &error);
   1.453 +
   1.454 +    if (error != RESAMPLER_ERR_SUCCESS) {
   1.455 +      return NS_ERROR_FAILURE;
   1.456 +    }
   1.457 +    speex_resampler_skip_zeros(mResampler);
   1.458 +  }
   1.459 +  // Set up configuration parameters for AAC encoder.
   1.460 +  sp<AMessage> format = new AMessage;
   1.461 +  // Fixed values.
   1.462 +  if (mCodecType == AAC_ENC) {
   1.463 +    format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC);
   1.464 +    format->setInt32("aac-profile", OMX_AUDIO_AACObjectLC);
   1.465 +    format->setInt32("bitrate", kAACBitrate);
   1.466 +    format->setInt32("sample-rate", aInputSampleRate);
   1.467 +  } else if (mCodecType == AMR_NB_ENC) {
   1.468 +    format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_NB);
   1.469 +    format->setInt32("bitrate", AMRNB_BITRATE);
   1.470 +    format->setInt32("sample-rate", aEncodedSampleRate);
   1.471 +  } else {
   1.472 +    MOZ_ASSERT(false, "Can't support this codec type!!");
   1.473 +  }
   1.474 +  // Input values.
   1.475 +  format->setInt32("channel-count", aChannels);
   1.476 +
   1.477 +  status_t result = mCodec->configure(format, nullptr, nullptr,
   1.478 +                                      MediaCodec::CONFIGURE_FLAG_ENCODE);
   1.479 +  NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
   1.480 +
   1.481 +  mChannels = aChannels;
   1.482 +  mSampleDuration = 1000000 / aInputSampleRate;
   1.483 +  mResamplingRatio = aEncodedSampleRate > 0 ? 1.0 *
   1.484 +                      aEncodedSampleRate / aInputSampleRate : 1.0;
   1.485 +  result = Start();
   1.486 +
   1.487 +  return result == OK ? NS_OK : NS_ERROR_FAILURE;
   1.488 +}
   1.489 +
   1.490 +class InputBufferHelper MOZ_FINAL {
   1.491 +public:
   1.492 +  InputBufferHelper(sp<MediaCodec>& aCodec, Vector<sp<ABuffer> >& aBuffers)
   1.493 +    : mCodec(aCodec)
   1.494 +    , mBuffers(aBuffers)
   1.495 +    , mIndex(0)
   1.496 +    , mData(nullptr)
   1.497 +    , mOffset(0)
   1.498 +    , mCapicity(0)
   1.499 +  {}
   1.500 +
   1.501 +  ~InputBufferHelper()
   1.502 +  {
   1.503 +    // Unflushed data in buffer.
   1.504 +    MOZ_ASSERT(!mData);
   1.505 +  }
   1.506 +
   1.507 +  status_t Dequeue()
   1.508 +  {
   1.509 +    // Shouldn't have dequeued buffer.
   1.510 +    MOZ_ASSERT(!mData);
   1.511 +
   1.512 +    status_t result = mCodec->dequeueInputBuffer(&mIndex,
   1.513 +                                                 INPUT_BUFFER_TIMEOUT_US);
   1.514 +    NS_ENSURE_TRUE(result == OK, result);
   1.515 +    sp<ABuffer> inBuf = mBuffers.itemAt(mIndex);
   1.516 +    mData = inBuf->data();
   1.517 +    mCapicity = inBuf->capacity();
   1.518 +    mOffset = 0;
   1.519 +
   1.520 +    return OK;
   1.521 +  }
   1.522 +
   1.523 +  uint8_t* GetPointer() { return mData + mOffset; }
   1.524 +
   1.525 +  const size_t AvailableSize() { return mCapicity - mOffset; }
   1.526 +
   1.527 +  void IncreaseOffset(size_t aValue)
   1.528 +  {
   1.529 +    // Should never out of bound.
   1.530 +    MOZ_ASSERT(mOffset + aValue <= mCapicity);
   1.531 +    mOffset += aValue;
   1.532 +  }
   1.533 +
   1.534 +  status_t Enqueue(int64_t aTimestamp, int aFlags)
   1.535 +  {
   1.536 +    // Should have dequeued buffer.
   1.537 +    MOZ_ASSERT(mData);
   1.538 +
   1.539 +    // Queue this buffer.
   1.540 +    status_t result = mCodec->queueInputBuffer(mIndex, 0, mOffset, aTimestamp,
   1.541 +                                               aFlags);
   1.542 +    NS_ENSURE_TRUE(result == OK, result);
   1.543 +    mData = nullptr;
   1.544 +
   1.545 +    return OK;
   1.546 +  }
   1.547 +
   1.548 +private:
   1.549 +  sp<MediaCodec>& mCodec;
   1.550 +  Vector<sp<ABuffer> >& mBuffers;
   1.551 +  size_t mIndex;
   1.552 +  uint8_t* mData;
   1.553 +  size_t mCapicity;
   1.554 +  size_t mOffset;
   1.555 +};
   1.556 +
   1.557 +OMXAudioEncoder::~OMXAudioEncoder()
   1.558 +{
   1.559 +  if (mResampler) {
   1.560 +    speex_resampler_destroy(mResampler);
   1.561 +    mResampler = nullptr;
   1.562 +  }
   1.563 +}
   1.564 +
   1.565 +nsresult
   1.566 +OMXAudioEncoder::Encode(AudioSegment& aSegment, int aInputFlags)
   1.567 +{
   1.568 +#ifndef MOZ_SAMPLE_TYPE_S16
   1.569 +#error MediaCodec accepts only 16-bit PCM data.
   1.570 +#endif
   1.571 +
   1.572 +  MOZ_ASSERT(mStarted, "Configure() should be called before Encode().");
   1.573 +
   1.574 +  size_t numSamples = aSegment.GetDuration();
   1.575 +
   1.576 +  // Get input buffer.
   1.577 +  InputBufferHelper buffer(mCodec, mInputBufs);
   1.578 +  status_t result = buffer.Dequeue();
   1.579 +  if (result == -EAGAIN) {
   1.580 +    // All input buffers are full. Caller can try again later after consuming
   1.581 +    // some output buffers.
   1.582 +    return NS_OK;
   1.583 +  }
   1.584 +  NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
   1.585 +
   1.586 +  size_t sourceSamplesCopied = 0; // Number of copied samples.
   1.587 +
   1.588 +  if (numSamples > 0) {
   1.589 +    // Copy input PCM data to input buffer until queue is empty.
   1.590 +    AudioSegment::ChunkIterator iter(const_cast<AudioSegment&>(aSegment));
   1.591 +    while (!iter.IsEnded()) {
   1.592 +      AudioChunk chunk = *iter;
   1.593 +      size_t sourceSamplesToCopy = chunk.GetDuration(); // Number of samples to copy.
   1.594 +      size_t bytesToCopy = sourceSamplesToCopy * mChannels *
   1.595 +                           sizeof(AudioDataValue) * mResamplingRatio;
   1.596 +      if (bytesToCopy > buffer.AvailableSize()) {
   1.597 +        // Not enough space left in input buffer. Send it to encoder and get a
   1.598 +        // new one.
   1.599 +        result = buffer.Enqueue(mTimestamp, aInputFlags & ~BUFFER_EOS);
   1.600 +        NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
   1.601 +
   1.602 +        result = buffer.Dequeue();
   1.603 +        if (result == -EAGAIN) {
   1.604 +          // All input buffers are full. Caller can try again later after
   1.605 +          // consuming some output buffers.
   1.606 +          aSegment.RemoveLeading(sourceSamplesCopied);
   1.607 +          return NS_OK;
   1.608 +        }
   1.609 +
   1.610 +        mTimestamp += sourceSamplesCopied * mSampleDuration;
   1.611 +        sourceSamplesCopied = 0;
   1.612 +
   1.613 +        NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
   1.614 +      }
   1.615 +
   1.616 +      AudioDataValue* dst = reinterpret_cast<AudioDataValue*>(buffer.GetPointer());
   1.617 +      uint32_t dstSamplesCopied = sourceSamplesToCopy;
   1.618 +      if (!chunk.IsNull()) {
   1.619 +        if (mResampler) {
   1.620 +          nsAutoTArray<AudioDataValue, 9600> pcm;
   1.621 +          pcm.SetLength(bytesToCopy);
   1.622 +          // Append the interleaved data to input buffer.
   1.623 +          AudioTrackEncoder::InterleaveTrackData(chunk, sourceSamplesToCopy,
   1.624 +                                                 mChannels,
   1.625 +                                                 pcm.Elements());
   1.626 +          uint32_t inframes = sourceSamplesToCopy;
   1.627 +          short* in = reinterpret_cast<short*>(pcm.Elements());
   1.628 +          speex_resampler_process_interleaved_int(mResampler, in, &inframes,
   1.629 +                                                              dst, &dstSamplesCopied);
   1.630 +        } else {
   1.631 +          AudioTrackEncoder::InterleaveTrackData(chunk, sourceSamplesToCopy,
   1.632 +                                                 mChannels,
   1.633 +                                                 dst);
   1.634 +          dstSamplesCopied = sourceSamplesToCopy * mChannels;
   1.635 +        }
   1.636 +      } else {
   1.637 +        // Silence.
   1.638 +        memset(dst, 0, mResamplingRatio * sourceSamplesToCopy * sizeof(AudioDataValue));
   1.639 +      }
   1.640 +
   1.641 +      sourceSamplesCopied += sourceSamplesToCopy;
   1.642 +      buffer.IncreaseOffset(dstSamplesCopied * sizeof(AudioDataValue));
   1.643 +      iter.Next();
   1.644 +    }
   1.645 +    if (sourceSamplesCopied > 0) {
   1.646 +      aSegment.RemoveLeading(sourceSamplesCopied);
   1.647 +    }
   1.648 +  } else if (aInputFlags & BUFFER_EOS) {
   1.649 +    // No audio data left in segment but we still have to feed something to
   1.650 +    // MediaCodec in order to notify EOS.
   1.651 +    size_t bytesToCopy = mChannels * sizeof(AudioDataValue);
   1.652 +    memset(buffer.GetPointer(), 0, bytesToCopy);
   1.653 +    buffer.IncreaseOffset(bytesToCopy);
   1.654 +    sourceSamplesCopied = 1;
   1.655 +  }
   1.656 +
   1.657 +  if (sourceSamplesCopied > 0) {
   1.658 +    int flags = aInputFlags;
   1.659 +    if (aSegment.GetDuration() > 0) {
   1.660 +      // Don't signal EOS until source segment is empty.
   1.661 +      flags &= ~BUFFER_EOS;
   1.662 +    }
   1.663 +    result = buffer.Enqueue(mTimestamp, flags);
   1.664 +    NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
   1.665 +
   1.666 +    mTimestamp += sourceSamplesCopied * mSampleDuration;
   1.667 +  }
   1.668 +
   1.669 +  return NS_OK;
   1.670 +}
   1.671 +
   1.672 +// Generate decoder config descriptor (defined in ISO/IEC 14496-1 8.3.4.1) for
   1.673 +// AAC. The hard-coded bytes are copied from
   1.674 +// MPEG4Writer::Track::writeMp4aEsdsBox() implementation in libstagefright.
   1.675 +status_t
   1.676 +OMXAudioEncoder::AppendDecoderConfig(nsTArray<uint8_t>* aOutputBuf,
   1.677 +                                     ABuffer* aData)
   1.678 +{
   1.679 +  MOZ_ASSERT(aData);
   1.680 +
   1.681 +  const size_t csdSize = aData->size();
   1.682 +
   1.683 +  // See
   1.684 +  // http://wiki.multimedia.cx/index.php?title=Understanding_AAC#Packaging.2FEncapsulation_And_Setup_Data
   1.685 +  // AAC decoder specific descriptor contains 2 bytes.
   1.686 +  NS_ENSURE_TRUE(csdSize == 2, ERROR_MALFORMED);
   1.687 +  // Encoder output must be consistent with kAACFrameDuration:
   1.688 +  // 14th bit (frame length flag) == 0 => 1024 (kAACFrameDuration) samples.
   1.689 +  NS_ENSURE_TRUE((aData->data()[1] & 0x04) == 0, ERROR_MALFORMED);
   1.690 +
   1.691 +  // Decoder config descriptor
   1.692 +  const uint8_t decConfig[] = {
   1.693 +    0x04,                   // Decoder config descriptor tag.
   1.694 +    15 + csdSize,           // Size: following bytes + csd size.
   1.695 +    0x40,                   // Object type: MPEG-4 audio.
   1.696 +    0x15,                   // Stream type: audio, reserved: 1.
   1.697 +    0x00, 0x03, 0x00,       // Buffer size: 768 (kAACFrameSize).
   1.698 +    0x00, 0x01, 0x77, 0x00, // Max bitrate: 96000 (kAACBitrate).
   1.699 +    0x00, 0x01, 0x77, 0x00, // Avg bitrate: 96000 (kAACBitrate).
   1.700 +    0x05,                   // Decoder specific descriptor tag.
   1.701 +    csdSize,                // Data size.
   1.702 +  };
   1.703 +  // SL config descriptor.
   1.704 +  const uint8_t slConfig[] = {
   1.705 +    0x06, // SL config descriptor tag.
   1.706 +    0x01, // Size.
   1.707 +    0x02, // Fixed value.
   1.708 +  };
   1.709 +
   1.710 +  aOutputBuf->SetCapacity(sizeof(decConfig) + csdSize + sizeof(slConfig));
   1.711 +  aOutputBuf->AppendElements(decConfig, sizeof(decConfig));
   1.712 +  aOutputBuf->AppendElements(aData->data(), csdSize);
   1.713 +  aOutputBuf->AppendElements(slConfig, sizeof(slConfig));
   1.714 +
   1.715 +  return OK;
   1.716 +}
   1.717 +
   1.718 +nsresult
   1.719 +OMXCodecWrapper::GetNextEncodedFrame(nsTArray<uint8_t>* aOutputBuf,
   1.720 +                                     int64_t* aOutputTimestamp,
   1.721 +                                     int* aOutputFlags, int64_t aTimeOut)
   1.722 +{
   1.723 +  MOZ_ASSERT(mStarted,
   1.724 +             "Configure() should be called before GetNextEncodedFrame().");
   1.725 +
   1.726 +  // Dequeue a buffer from output buffers.
   1.727 +  size_t index = 0;
   1.728 +  size_t outOffset = 0;
   1.729 +  size_t outSize = 0;
   1.730 +  int64_t outTimeUs = 0;
   1.731 +  uint32_t outFlags = 0;
   1.732 +  bool retry = false;
   1.733 +  do {
   1.734 +    status_t result = mCodec->dequeueOutputBuffer(&index, &outOffset, &outSize,
   1.735 +                                                  &outTimeUs, &outFlags,
   1.736 +                                                  aTimeOut);
   1.737 +    switch (result) {
   1.738 +      case OK:
   1.739 +        break;
   1.740 +      case INFO_OUTPUT_BUFFERS_CHANGED:
   1.741 +        // Update our references to new buffers.
   1.742 +        result = mCodec->getOutputBuffers(&mOutputBufs);
   1.743 +        // Get output from a new buffer.
   1.744 +        retry = true;
   1.745 +        break;
   1.746 +      case INFO_FORMAT_CHANGED:
   1.747 +        // It's okay: for encoder, MediaCodec reports this only to inform caller
   1.748 +        // that there will be a codec config buffer next.
   1.749 +        return NS_OK;
   1.750 +      case -EAGAIN:
   1.751 +        // Output buffer not available. Caller can try again later.
   1.752 +        return NS_OK;
   1.753 +      default:
   1.754 +        CODEC_ERROR("MediaCodec error:%d", result);
   1.755 +        MOZ_ASSERT(false, "MediaCodec error.");
   1.756 +        return NS_ERROR_FAILURE;
   1.757 +    }
   1.758 +  } while (retry);
   1.759 +
   1.760 +  if (aOutputBuf) {
   1.761 +    aOutputBuf->Clear();
   1.762 +    const sp<ABuffer> omxBuf = mOutputBufs.itemAt(index);
   1.763 +    if (outFlags & MediaCodec::BUFFER_FLAG_CODECCONFIG) {
   1.764 +      // Codec specific data.
   1.765 +      if (AppendDecoderConfig(aOutputBuf, omxBuf.get()) != OK) {
   1.766 +        mCodec->releaseOutputBuffer(index);
   1.767 +        return NS_ERROR_FAILURE;
   1.768 +      }
   1.769 +    } else if ((mCodecType == AMR_NB_ENC) && !mAMRCSDProvided){
   1.770 +      // OMX AMR codec won't provide csd data, need to generate a fake one.
   1.771 +      nsRefPtr<EncodedFrame> audiodata = new EncodedFrame();
   1.772 +      // Decoder config descriptor
   1.773 +      const uint8_t decConfig[] = {
   1.774 +        0x0, 0x0, 0x0, 0x0, // vendor: 4 bytes
   1.775 +        0x0,                // decoder version
   1.776 +        0x83, 0xFF,         // mode set: all enabled
   1.777 +        0x00,               // mode change period
   1.778 +        0x01,               // frames per sample
   1.779 +      };
   1.780 +      aOutputBuf->AppendElements(decConfig, sizeof(decConfig));
   1.781 +      outFlags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
   1.782 +      mAMRCSDProvided = true;
   1.783 +    } else {
   1.784 +      AppendFrame(aOutputBuf, omxBuf->data(), omxBuf->size());
   1.785 +    }
   1.786 +  }
   1.787 +  mCodec->releaseOutputBuffer(index);
   1.788 +
   1.789 +  if (aOutputTimestamp) {
   1.790 +    *aOutputTimestamp = outTimeUs;
   1.791 +  }
   1.792 +
   1.793 +  if (aOutputFlags) {
   1.794 +    *aOutputFlags = outFlags;
   1.795 +  }
   1.796 +
   1.797 +  return NS_OK;
   1.798 +}
   1.799 +
   1.800 +}

mercurial