1 /*
2 * Copyright (c) 2009-2011 Intel Corporation.  All rights reserved.
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 "PVSoftMPEG4Encoder"
19 #include <wrs_omxil_core/log.h>
20 
21 #include "mp4enc_api.h"
22 #include "OMX_Video.h"
23 
24 #include <media/stagefright/foundation/ADebug.h>
25 #include <media/stagefright/MediaDefs.h>
26 #include <media/stagefright/MediaErrors.h>
27 #include <media/stagefright/MetaData.h>
28 #include <media/stagefright/Utils.h>
29 
30 #include <ui/GraphicBufferMapper.h>
31 #include <ui/Rect.h>
32 
33 #include "PVSoftMPEG4Encoder.h"
34 #include "VideoEncoderLog.h"
35 
36 #define ALIGN(x, align)                  (((x) + (align) - 1) & (~((align) - 1)))
37 
ConvertYUV420SemiPlanarToYUV420Planar(uint8_t * inyuv,uint8_t * outyuv,int32_t width,int32_t height)38 inline static void ConvertYUV420SemiPlanarToYUV420Planar(
39         uint8_t *inyuv, uint8_t* outyuv,
40         int32_t width, int32_t height) {
41 
42     int32_t outYsize = width * height;
43     uint32_t *outy =  (uint32_t *) outyuv;
44     uint16_t *outcb = (uint16_t *) (outyuv + outYsize);
45     uint16_t *outcr = (uint16_t *) (outyuv + outYsize + (outYsize >> 2));
46 
47     /* Y copying */
48     memcpy(outy, inyuv, outYsize);
49 
50     /* U & V copying */
51     uint32_t *inyuv_4 = (uint32_t *) (inyuv + outYsize);
52     for (int32_t i = height >> 1; i > 0; --i) {
53         for (int32_t j = width >> 2; j > 0; --j) {
54             uint32_t temp = *inyuv_4++;
55             uint32_t tempU = temp & 0xFF;
56             tempU = tempU | ((temp >> 8) & 0xFF00);
57 
58             uint32_t tempV = (temp >> 8) & 0xFF;
59             tempV = tempV | ((temp >> 16) & 0xFF00);
60 
61             // Flip U and V
62             *outcb++ = tempU;
63             *outcr++ = tempV;
64         }
65     }
66 }
67 
trimBuffer(uint8_t * dataIn,uint8_t * dataOut,int32_t width,int32_t height,int32_t alignedHeight,int32_t stride)68 inline static void trimBuffer(uint8_t *dataIn, uint8_t *dataOut,
69         int32_t width, int32_t height,
70         int32_t alignedHeight, int32_t stride) {
71     int32_t h;
72     uint8_t *y_start, *uv_start, *_y_start, *_uv_start;
73     y_start = dataOut;
74     uv_start = dataOut + width * height;
75     _y_start = dataIn;
76     _uv_start = dataIn + stride * alignedHeight;
77 
78     for (h = 0; h < height; h++)
79         memcpy(y_start + h * width, _y_start + h * stride, width);
80     for (h = 0; h < height / 2; h++)
81         memcpy(uv_start + h * width,
82                 _uv_start + h * stride, width);
83 }
84 
PVSoftMPEG4Encoder(const char * name)85 PVSoftMPEG4Encoder::PVSoftMPEG4Encoder(const char *name)
86     :  mEncodeMode(COMBINE_MODE_WITH_ERR_RES),
87       mVideoWidth(176),
88       mVideoHeight(144),
89       mVideoFrameRate(30),
90       mVideoBitRate(192000),
91       mVideoColorFormat(OMX_COLOR_FormatYUV420SemiPlanar),
92       mStoreMetaDataInBuffers(false),
93       mIDRFrameRefreshIntervalInSec(1),
94       mNumInputFrames(-1),
95       mStarted(false),
96       mSawInputEOS(false),
97       mSignalledError(false),
98       mHandle(new tagvideoEncControls),
99       mEncParams(new tagvideoEncOptions),
100       mInputFrameData(NULL)
101 {
102 
103     if (!strcmp(name, "OMX.google.h263.encoder")) {
104         mEncodeMode = H263_MODE;
105         LOG_I("construct h263 encoder");
106     } else {
107         CHECK(!strcmp(name, "OMX.google.mpeg4.encoder"));
108         LOG_I("construct mpeg4 encoder");
109     }
110 
111     setDefaultParams();
112 #if NO_BUFFER_SHARE
113     mVASurfaceMappingAction |= MAPACT_COPY;
114 #endif
115 
116     LOG_I("Construct PVSoftMPEG4Encoder");
117 
118 }
119 
~PVSoftMPEG4Encoder()120 PVSoftMPEG4Encoder::~PVSoftMPEG4Encoder() {
121     LOG_I("Destruct PVSoftMPEG4Encoder");
122     releaseEncoder();
123 
124 }
125 
setDefaultParams()126 void PVSoftMPEG4Encoder::setDefaultParams() {
127 
128     // Set default value for input parameters
129     mComParams.profile = VAProfileH264Baseline;
130     mComParams.level = 41;
131     mComParams.rawFormat = RAW_FORMAT_NV12;
132     mComParams.frameRate.frameRateNum = 30;
133     mComParams.frameRate.frameRateDenom = 1;
134     mComParams.resolution.width = 0;
135     mComParams.resolution.height = 0;
136     mComParams.intraPeriod = 30;
137     mComParams.rcMode = RATE_CONTROL_NONE;
138     mComParams.rcParams.initQP = 15;
139     mComParams.rcParams.minQP = 0;
140     mComParams.rcParams.bitRate = 640000;
141     mComParams.rcParams.targetPercentage= 0;
142     mComParams.rcParams.windowSize = 0;
143     mComParams.rcParams.disableFrameSkip = 0;
144     mComParams.rcParams.disableBitsStuffing = 1;
145     mComParams.cyclicFrameInterval = 30;
146     mComParams.refreshType = VIDEO_ENC_NONIR;
147     mComParams.airParams.airMBs = 0;
148     mComParams.airParams.airThreshold = 0;
149     mComParams.airParams.airAuto = 1;
150     mComParams.disableDeblocking = 2;
151     mComParams.syncEncMode = false;
152     mComParams.codedBufNum = 2;
153 
154 }
155 
initEncParams()156 Encode_Status PVSoftMPEG4Encoder::initEncParams() {
157     CHECK(mHandle != NULL);
158     memset(mHandle, 0, sizeof(tagvideoEncControls));
159 
160     CHECK(mEncParams != NULL);
161     memset(mEncParams, 0, sizeof(tagvideoEncOptions));
162     if (!PVGetDefaultEncOption(mEncParams, 0)) {
163         LOG_E("Failed to get default encoding parameters");
164         return ENCODE_FAIL;
165     }
166     mEncParams->encMode = mEncodeMode;
167     mEncParams->encWidth[0] = mVideoWidth;
168     mEncParams->encHeight[0] = mVideoHeight;
169     mEncParams->encFrameRate[0] = mVideoFrameRate;
170     mEncParams->rcType = VBR_1;
171     mEncParams->vbvDelay = 5.0f;
172 
173     // FIXME:
174     // Add more profile and level support for MPEG4 encoder
175     mEncParams->profile_level = CORE_PROFILE_LEVEL2;
176     mEncParams->packetSize = 32;
177     mEncParams->rvlcEnable = PV_OFF;
178     mEncParams->numLayers = 1;
179     mEncParams->timeIncRes = 1000;
180     mEncParams->tickPerSrc = mEncParams->timeIncRes / mVideoFrameRate;
181 
182     mEncParams->bitRate[0] = mVideoBitRate <= 2000000 ? mVideoBitRate : 2000000;
183     mEncParams->iQuant[0] = 15;
184     mEncParams->pQuant[0] = 12;
185     mEncParams->quantType[0] = 0;
186     mEncParams->noFrameSkipped = PV_OFF;
187 
188     mTrimedInputData =
189         (uint8_t *) malloc((mVideoWidth * mVideoHeight * 3 ) >> 1);
190     CHECK(mTrimedInputData != NULL);
191 
192     if (mVideoColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) {
193         // Color conversion is needed.
194         CHECK(mInputFrameData == NULL);
195         mInputFrameData =
196             (uint8_t *) malloc((mVideoWidth * mVideoHeight * 3 ) >> 1);
197         CHECK(mInputFrameData != NULL);
198     }
199 
200     // PV's MPEG4 encoder requires the video dimension of multiple
201     if (mVideoWidth % 16 != 0 || mVideoHeight % 16 != 0) {
202         LOG_E("Video frame size %dx%d must be a multiple of 16",
203             mVideoWidth, mVideoHeight);
204         return ENCODE_INVALID_PARAMS;
205     }
206 
207     // Set IDR frame refresh interval
208     if (mIDRFrameRefreshIntervalInSec < 0) {
209         mEncParams->intraPeriod = -1;
210     } else if (mIDRFrameRefreshIntervalInSec == 0) {
211         mEncParams->intraPeriod = 1;  // All I frames
212     } else {
213         mEncParams->intraPeriod =
214             (mIDRFrameRefreshIntervalInSec * mVideoFrameRate);
215     }
216 
217     mEncParams->numIntraMB = 0;
218     mEncParams->sceneDetect = PV_ON;
219     mEncParams->searchRange = 16;
220     mEncParams->mv8x8Enable = PV_OFF;
221     mEncParams->gobHeaderInterval = 0;
222     mEncParams->useACPred = PV_ON;
223     mEncParams->intraDCVlcTh = 0;
224 
225     return ENCODE_SUCCESS;
226 }
227 
initEncoder()228 Encode_Status PVSoftMPEG4Encoder::initEncoder() {
229     LOG_V("Begin\n");
230 
231     CHECK(!mStarted);
232 
233     Encode_Status ret = ENCODE_SUCCESS;
234     if (ENCODE_SUCCESS != (ret = initEncParams())) {
235         LOG_E("Failed to initialized encoder params");
236         mSignalledError = true;
237         return ret;
238     }
239 
240     if (!PVInitVideoEncoder(mHandle, mEncParams)) {
241         LOG_E("Failed to initialize the encoder");
242         mSignalledError = true;
243         return ENCODE_FAIL;
244     }
245 
246     mNumInputFrames = -1;  // 1st buffer for codec specific data
247     mStarted = true;
248     mCurTimestampUs = 0;
249     mLastTimestampUs = 0;
250     mVolHeaderLength = 256;
251 
252     LOG_V("End\n");
253 
254     return ENCODE_SUCCESS;
255 }
256 
releaseEncoder()257 Encode_Status PVSoftMPEG4Encoder::releaseEncoder() {
258     LOG_V("Begin\n");
259 
260     if (!mStarted) {
261         return ENCODE_SUCCESS;
262     }
263 
264     PVCleanUpVideoEncoder(mHandle);
265 
266     delete mTrimedInputData;
267     mTrimedInputData = NULL;
268 
269     delete mInputFrameData;
270     mInputFrameData = NULL;
271 
272     delete mEncParams;
273     mEncParams = NULL;
274 
275     delete mHandle;
276     mHandle = NULL;
277 
278     mStarted = false;
279 
280     LOG_V("End\n");
281 
282     return ENCODE_SUCCESS;
283 }
284 
setParameters(VideoParamConfigSet * videoEncParams)285 Encode_Status PVSoftMPEG4Encoder::setParameters(
286         VideoParamConfigSet *videoEncParams)
287 {
288 
289     Encode_Status ret = ENCODE_SUCCESS;
290     CHECK_NULL_RETURN_IFFAIL(videoEncParams);
291     LOG_I("Config type = %d\n", (int)videoEncParams->type);
292 
293     if (mStarted) {
294         LOG_E("Encoder has been initialized, should use setConfig to change configurations\n");
295         return ENCODE_ALREADY_INIT;
296     }
297 
298     switch (videoEncParams->type) {
299         case VideoParamsTypeCommon: {
300 
301             VideoParamsCommon *paramsCommon =
302                     reinterpret_cast <VideoParamsCommon *> (videoEncParams);
303             if (paramsCommon->size != sizeof (VideoParamsCommon)) {
304                 return ENCODE_INVALID_PARAMS;
305             }
306             if(paramsCommon->codedBufNum < 2)
307                 paramsCommon->codedBufNum =2;
308             mComParams = *paramsCommon;
309 
310             mVideoWidth = mComParams.resolution.width;
311             mVideoHeight = mComParams.resolution.height;
312             mVideoFrameRate =  mComParams.frameRate.frameRateNum / \
313                                mComParams.frameRate.frameRateDenom;
314             mVideoBitRate = mComParams.rcParams.bitRate;
315             mVideoColorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
316             break;
317         }
318 
319         case VideoParamsTypeStoreMetaDataInBuffers: {
320             VideoParamsStoreMetaDataInBuffers *metadata =
321                     reinterpret_cast <VideoParamsStoreMetaDataInBuffers *> (videoEncParams);
322 
323             if (metadata->size != sizeof (VideoParamsStoreMetaDataInBuffers)) {
324                 return ENCODE_INVALID_PARAMS;
325             }
326 
327             mStoreMetaDataInBuffers = metadata->isEnabled;
328 
329             break;
330         }
331 
332         default: {
333             LOG_I ("Wrong ParamType here\n");
334             break;
335         }
336     }
337 
338     return ret;
339 }
340 
getParameters(VideoParamConfigSet * videoEncParams)341 Encode_Status PVSoftMPEG4Encoder::getParameters(
342         VideoParamConfigSet *videoEncParams) {
343 
344     Encode_Status ret = ENCODE_SUCCESS;
345     CHECK_NULL_RETURN_IFFAIL(videoEncParams);
346     LOG_I("Config type = %d\n", (int)videoEncParams->type);
347 
348     switch (videoEncParams->type) {
349         case VideoParamsTypeCommon: {
350 
351             VideoParamsCommon *paramsCommon =
352                     reinterpret_cast <VideoParamsCommon *> (videoEncParams);
353 
354             if (paramsCommon->size != sizeof (VideoParamsCommon)) {
355                 return ENCODE_INVALID_PARAMS;
356             }
357             *paramsCommon = mComParams;
358             break;
359         }
360 
361         case VideoParamsTypeStoreMetaDataInBuffers: {
362             VideoParamsStoreMetaDataInBuffers *metadata =
363                     reinterpret_cast <VideoParamsStoreMetaDataInBuffers *> (videoEncParams);
364 
365             if (metadata->size != sizeof (VideoParamsStoreMetaDataInBuffers)) {
366                 return ENCODE_INVALID_PARAMS;
367             }
368 
369             metadata->isEnabled = mStoreMetaDataInBuffers;
370 
371             break;
372         }
373 
374         default: {
375             LOG_I ("Wrong ParamType here\n");
376             break;
377         }
378 
379     }
380     return ret;
381 }
382 
encode(VideoEncRawBuffer * inBuffer,uint32_t timeout)383 Encode_Status PVSoftMPEG4Encoder::encode(VideoEncRawBuffer *inBuffer, uint32_t timeout)
384 {
385     LOG_V("Begin\n");
386 
387     Encode_Status ret = ENCODE_SUCCESS;
388 
389     if (mCurTimestampUs <= inBuffer->timeStamp) {
390         mLastTimestampUs = mCurTimestampUs;
391         mCurTimestampUs = inBuffer->timeStamp;
392     }
393 
394     if (mNumInputFrames < 0) {
395         if (!PVGetVolHeader(mHandle, mVolHeader, &mVolHeaderLength, 0)) {
396             LOG_E("Failed to get VOL header");
397             mSignalledError = true;
398             return ENCODE_FAIL;
399         }
400         LOG_I("Output VOL header: %d bytes", mVolHeaderLength);
401         mNumInputFrames++;
402         //return ENCODE_SUCCESS;
403     }
404 
405     if (mStoreMetaDataInBuffers) {
406         IntelMetadataBuffer imb;
407         int32_t type;
408         int32_t value;
409         uint8_t *img;
410         const android::Rect rect(mVideoWidth, mVideoHeight);
411         android::status_t res;
412         ValueInfo vinfo;
413         ValueInfo *pvinfo = &vinfo;
414         CHECK(IMB_SUCCESS == imb.UnSerialize(inBuffer->data, inBuffer->size));
415         imb.GetType((::IntelMetadataBufferType&)type);
416         imb.GetValue(value);
417         imb.GetValueInfo(pvinfo);
418         if(pvinfo == NULL) {
419             res = android::GraphicBufferMapper::get().lock((buffer_handle_t)value,
420                     GRALLOC_USAGE_SW_READ_MASK,
421                     rect, (void**)&img);
422         } else {
423             img = (uint8_t*)value;
424         }
425         if (pvinfo != NULL)
426             trimBuffer(img, mTrimedInputData, pvinfo->width, pvinfo->height,
427                    pvinfo->height, pvinfo->lumaStride);
428         else {
429             //NV12 Y-TILED
430             trimBuffer(img, mTrimedInputData, mVideoWidth, mVideoHeight,
431                     ALIGN(mVideoHeight, 32), ALIGN(mVideoWidth, 128));
432             android::GraphicBufferMapper::get().unlock((buffer_handle_t)value);
433         }
434     } else {
435         memcpy(mTrimedInputData, inBuffer->data,
436                 (mVideoWidth * mVideoHeight * 3 ) >> 1);
437     }
438 
439     if (mVideoColorFormat != OMX_COLOR_FormatYUV420Planar) {
440         ConvertYUV420SemiPlanarToYUV420Planar(
441                 mTrimedInputData, mInputFrameData, mVideoWidth, mVideoHeight);
442     } else {
443         memcpy(mTrimedInputData, mInputFrameData,
444                 (mVideoWidth * mVideoHeight * 3 ) >> 1);
445     }
446 
447     LOG_V("End\n");
448 
449     return ret;
450 }
451 
getOutput(VideoEncOutputBuffer * outBuffer,uint32_t timeout)452 Encode_Status PVSoftMPEG4Encoder::getOutput(VideoEncOutputBuffer *outBuffer, uint32_t timeout)
453 {
454     LOG_V("Begin\n");
455 
456     Encode_Status ret = ENCODE_SUCCESS;
457     uint8_t *outPtr = outBuffer->data;
458     int32_t dataLength = outBuffer->bufferSize;
459     outBuffer->flag = 0;
460 
461     if ((mEncodeMode == COMBINE_MODE_WITH_ERR_RES) &&
462             (outBuffer->format == OUTPUT_CODEC_DATA)) {
463         memcpy(outPtr, mVolHeader, mVolHeaderLength);
464         ++mNumInputFrames;
465         outBuffer->flag |= ENCODE_BUFFERFLAG_CODECCONFIG;
466         outBuffer->flag |= ENCODE_BUFFERFLAG_ENDOFFRAME;
467         outBuffer->flag |= ENCODE_BUFFERFLAG_SYNCFRAME;
468         outBuffer->dataSize = mVolHeaderLength;
469         outBuffer->remainingSize = 0;
470         return ENCODE_SUCCESS;
471     }
472 
473     outBuffer->timeStamp = mCurTimestampUs;
474     LOG_I("info.mTimeUs %lld\n", outBuffer->timeStamp);
475 
476     VideoEncFrameIO vin, vout;
477     memset(&vin, 0, sizeof(vin));
478     memset(&vout, 0, sizeof(vout));
479     vin.height = ((mVideoHeight  + 15) >> 4) << 4;
480     vin.pitch = ((mVideoWidth + 15) >> 4) << 4;
481     vin.timestamp = (outBuffer->timeStamp + 500) / 1000;  // in ms
482     vin.yChan = mInputFrameData;
483     vin.uChan = vin.yChan + vin.height * vin.pitch;
484     vin.vChan = vin.uChan + ((vin.height * vin.pitch) >> 2);
485 
486     unsigned long modTimeMs = 0;
487     int32_t nLayer = 0;
488     MP4HintTrack hintTrack;
489     if (!PVEncodeVideoFrame(mHandle, &vin, &vout,
490                 &modTimeMs, outPtr, &dataLength, &nLayer) ||
491             !PVGetHintTrack(mHandle, &hintTrack)) {
492         LOG_E("Failed to encode frame or get hink track at frame %lld",
493                 mNumInputFrames);
494         mSignalledError = true;
495         hintTrack.CodeType = 0;
496         ret = ENCODE_FAIL;
497     }
498     LOG_I("dataLength %d\n", dataLength);
499     CHECK(NULL == PVGetOverrunBuffer(mHandle));
500     if (hintTrack.CodeType == 0) {  // I-frame serves as sync frame
501         outBuffer->flag |= ENCODE_BUFFERFLAG_SYNCFRAME;
502     }
503 
504     ++mNumInputFrames;
505 
506     outBuffer->flag |= ENCODE_BUFFERFLAG_ENDOFFRAME;
507     outBuffer->dataSize = dataLength;
508 
509     LOG_V("End\n");
510 
511     return ret;
512 }
513 
514