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