James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2010 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | //#define LOG_NDEBUG 0 |
| 18 | #define LOG_TAG "M4vH263Encoder" |
| 19 | #include <utils/Log.h> |
| 20 | |
| 21 | #include "M4vH263Encoder.h" |
| 22 | |
| 23 | #include "mp4enc_api.h" |
| 24 | #include "OMX_Video.h" |
| 25 | |
| 26 | #include <media/stagefright/MediaBufferGroup.h> |
| 27 | #include <media/stagefright/MediaDebug.h> |
| 28 | #include <media/stagefright/MediaDefs.h> |
| 29 | #include <media/stagefright/MediaErrors.h> |
| 30 | #include <media/stagefright/MetaData.h> |
| 31 | #include <media/stagefright/Utils.h> |
| 32 | |
| 33 | namespace android { |
| 34 | |
James Dong | af50969 | 2010-10-19 17:35:35 -0700 | [diff] [blame] | 35 | static status_t ConvertOmxProfileLevel( |
| 36 | MP4EncodingMode mode, |
| 37 | int32_t omxProfile, |
| 38 | int32_t omxLevel, |
| 39 | ProfileLevelType* pvProfileLevel) { |
Steve Block | 71f2cf1 | 2011-10-20 11:56:00 +0100 | [diff] [blame] | 40 | ALOGV("ConvertOmxProfileLevel: %d/%d/%d", mode, omxProfile, omxLevel); |
James Dong | af50969 | 2010-10-19 17:35:35 -0700 | [diff] [blame] | 41 | ProfileLevelType profileLevel; |
| 42 | if (mode == H263_MODE) { |
| 43 | switch (omxProfile) { |
| 44 | case OMX_VIDEO_H263ProfileBaseline: |
| 45 | if (omxLevel > OMX_VIDEO_H263Level45) { |
| 46 | LOGE("Unsupported level (%d) for H263", omxLevel); |
| 47 | return BAD_VALUE; |
| 48 | } else { |
| 49 | LOGW("PV does not support level configuration for H263"); |
| 50 | profileLevel = CORE_PROFILE_LEVEL2; |
| 51 | break; |
| 52 | } |
James Dong | 0244d94 | 2010-11-09 15:00:00 -0800 | [diff] [blame] | 53 | break; |
James Dong | af50969 | 2010-10-19 17:35:35 -0700 | [diff] [blame] | 54 | default: |
| 55 | LOGE("Unsupported profile (%d) for H263", omxProfile); |
| 56 | return BAD_VALUE; |
| 57 | } |
| 58 | } else { // MPEG4 |
| 59 | switch (omxProfile) { |
| 60 | case OMX_VIDEO_MPEG4ProfileSimple: |
| 61 | switch (omxLevel) { |
| 62 | case OMX_VIDEO_MPEG4Level0b: |
| 63 | profileLevel = SIMPLE_PROFILE_LEVEL0; |
| 64 | break; |
| 65 | case OMX_VIDEO_MPEG4Level1: |
| 66 | profileLevel = SIMPLE_PROFILE_LEVEL1; |
| 67 | break; |
| 68 | case OMX_VIDEO_MPEG4Level2: |
| 69 | profileLevel = SIMPLE_PROFILE_LEVEL2; |
| 70 | break; |
| 71 | case OMX_VIDEO_MPEG4Level3: |
| 72 | profileLevel = SIMPLE_PROFILE_LEVEL3; |
| 73 | break; |
| 74 | default: |
| 75 | LOGE("Unsupported level (%d) for MPEG4 simple profile", |
| 76 | omxLevel); |
| 77 | return BAD_VALUE; |
James Dong | 0244d94 | 2010-11-09 15:00:00 -0800 | [diff] [blame] | 78 | } |
| 79 | break; |
James Dong | af50969 | 2010-10-19 17:35:35 -0700 | [diff] [blame] | 80 | case OMX_VIDEO_MPEG4ProfileSimpleScalable: |
| 81 | switch (omxLevel) { |
| 82 | case OMX_VIDEO_MPEG4Level0b: |
| 83 | profileLevel = SIMPLE_SCALABLE_PROFILE_LEVEL0; |
| 84 | break; |
| 85 | case OMX_VIDEO_MPEG4Level1: |
| 86 | profileLevel = SIMPLE_SCALABLE_PROFILE_LEVEL1; |
| 87 | break; |
| 88 | case OMX_VIDEO_MPEG4Level2: |
| 89 | profileLevel = SIMPLE_SCALABLE_PROFILE_LEVEL2; |
| 90 | break; |
| 91 | default: |
| 92 | LOGE("Unsupported level (%d) for MPEG4 simple " |
| 93 | "scalable profile", omxLevel); |
| 94 | return BAD_VALUE; |
| 95 | } |
James Dong | 0244d94 | 2010-11-09 15:00:00 -0800 | [diff] [blame] | 96 | break; |
James Dong | af50969 | 2010-10-19 17:35:35 -0700 | [diff] [blame] | 97 | case OMX_VIDEO_MPEG4ProfileCore: |
| 98 | switch (omxLevel) { |
| 99 | case OMX_VIDEO_MPEG4Level1: |
| 100 | profileLevel = CORE_PROFILE_LEVEL1; |
| 101 | break; |
| 102 | case OMX_VIDEO_MPEG4Level2: |
| 103 | profileLevel = CORE_PROFILE_LEVEL2; |
| 104 | break; |
| 105 | default: |
| 106 | LOGE("Unsupported level (%d) for MPEG4 core " |
| 107 | "profile", omxLevel); |
| 108 | return BAD_VALUE; |
| 109 | } |
James Dong | 0244d94 | 2010-11-09 15:00:00 -0800 | [diff] [blame] | 110 | break; |
James Dong | af50969 | 2010-10-19 17:35:35 -0700 | [diff] [blame] | 111 | case OMX_VIDEO_MPEG4ProfileCoreScalable: |
| 112 | switch (omxLevel) { |
| 113 | case OMX_VIDEO_MPEG4Level1: |
| 114 | profileLevel = CORE_SCALABLE_PROFILE_LEVEL1; |
| 115 | break; |
| 116 | case OMX_VIDEO_MPEG4Level2: |
| 117 | profileLevel = CORE_SCALABLE_PROFILE_LEVEL2; |
| 118 | break; |
| 119 | case OMX_VIDEO_MPEG4Level3: |
| 120 | profileLevel = CORE_SCALABLE_PROFILE_LEVEL3; |
| 121 | break; |
| 122 | default: |
| 123 | LOGE("Unsupported level (%d) for MPEG4 core " |
| 124 | "scalable profile", omxLevel); |
| 125 | return BAD_VALUE; |
| 126 | } |
James Dong | 0244d94 | 2010-11-09 15:00:00 -0800 | [diff] [blame] | 127 | break; |
James Dong | af50969 | 2010-10-19 17:35:35 -0700 | [diff] [blame] | 128 | default: |
| 129 | LOGE("Unsupported MPEG4 profile (%d)", omxProfile); |
| 130 | return BAD_VALUE; |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | *pvProfileLevel = profileLevel; |
| 135 | return OK; |
| 136 | } |
| 137 | |
James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 138 | inline static void ConvertYUV420SemiPlanarToYUV420Planar( |
| 139 | uint8_t *inyuv, uint8_t* outyuv, |
| 140 | int32_t width, int32_t height) { |
| 141 | |
| 142 | int32_t outYsize = width * height; |
| 143 | uint32_t *outy = (uint32_t *) outyuv; |
| 144 | uint16_t *outcb = (uint16_t *) (outyuv + outYsize); |
| 145 | uint16_t *outcr = (uint16_t *) (outyuv + outYsize + (outYsize >> 2)); |
| 146 | |
| 147 | /* Y copying */ |
| 148 | memcpy(outy, inyuv, outYsize); |
| 149 | |
| 150 | /* U & V copying */ |
| 151 | uint32_t *inyuv_4 = (uint32_t *) (inyuv + outYsize); |
| 152 | for (int32_t i = height >> 1; i > 0; --i) { |
| 153 | for (int32_t j = width >> 2; j > 0; --j) { |
| 154 | uint32_t temp = *inyuv_4++; |
| 155 | uint32_t tempU = temp & 0xFF; |
| 156 | tempU = tempU | ((temp >> 8) & 0xFF00); |
| 157 | |
| 158 | uint32_t tempV = (temp >> 8) & 0xFF; |
| 159 | tempV = tempV | ((temp >> 16) & 0xFF00); |
| 160 | |
| 161 | // Flip U and V |
| 162 | *outcb++ = tempV; |
| 163 | *outcr++ = tempU; |
| 164 | } |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | M4vH263Encoder::M4vH263Encoder( |
| 169 | const sp<MediaSource>& source, |
| 170 | const sp<MetaData>& meta) |
| 171 | : mSource(source), |
| 172 | mMeta(meta), |
| 173 | mNumInputFrames(-1), |
| 174 | mNextModTimeUs(0), |
James Dong | e95d192 | 2010-08-12 15:41:11 -0700 | [diff] [blame] | 175 | mPrevTimestampUs(-1), |
James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 176 | mStarted(false), |
| 177 | mInputBuffer(NULL), |
| 178 | mInputFrameData(NULL), |
| 179 | mGroup(NULL) { |
| 180 | |
James Dong | 6312dd6 | 2010-12-02 14:48:23 -0800 | [diff] [blame] | 181 | LOGI("Construct software M4vH263Encoder"); |
James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 182 | |
| 183 | mHandle = new tagvideoEncControls; |
| 184 | memset(mHandle, 0, sizeof(tagvideoEncControls)); |
| 185 | |
| 186 | mInitCheck = initCheck(meta); |
| 187 | } |
| 188 | |
| 189 | M4vH263Encoder::~M4vH263Encoder() { |
Steve Block | 71f2cf1 | 2011-10-20 11:56:00 +0100 | [diff] [blame] | 190 | ALOGV("Destruct software M4vH263Encoder"); |
James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 191 | if (mStarted) { |
| 192 | stop(); |
| 193 | } |
| 194 | |
| 195 | delete mEncParams; |
| 196 | delete mHandle; |
| 197 | } |
| 198 | |
| 199 | status_t M4vH263Encoder::initCheck(const sp<MetaData>& meta) { |
Steve Block | 71f2cf1 | 2011-10-20 11:56:00 +0100 | [diff] [blame] | 200 | ALOGV("initCheck"); |
James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 201 | CHECK(meta->findInt32(kKeyWidth, &mVideoWidth)); |
| 202 | CHECK(meta->findInt32(kKeyHeight, &mVideoHeight)); |
James Dong | aac193c | 2010-11-10 20:43:53 -0800 | [diff] [blame] | 203 | CHECK(meta->findInt32(kKeyFrameRate, &mVideoFrameRate)); |
James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 204 | CHECK(meta->findInt32(kKeyBitRate, &mVideoBitRate)); |
| 205 | |
| 206 | // XXX: Add more color format support |
| 207 | CHECK(meta->findInt32(kKeyColorFormat, &mVideoColorFormat)); |
| 208 | if (mVideoColorFormat != OMX_COLOR_FormatYUV420Planar) { |
| 209 | if (mVideoColorFormat != OMX_COLOR_FormatYUV420SemiPlanar) { |
| 210 | LOGE("Color format %d is not supported", mVideoColorFormat); |
| 211 | return BAD_VALUE; |
| 212 | } |
| 213 | // Allocate spare buffer only when color conversion is needed. |
| 214 | // Assume the color format is OMX_COLOR_FormatYUV420SemiPlanar. |
| 215 | mInputFrameData = |
| 216 | (uint8_t *) malloc((mVideoWidth * mVideoHeight * 3 ) >> 1); |
| 217 | CHECK(mInputFrameData); |
| 218 | } |
| 219 | |
| 220 | // XXX: Remove this restriction |
| 221 | if (mVideoWidth % 16 != 0 || mVideoHeight % 16 != 0) { |
| 222 | LOGE("Video frame size %dx%d must be a multiple of 16", |
| 223 | mVideoWidth, mVideoHeight); |
| 224 | return BAD_VALUE; |
| 225 | } |
| 226 | |
| 227 | mEncParams = new tagvideoEncOptions; |
| 228 | memset(mEncParams, 0, sizeof(tagvideoEncOptions)); |
| 229 | if (!PVGetDefaultEncOption(mEncParams, 0)) { |
| 230 | LOGE("Failed to get default encoding parameters"); |
| 231 | return BAD_VALUE; |
| 232 | } |
| 233 | |
| 234 | // Need to know which role the encoder is in. |
| 235 | // XXX: Set the mode proper for other types of applications |
| 236 | // like streaming or video conference |
| 237 | const char *mime; |
| 238 | CHECK(meta->findCString(kKeyMIMEType, &mime)); |
| 239 | CHECK(!strcmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || |
| 240 | !strcmp(mime, MEDIA_MIMETYPE_VIDEO_H263)); |
| 241 | if (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)) { |
| 242 | mEncParams->encMode = COMBINE_MODE_WITH_ERR_RES; |
| 243 | } else { |
| 244 | mEncParams->encMode = H263_MODE; |
| 245 | } |
| 246 | mEncParams->encWidth[0] = mVideoWidth; |
| 247 | mEncParams->encHeight[0] = mVideoHeight; |
| 248 | mEncParams->encFrameRate[0] = mVideoFrameRate; |
| 249 | mEncParams->rcType = VBR_1; |
| 250 | mEncParams->vbvDelay = (float)5.0; |
| 251 | |
| 252 | // Set profile and level |
| 253 | // If profile and level setting is not correct, failure |
| 254 | // is reported when the encoder is initialized. |
| 255 | mEncParams->profile_level = CORE_PROFILE_LEVEL2; |
James Dong | af50969 | 2010-10-19 17:35:35 -0700 | [diff] [blame] | 256 | int32_t profile, level; |
| 257 | if (meta->findInt32(kKeyVideoProfile, &profile) && |
| 258 | meta->findInt32(kKeyVideoLevel, &level)) { |
| 259 | if (OK != ConvertOmxProfileLevel( |
| 260 | mEncParams->encMode, profile, level, |
| 261 | &mEncParams->profile_level)) { |
| 262 | return BAD_VALUE; |
| 263 | } |
James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 264 | } |
| 265 | |
| 266 | mEncParams->packetSize = 32; |
| 267 | mEncParams->rvlcEnable = PV_OFF; |
| 268 | mEncParams->numLayers = 1; |
| 269 | mEncParams->timeIncRes = 1000; |
| 270 | mEncParams->tickPerSrc = mEncParams->timeIncRes / mVideoFrameRate; |
| 271 | |
| 272 | mEncParams->bitRate[0] = mVideoBitRate; |
| 273 | mEncParams->iQuant[0] = 15; |
| 274 | mEncParams->pQuant[0] = 12; |
| 275 | mEncParams->quantType[0] = 0; |
| 276 | mEncParams->noFrameSkipped = PV_OFF; |
| 277 | |
| 278 | // Set IDR frame refresh interval |
| 279 | int32_t iFramesIntervalSec; |
| 280 | CHECK(meta->findInt32(kKeyIFramesInterval, &iFramesIntervalSec)); |
| 281 | if (iFramesIntervalSec < 0) { |
| 282 | mEncParams->intraPeriod = -1; |
| 283 | } else if (iFramesIntervalSec == 0) { |
| 284 | mEncParams->intraPeriod = 1; // All I frames |
| 285 | } else { |
| 286 | mEncParams->intraPeriod = |
| 287 | (iFramesIntervalSec * mVideoFrameRate); |
| 288 | } |
| 289 | |
| 290 | mEncParams->numIntraMB = 0; |
| 291 | mEncParams->sceneDetect = PV_ON; |
| 292 | mEncParams->searchRange = 16; |
| 293 | mEncParams->mv8x8Enable = PV_OFF; |
| 294 | mEncParams->gobHeaderInterval = 0; |
| 295 | mEncParams->useACPred = PV_ON; |
| 296 | mEncParams->intraDCVlcTh = 0; |
| 297 | |
| 298 | mFormat = new MetaData; |
| 299 | mFormat->setInt32(kKeyWidth, mVideoWidth); |
| 300 | mFormat->setInt32(kKeyHeight, mVideoHeight); |
| 301 | mFormat->setInt32(kKeyBitRate, mVideoBitRate); |
James Dong | aac193c | 2010-11-10 20:43:53 -0800 | [diff] [blame] | 302 | mFormat->setInt32(kKeyFrameRate, mVideoFrameRate); |
James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 303 | mFormat->setInt32(kKeyColorFormat, mVideoColorFormat); |
| 304 | |
| 305 | mFormat->setCString(kKeyMIMEType, mime); |
| 306 | mFormat->setCString(kKeyDecoderComponent, "M4vH263Encoder"); |
| 307 | return OK; |
| 308 | } |
| 309 | |
| 310 | status_t M4vH263Encoder::start(MetaData *params) { |
Steve Block | 71f2cf1 | 2011-10-20 11:56:00 +0100 | [diff] [blame] | 311 | ALOGV("start"); |
James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 312 | if (mInitCheck != OK) { |
| 313 | return mInitCheck; |
| 314 | } |
| 315 | |
| 316 | if (mStarted) { |
| 317 | LOGW("Call start() when encoder already started"); |
| 318 | return OK; |
| 319 | } |
| 320 | |
| 321 | if (!PVInitVideoEncoder(mHandle, mEncParams)) { |
| 322 | LOGE("Failed to initialize the encoder"); |
| 323 | return UNKNOWN_ERROR; |
| 324 | } |
| 325 | |
| 326 | mGroup = new MediaBufferGroup(); |
| 327 | int32_t maxSize; |
| 328 | if (!PVGetMaxVideoFrameSize(mHandle, &maxSize)) { |
| 329 | maxSize = 256 * 1024; // Magic # |
| 330 | } |
Steve Block | 71f2cf1 | 2011-10-20 11:56:00 +0100 | [diff] [blame] | 331 | ALOGV("Max output buffer size: %d", maxSize); |
James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 332 | mGroup->add_buffer(new MediaBuffer(maxSize)); |
| 333 | |
| 334 | mSource->start(params); |
| 335 | mNumInputFrames = -1; // 1st frame contains codec specific data |
| 336 | mStarted = true; |
| 337 | |
| 338 | return OK; |
| 339 | } |
| 340 | |
| 341 | status_t M4vH263Encoder::stop() { |
Steve Block | 71f2cf1 | 2011-10-20 11:56:00 +0100 | [diff] [blame] | 342 | ALOGV("stop"); |
James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 343 | if (!mStarted) { |
| 344 | LOGW("Call stop() when encoder has not started"); |
| 345 | return OK; |
| 346 | } |
| 347 | |
| 348 | if (mInputBuffer) { |
| 349 | mInputBuffer->release(); |
| 350 | mInputBuffer = NULL; |
| 351 | } |
| 352 | |
| 353 | if (mGroup) { |
| 354 | delete mGroup; |
| 355 | mGroup = NULL; |
| 356 | } |
| 357 | |
| 358 | if (mInputFrameData) { |
| 359 | delete mInputFrameData; |
| 360 | mInputFrameData = NULL; |
| 361 | } |
| 362 | |
| 363 | CHECK(PVCleanUpVideoEncoder(mHandle)); |
| 364 | |
| 365 | mSource->stop(); |
| 366 | mStarted = false; |
| 367 | |
| 368 | return OK; |
| 369 | } |
| 370 | |
| 371 | sp<MetaData> M4vH263Encoder::getFormat() { |
Steve Block | 71f2cf1 | 2011-10-20 11:56:00 +0100 | [diff] [blame] | 372 | ALOGV("getFormat"); |
James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 373 | return mFormat; |
| 374 | } |
| 375 | |
| 376 | status_t M4vH263Encoder::read( |
| 377 | MediaBuffer **out, const ReadOptions *options) { |
| 378 | |
James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 379 | *out = NULL; |
| 380 | |
| 381 | MediaBuffer *outputBuffer; |
| 382 | CHECK_EQ(OK, mGroup->acquire_buffer(&outputBuffer)); |
| 383 | uint8_t *outPtr = (uint8_t *) outputBuffer->data(); |
| 384 | int32_t dataLength = outputBuffer->size(); |
| 385 | |
| 386 | // Output codec specific data |
| 387 | if (mNumInputFrames < 0) { |
| 388 | if (!PVGetVolHeader(mHandle, outPtr, &dataLength, 0)) { |
| 389 | LOGE("Failed to get VOL header"); |
| 390 | return UNKNOWN_ERROR; |
| 391 | } |
Steve Block | 71f2cf1 | 2011-10-20 11:56:00 +0100 | [diff] [blame] | 392 | ALOGV("Output VOL header: %d bytes", dataLength); |
James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 393 | outputBuffer->meta_data()->setInt32(kKeyIsCodecConfig, 1); |
| 394 | outputBuffer->set_range(0, dataLength); |
| 395 | *out = outputBuffer; |
| 396 | ++mNumInputFrames; |
| 397 | return OK; |
| 398 | } |
| 399 | |
| 400 | // Ready for accepting an input video frame |
James Dong | d39e8f4 | 2011-06-29 22:35:59 -0700 | [diff] [blame] | 401 | status_t err = mSource->read(&mInputBuffer, options); |
| 402 | if (OK != err) { |
| 403 | if (err != ERROR_END_OF_STREAM) { |
| 404 | LOGE("Failed to read from data source"); |
| 405 | } |
James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 406 | outputBuffer->release(); |
James Dong | d39e8f4 | 2011-06-29 22:35:59 -0700 | [diff] [blame] | 407 | return err; |
James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 408 | } |
James Dong | e6daea5 | 2010-08-09 17:45:29 -0700 | [diff] [blame] | 409 | |
| 410 | if (mInputBuffer->size() - ((mVideoWidth * mVideoHeight * 3) >> 1) != 0) { |
| 411 | outputBuffer->release(); |
| 412 | mInputBuffer->release(); |
| 413 | mInputBuffer = NULL; |
| 414 | return UNKNOWN_ERROR; |
| 415 | } |
| 416 | |
James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 417 | int64_t timeUs; |
| 418 | CHECK(mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)); |
James Dong | 708ec39 | 2010-08-11 17:29:09 -0700 | [diff] [blame] | 419 | |
| 420 | // When the timestamp of the current sample is the same as that |
| 421 | // of the previous sample, encoding of the current sample is |
| 422 | // bypassed, and the output length of the sample is set to 0 |
| 423 | if (mNumInputFrames >= 1 && |
| 424 | (mNextModTimeUs > timeUs || mPrevTimestampUs == timeUs)) { |
| 425 | // Frame arrives too late |
James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 426 | outputBuffer->set_range(0, 0); |
| 427 | *out = outputBuffer; |
| 428 | mInputBuffer->release(); |
| 429 | mInputBuffer = NULL; |
| 430 | return OK; |
| 431 | } |
| 432 | |
James Dong | 708ec39 | 2010-08-11 17:29:09 -0700 | [diff] [blame] | 433 | // Don't accept out-of-order samples |
| 434 | CHECK(mPrevTimestampUs < timeUs); |
| 435 | mPrevTimestampUs = timeUs; |
| 436 | |
James Dong | 42ef0c7 | 2010-07-12 21:46:25 -0700 | [diff] [blame] | 437 | // Color convert to OMX_COLOR_FormatYUV420Planar if necessary |
| 438 | outputBuffer->meta_data()->setInt64(kKeyTime, timeUs); |
| 439 | uint8_t *inPtr = (uint8_t *) mInputBuffer->data(); |
| 440 | if (mVideoColorFormat != OMX_COLOR_FormatYUV420Planar) { |
| 441 | CHECK(mInputFrameData); |
| 442 | CHECK(mVideoColorFormat == OMX_COLOR_FormatYUV420SemiPlanar); |
| 443 | ConvertYUV420SemiPlanarToYUV420Planar( |
| 444 | inPtr, mInputFrameData, mVideoWidth, mVideoHeight); |
| 445 | inPtr = mInputFrameData; |
| 446 | } |
| 447 | CHECK(inPtr != NULL); |
| 448 | |
| 449 | // Ready for encoding a video frame |
| 450 | VideoEncFrameIO vin, vout; |
| 451 | vin.height = ((mVideoHeight + 15) >> 4) << 4; |
| 452 | vin.pitch = ((mVideoWidth + 15) >> 4) << 4; |
| 453 | vin.timestamp = (timeUs + 500) / 1000; // in ms |
| 454 | vin.yChan = inPtr; |
| 455 | vin.uChan = vin.yChan + vin.height * vin.pitch; |
| 456 | vin.vChan = vin.uChan + ((vin.height * vin.pitch) >> 2); |
| 457 | unsigned long modTimeMs = 0; |
| 458 | int32_t nLayer = 0; |
| 459 | MP4HintTrack hintTrack; |
| 460 | if (!PVEncodeVideoFrame(mHandle, &vin, &vout, |
| 461 | &modTimeMs, outPtr, &dataLength, &nLayer) || |
| 462 | !PVGetHintTrack(mHandle, &hintTrack)) { |
| 463 | LOGE("Failed to encode frame or get hink track at frame %lld", |
| 464 | mNumInputFrames); |
| 465 | outputBuffer->release(); |
| 466 | mInputBuffer->release(); |
| 467 | mInputBuffer = NULL; |
| 468 | return UNKNOWN_ERROR; |
| 469 | } |
| 470 | CHECK_EQ(NULL, PVGetOverrunBuffer(mHandle)); |
| 471 | if (hintTrack.CodeType == 0) { // I-frame serves as sync frame |
| 472 | outputBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); |
| 473 | } |
| 474 | |
| 475 | ++mNumInputFrames; |
| 476 | mNextModTimeUs = modTimeMs * 1000LL; |
| 477 | outputBuffer->set_range(0, dataLength); |
| 478 | *out = outputBuffer; |
| 479 | mInputBuffer->release(); |
| 480 | mInputBuffer = NULL; |
| 481 | return OK; |
| 482 | } |
| 483 | |
| 484 | void M4vH263Encoder::signalBufferReturned(MediaBuffer *buffer) { |
| 485 | } |
| 486 | |
| 487 | } // namespace android |