blob: dfb226b82b0b86a5cf0a5abaa9db5b7a200ad8e6 [file] [log] [blame]
/*
* Copyright (c) 2009-2011 Intel Corporation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#include <stdlib.h>
#include "VideoEncoderLog.h"
#include "VideoEncoderAVC.h"
#include <va/va_tpi.h>
#include <va/va_enc_h264.h>
#include <bitstream.h>
VideoEncoderAVC::VideoEncoderAVC()
:VideoEncoderBase() {
if(VideoEncoderBase::queryProfileLevelConfig(mVADisplay, VAProfileH264High) == ENCODE_SUCCESS){
mComParams.profile = VAProfileH264High;
mComParams.level = 42;
}else if(VideoEncoderBase::queryProfileLevelConfig(mVADisplay, VAProfileH264Main) == ENCODE_SUCCESS){
mComParams.profile = VAProfileH264Main;
mComParams.level = 41;
}
mVideoParamsAVC.basicUnitSize = 0;
mVideoParamsAVC.VUIFlag = 0;
mVideoParamsAVC.sliceNum.iSliceNum = 2;
mVideoParamsAVC.sliceNum.pSliceNum = 2;
mVideoParamsAVC.idrInterval = 2;
mVideoParamsAVC.ipPeriod = 1;
mVideoParamsAVC.maxSliceSize = 0;
mVideoParamsAVC.delimiterType = AVC_DELIMITER_ANNEXB;
mSliceNum = 2;
mVideoParamsAVC.crop.LeftOffset = 0;
mVideoParamsAVC.crop.RightOffset = 0;
mVideoParamsAVC.crop.TopOffset = 0;
mVideoParamsAVC.crop.BottomOffset = 0;
mVideoParamsAVC.SAR.SarWidth = 0;
mVideoParamsAVC.SAR.SarHeight = 0;
mVideoParamsAVC.bEntropyCodingCABAC = 0;
mVideoParamsAVC.bWeightedPPrediction = 0;
mVideoParamsAVC.bDirect8x8Inference = 0;
mVideoParamsAVC.bConstIpred = 0;
mAutoReferenceSurfaceNum = 4;
packed_seq_header_param_buf_id = VA_INVALID_ID;
packed_seq_buf_id = VA_INVALID_ID;
packed_pic_header_param_buf_id = VA_INVALID_ID;
packed_pic_buf_id = VA_INVALID_ID;
packed_sei_header_param_buf_id = VA_INVALID_ID; /* the SEI buffer */
packed_sei_buf_id = VA_INVALID_ID;
}
Encode_Status VideoEncoderAVC::start() {
Encode_Status ret = ENCODE_SUCCESS;
LOG_V( "Begin\n");
if (mComParams.rcMode == VA_RC_VCM) {
// If we are in VCM, we will set slice num to max value
// mVideoParamsAVC.sliceNum.iSliceNum = (mComParams.resolution.height + 15) / 16;
// mVideoParamsAVC.sliceNum.pSliceNum = mVideoParamsAVC.sliceNum.iSliceNum;
}
ret = VideoEncoderBase::start ();
CHECK_ENCODE_STATUS_RETURN("VideoEncoderBase::start");
LOG_V( "end\n");
return ret;
}
Encode_Status VideoEncoderAVC::derivedSetParams(VideoParamConfigSet *videoEncParams) {
CHECK_NULL_RETURN_IFFAIL(videoEncParams);
VideoParamsAVC *encParamsAVC = reinterpret_cast <VideoParamsAVC *> (videoEncParams);
// AVC parames
if (encParamsAVC->size != sizeof (VideoParamsAVC)) {
return ENCODE_INVALID_PARAMS;
}
if(encParamsAVC->ipPeriod == 0 || encParamsAVC->ipPeriod >4)
return ENCODE_INVALID_PARAMS;
if((mComParams.intraPeriod >1)&&(mComParams.intraPeriod % encParamsAVC->ipPeriod !=0))
return ENCODE_INVALID_PARAMS;
mVideoParamsAVC = *encParamsAVC;
if(mComParams.profile == VAProfileH264Baseline){
mVideoParamsAVC.bEntropyCodingCABAC = 0;
mVideoParamsAVC.bDirect8x8Inference = 0;
mVideoParamsAVC.bWeightedPPrediction = 0;
}
return ENCODE_SUCCESS;
}
Encode_Status VideoEncoderAVC:: derivedGetParams(VideoParamConfigSet *videoEncParams) {
CHECK_NULL_RETURN_IFFAIL(videoEncParams);
VideoParamsAVC *encParamsAVC = reinterpret_cast <VideoParamsAVC *> (videoEncParams);
// AVC parames
if (encParamsAVC->size != sizeof (VideoParamsAVC)) {
return ENCODE_INVALID_PARAMS;
}
*encParamsAVC = mVideoParamsAVC;
return ENCODE_SUCCESS;
}
Encode_Status VideoEncoderAVC::derivedSetConfig(VideoParamConfigSet *videoEncConfig) {
CHECK_NULL_RETURN_IFFAIL(videoEncConfig);
LOG_V("Config type = %d\n", (int)videoEncConfig->type);
switch (videoEncConfig->type) {
case VideoConfigTypeAVCIntraPeriod: {
VideoConfigAVCIntraPeriod *configAVCIntraPeriod =
reinterpret_cast <VideoConfigAVCIntraPeriod *> (videoEncConfig);
// Config Intra Peroid
if (configAVCIntraPeriod->size != sizeof (VideoConfigAVCIntraPeriod)) {
return ENCODE_INVALID_PARAMS;
}
if(configAVCIntraPeriod->ipPeriod == 0 || configAVCIntraPeriod->ipPeriod >4)
return ENCODE_INVALID_PARAMS;
if((configAVCIntraPeriod->intraPeriod >1)&&(configAVCIntraPeriod->intraPeriod % configAVCIntraPeriod->ipPeriod !=0))
return ENCODE_INVALID_PARAMS;
mVideoParamsAVC.idrInterval = configAVCIntraPeriod->idrInterval;
mVideoParamsAVC.ipPeriod = configAVCIntraPeriod->ipPeriod;
mComParams.intraPeriod = configAVCIntraPeriod->intraPeriod;
mNewHeader = true;
break;
}
case VideoConfigTypeNALSize: {
// Config MTU
VideoConfigNALSize *configNALSize =
reinterpret_cast <VideoConfigNALSize *> (videoEncConfig);
if (configNALSize->size != sizeof (VideoConfigNALSize)) {
return ENCODE_INVALID_PARAMS;
}
mVideoParamsAVC.maxSliceSize = configNALSize->maxSliceSize;
mRenderMaxSliceSize = true;
break;
}
case VideoConfigTypeIDRRequest: {
if(mVideoParamsAVC.ipPeriod >1)
return ENCODE_FAIL;
else
mNewHeader = true;
break;
}
case VideoConfigTypeSliceNum: {
VideoConfigSliceNum *configSliceNum =
reinterpret_cast <VideoConfigSliceNum *> (videoEncConfig);
// Config Slice size
if (configSliceNum->size != sizeof (VideoConfigSliceNum)) {
return ENCODE_INVALID_PARAMS;
}
mVideoParamsAVC.sliceNum = configSliceNum->sliceNum;
break;
}
default: {
LOG_E ("Invalid Config Type");
break;
}
}
return ENCODE_SUCCESS;
}
Encode_Status VideoEncoderAVC:: derivedGetConfig(
VideoParamConfigSet *videoEncConfig) {
CHECK_NULL_RETURN_IFFAIL(videoEncConfig);
LOG_V("Config type = %d\n", (int)videoEncConfig->type);
switch (videoEncConfig->type) {
case VideoConfigTypeAVCIntraPeriod: {
VideoConfigAVCIntraPeriod *configAVCIntraPeriod =
reinterpret_cast <VideoConfigAVCIntraPeriod *> (videoEncConfig);
if (configAVCIntraPeriod->size != sizeof (VideoConfigAVCIntraPeriod)) {
return ENCODE_INVALID_PARAMS;
}
configAVCIntraPeriod->idrInterval = mVideoParamsAVC.idrInterval;
configAVCIntraPeriod->intraPeriod = mComParams.intraPeriod;
configAVCIntraPeriod->ipPeriod = mVideoParamsAVC.ipPeriod;
break;
}
case VideoConfigTypeNALSize: {
VideoConfigNALSize *configNALSize =
reinterpret_cast <VideoConfigNALSize *> (videoEncConfig);
if (configNALSize->size != sizeof (VideoConfigNALSize)) {
return ENCODE_INVALID_PARAMS;
}
configNALSize->maxSliceSize = mVideoParamsAVC.maxSliceSize;
break;
}
case VideoConfigTypeIDRRequest: {
break;
}
case VideoConfigTypeSliceNum: {
VideoConfigSliceNum *configSliceNum =
reinterpret_cast <VideoConfigSliceNum *> (videoEncConfig);
if (configSliceNum->size != sizeof (VideoConfigSliceNum)) {
return ENCODE_INVALID_PARAMS;
}
configSliceNum->sliceNum = mVideoParamsAVC.sliceNum;
break;
}
default: {
LOG_E ("Invalid Config Type");
break;
}
}
return ENCODE_SUCCESS;
}
Encode_Status VideoEncoderAVC::updateFrameInfo(EncodeTask* task) {
uint32_t idrPeroid = mComParams.intraPeriod * mVideoParamsAVC.idrInterval;
FrameType frametype;
uint32_t frame_num = mFrameNum;
uint32_t intraPeriod = mComParams.intraPeriod;
if (idrPeroid != 0) {
if(mVideoParamsAVC.ipPeriod > 1)
frame_num = frame_num % (idrPeroid + 1);
else
frame_num = frame_num % idrPeroid ;
}else{
if (mComParams.intraPeriod == 0)
intraPeriod = 0xFFFFFFFF;
}
if(frame_num ==0){
frametype = FTYPE_IDR;
}else if(intraPeriod ==1)
// only I frame need intraPeriod=idrInterval=ipPeriod=0
frametype = FTYPE_I;
else if(mVideoParamsAVC.ipPeriod == 1){ // no B frame
if((frame_num > 1) &&((frame_num -1)%intraPeriod == 0))
frametype = FTYPE_I;
else
frametype = FTYPE_P;
} else {
if(((frame_num-1)%intraPeriod == 0)&&(frame_num >intraPeriod))
frametype = FTYPE_I;
else{
frame_num = frame_num%intraPeriod;
if(frame_num == 0)
frametype = FTYPE_B;
else if((frame_num-1)%mVideoParamsAVC.ipPeriod == 0)
frametype = FTYPE_P;
else
frametype = FTYPE_B;
}
}
if (frametype == FTYPE_IDR || frametype == FTYPE_I)
task->flag |= ENCODE_BUFFERFLAG_SYNCFRAME;
if (frametype != task->type) {
const char* FrameTypeStr[10] = {"UNKNOWN", "I", "P", "B", "SI", "SP", "EI", "EP", "S", "IDR"};
if ((uint32_t) task->type < 9)
LOG_V("libMIX thinks it is %s Frame, the input is %s Frame", FrameTypeStr[frametype], FrameTypeStr[task->type]);
else
LOG_V("Wrong Frame type %d, type may not be initialized ?\n", task->type);
}
//temparily comment out to avoid uninitialize error
// if (task->type == FTYPE_UNKNOWN || (uint32_t) task->type > 9)
task->type = frametype;
return ENCODE_SUCCESS;
}
Encode_Status VideoEncoderAVC::getExtFormatOutput(VideoEncOutputBuffer *outBuffer) {
Encode_Status ret = ENCODE_SUCCESS;
LOG_V("Begin\n");
switch (outBuffer->format) {
case OUTPUT_CODEC_DATA: {
// Output the codec data
ret = outputCodecData(outBuffer);
CHECK_ENCODE_STATUS_CLEANUP("outputCodecData");
break;
}
case OUTPUT_ONE_NAL: {
// Output only one NAL unit
ret = outputOneNALU(outBuffer, true);
CHECK_ENCODE_STATUS_CLEANUP("outputOneNALU");
break;
}
case OUTPUT_ONE_NAL_WITHOUT_STARTCODE: {
ret = outputOneNALU(outBuffer, false);
CHECK_ENCODE_STATUS_CLEANUP("outputOneNALU");
break;
}
case OUTPUT_LENGTH_PREFIXED: {
// Output length prefixed
ret = outputLengthPrefixed(outBuffer);
CHECK_ENCODE_STATUS_CLEANUP("outputLengthPrefixed");
break;
}
case OUTPUT_NALULENGTHS_PREFIXED: {
// Output nalu lengths ahead of bitstream
ret = outputNaluLengthsPrefixed(outBuffer);
CHECK_ENCODE_STATUS_CLEANUP("outputNaluLengthsPrefixed");
break;
}
default:
LOG_E("Invalid buffer mode\n");
ret = ENCODE_FAIL;
break;
}
LOG_V("out size is = %d\n", outBuffer->dataSize);
CLEAN_UP:
LOG_V("End\n");
return ret;
}
Encode_Status VideoEncoderAVC::getOneNALUnit(
uint8_t *inBuffer, uint32_t bufSize, uint32_t *nalSize,
uint32_t *nalType, uint32_t *nalOffset, uint32_t status) {
uint32_t pos = 0;
uint32_t zeroByteCount = 0;
uint32_t singleByteTable[3][2] = {{1,0},{2,0},{2,3}};
uint32_t dataRemaining = 0;
uint8_t *dataPtr;
// Don't need to check parameters here as we just checked by caller
while ((inBuffer[pos++] == 0x00)) {
zeroByteCount ++;
if (pos >= bufSize) //to make sure the buffer to be accessed is valid
break;
}
if (inBuffer[pos - 1] != 0x01 || zeroByteCount < 2) {
LOG_E("The stream is not AnnexB format \n");
LOG_E("segment status is %x \n", status);
return ENCODE_FAIL; //not AnnexB, we won't process it
}
*nalType = (*(inBuffer + pos)) & 0x1F;
LOG_V ("NAL type = 0x%x\n", *nalType);
zeroByteCount = 0;
*nalOffset = pos;
if (status & VA_CODED_BUF_STATUS_SINGLE_NALU) {
*nalSize = bufSize - pos;
return ENCODE_SUCCESS;
}
dataPtr = inBuffer + pos;
dataRemaining = bufSize - pos + 1;
while ((dataRemaining > 0) && (zeroByteCount < 3)) {
if (((((intptr_t)dataPtr) & 0xF ) == 0) && (0 == zeroByteCount)
&& (dataRemaining > 0xF)) {
__asm__ (
//Data input
"movl %1, %%ecx\n\t"//data_ptr=>ecx
"movl %0, %%eax\n\t"//data_remaing=>eax
//Main compare loop
//
"0:\n\t" //MATCH_8_ZERO:
"pxor %%xmm0,%%xmm0\n\t"//set 0=>xmm0
"pcmpeqb (%%ecx),%%xmm0\n\t"//data_ptr=xmm0,(byte==0)?0xFF:0x00
"pmovmskb %%xmm0, %%edx\n\t"//edx[0]=xmm0[7],edx[1]=xmm0[15],...,edx[15]=xmm0[127]
"test $0xAAAA, %%edx\n\t"//edx& 1010 1010 1010 1010b
"jnz 2f\n\t"//Not equal to zero means that at least one byte 0x00
"1:\n\t" //PREPARE_NEXT_MATCH:
"sub $0x10, %%eax\n\t"//16 + ecx --> ecx
"add $0x10, %%ecx\n\t"//eax-16 --> eax
"cmp $0x10, %%eax\n\t"
"jge 0b\n\t"//search next 16 bytes
"2:\n\t" //DATA_RET:
"movl %%ecx, %1\n\t"//output ecx->data_ptr
"movl %%eax, %0\n\t"//output eax->data_remaining
: "+m"(dataRemaining), "+m"(dataPtr)
:
:"eax", "ecx", "edx", "xmm0"
);
if (0 >= dataRemaining) {
break;
}
}
//check the value of each byte
if ((*dataPtr) >= 2) {
zeroByteCount = 0;
}
else {
zeroByteCount = singleByteTable[zeroByteCount][*dataPtr];
}
dataPtr ++;
dataRemaining --;
}
if ((3 == zeroByteCount) && (dataRemaining > 0)) {
*nalSize = bufSize - dataRemaining - *nalOffset - 3;
} else if (0 == dataRemaining) {
*nalSize = bufSize - *nalOffset;
}
return ENCODE_SUCCESS;
}
Encode_Status VideoEncoderAVC::getHeader(
uint8_t *inBuffer, uint32_t bufSize, uint32_t *headerSize, uint32_t status) {
uint32_t nalType = 0;
uint32_t nalSize = 0;
uint32_t nalOffset = 0;
uint32_t size = 0;
uint8_t *buf = inBuffer;
Encode_Status ret = ENCODE_SUCCESS;
*headerSize = 0;
CHECK_NULL_RETURN_IFFAIL(inBuffer);
if (bufSize == 0) {
//bufSize shoule not be 0, error happens
LOG_E("Buffer size is 0\n");
return ENCODE_FAIL;
}
while (1) {
nalType = nalSize = nalOffset = 0;
ret = getOneNALUnit(buf, bufSize, &nalSize, &nalType, &nalOffset, status);
CHECK_ENCODE_STATUS_RETURN("getOneNALUnit");
LOG_V("NAL type = %d, NAL size = %d, offset = %d\n", nalType, nalSize, nalOffset);
size = nalSize + nalOffset;
// Codec_data should be SPS or PPS
if (nalType == 7 || nalType == 8) {
*headerSize += size;
buf += size;
bufSize -= size;
} else {
LOG_V("No header found or no header anymore\n");
break;
}
}
return ENCODE_SUCCESS;
}
Encode_Status VideoEncoderAVC::outputCodecData(
VideoEncOutputBuffer *outBuffer) {
Encode_Status ret = ENCODE_SUCCESS;
uint32_t headerSize = 0;
ret = getHeader((uint8_t *)mCurSegment->buf + mOffsetInSeg,
mCurSegment->size - mOffsetInSeg, &headerSize, mCurSegment->status);
CHECK_ENCODE_STATUS_RETURN("getHeader");
if (headerSize == 0) {
outBuffer->dataSize = 0;
mCurSegment = NULL;
return ENCODE_NO_REQUEST_DATA;
}
if (headerSize <= outBuffer->bufferSize) {
memcpy(outBuffer->data, (uint8_t *)mCurSegment->buf + mOffsetInSeg, headerSize);
mTotalSizeCopied += headerSize;
mOffsetInSeg += headerSize;
outBuffer->dataSize = headerSize;
outBuffer->remainingSize = 0;
outBuffer->flag |= ENCODE_BUFFERFLAG_ENDOFFRAME;
outBuffer->flag |= ENCODE_BUFFERFLAG_CODECCONFIG;
outBuffer->flag |= ENCODE_BUFFERFLAG_SYNCFRAME;
} else {
// we need a big enough buffer, otherwise we won't output anything
outBuffer->dataSize = 0;
outBuffer->remainingSize = headerSize;
outBuffer->flag |= ENCODE_BUFFERFLAG_DATAINVALID;
LOG_E("Buffer size too small\n");
return ENCODE_BUFFER_TOO_SMALL;
}
return ret;
}
Encode_Status VideoEncoderAVC::outputOneNALU(
VideoEncOutputBuffer *outBuffer, bool startCode) {
uint32_t nalType = 0;
uint32_t nalSize = 0;
uint32_t nalOffset = 0;
uint32_t sizeToBeCopied = 0;
Encode_Status ret = ENCODE_SUCCESS;
CHECK_NULL_RETURN_IFFAIL(mCurSegment->buf);
ret = getOneNALUnit((uint8_t *)mCurSegment->buf + mOffsetInSeg,
mCurSegment->size - mOffsetInSeg, &nalSize, &nalType, &nalOffset, mCurSegment->status);
CHECK_ENCODE_STATUS_RETURN("getOneNALUnit");
// check if we need startcode along with the payload
if (startCode) {
sizeToBeCopied = nalSize + nalOffset;
} else {
sizeToBeCopied = nalSize;
}
if (sizeToBeCopied <= outBuffer->bufferSize) {
if (startCode) {
memcpy(outBuffer->data, (uint8_t *)mCurSegment->buf + mOffsetInSeg, sizeToBeCopied);
} else {
memcpy(outBuffer->data, (uint8_t *)mCurSegment->buf + mOffsetInSeg + nalOffset,
sizeToBeCopied);
}
mTotalSizeCopied += sizeToBeCopied;
mOffsetInSeg += (nalSize + nalOffset);
outBuffer->dataSize = sizeToBeCopied;
outBuffer->flag |= ENCODE_BUFFERFLAG_PARTIALFRAME;
outBuffer->remainingSize = 0;
} else {
// if nothing to be copied out, set flag to invalid
outBuffer->dataSize = 0;
outBuffer->flag |= ENCODE_BUFFERFLAG_DATAINVALID;
outBuffer->remainingSize = sizeToBeCopied;
LOG_W("Buffer size too small\n");
return ENCODE_BUFFER_TOO_SMALL;
}
// check if all data in current segment has been copied out
if (mCurSegment->size == mOffsetInSeg) {
if (mCurSegment->next != NULL) {
mCurSegment = (VACodedBufferSegment *)mCurSegment->next;
mOffsetInSeg = 0;
} else {
LOG_V("End of stream\n");
outBuffer->flag |= ENCODE_BUFFERFLAG_ENDOFFRAME;
mCurSegment = NULL;
}
}
return ENCODE_SUCCESS;
}
Encode_Status VideoEncoderAVC::outputLengthPrefixed(VideoEncOutputBuffer *outBuffer) {
Encode_Status ret = ENCODE_SUCCESS;
uint32_t nalType = 0;
uint32_t nalSize = 0;
uint32_t nalOffset = 0;
uint32_t sizeCopiedHere = 0;
CHECK_NULL_RETURN_IFFAIL(mCurSegment->buf);
while (1) {
if (mCurSegment->size < mOffsetInSeg || outBuffer->bufferSize < sizeCopiedHere) {
LOG_E("mCurSegment->size < mOffsetInSeg || outBuffer->bufferSize < sizeCopiedHere\n");
return ENCODE_FAIL;
}
// we need to handle the whole bitstream NAL by NAL
ret = getOneNALUnit(
(uint8_t *)mCurSegment->buf + mOffsetInSeg,
mCurSegment->size - mOffsetInSeg, &nalSize, &nalType, &nalOffset, mCurSegment->status);
CHECK_ENCODE_STATUS_RETURN("getOneNALUnit");
if (nalSize + 4 <= outBuffer->bufferSize - sizeCopiedHere) {
// write the NAL length to bit stream
outBuffer->data[sizeCopiedHere] = (nalSize >> 24) & 0xff;
outBuffer->data[sizeCopiedHere + 1] = (nalSize >> 16) & 0xff;
outBuffer->data[sizeCopiedHere + 2] = (nalSize >> 8) & 0xff;
outBuffer->data[sizeCopiedHere + 3] = nalSize & 0xff;
sizeCopiedHere += 4;
mTotalSizeCopied += 4;
memcpy(outBuffer->data + sizeCopiedHere,
(uint8_t *)mCurSegment->buf + mOffsetInSeg + nalOffset, nalSize);
sizeCopiedHere += nalSize;
mTotalSizeCopied += nalSize;
mOffsetInSeg += (nalSize + nalOffset);
} else {
outBuffer->dataSize = sizeCopiedHere;
// In case the start code is 3-byte length but we use 4-byte for length prefixed
// so the remainingSize size may larger than the remaining data size
outBuffer->remainingSize = mTotalSize - mTotalSizeCopied + 100;
outBuffer->flag |= ENCODE_BUFFERFLAG_PARTIALFRAME;
LOG_E("Buffer size too small\n");
return ENCODE_BUFFER_TOO_SMALL;
}
// check if all data in current segment has been copied out
if (mCurSegment->size == mOffsetInSeg) {
if (mCurSegment->next != NULL) {
mCurSegment = (VACodedBufferSegment *)mCurSegment->next;
mOffsetInSeg = 0;
} else {
LOG_V("End of stream\n");
outBuffer->dataSize = sizeCopiedHere;
outBuffer->remainingSize = 0;
outBuffer->flag |= ENCODE_BUFFERFLAG_ENDOFFRAME;
mCurSegment = NULL;
break;
}
}
}
return ENCODE_SUCCESS;
}
Encode_Status VideoEncoderAVC::outputNaluLengthsPrefixed(VideoEncOutputBuffer *outBuffer) {
Encode_Status ret = ENCODE_SUCCESS;
uint32_t nalType = 0;
uint32_t nalSize = 0;
uint32_t nalOffset = 0;
uint32_t sizeCopiedHere = 0;
const uint32_t NALUINFO_OFFSET = 256;
uint32_t nalNum = 0;
CHECK_NULL_RETURN_IFFAIL(mCurSegment->buf);
while (1) {
if (mCurSegment->size < mOffsetInSeg || outBuffer->bufferSize < sizeCopiedHere) {
LOG_E("mCurSegment->size < mOffsetInSeg || outBuffer->bufferSize < sizeCopiedHere\n");
return ENCODE_FAIL;
}
// we need to handle the whole bitstream NAL by NAL
ret = getOneNALUnit(
(uint8_t *)mCurSegment->buf + mOffsetInSeg,
mCurSegment->size - mOffsetInSeg, &nalSize, &nalType, &nalOffset, mCurSegment->status);
CHECK_ENCODE_STATUS_RETURN("getOneNALUnit");
if (nalSize + 4 <= outBuffer->bufferSize - NALUINFO_OFFSET - sizeCopiedHere) {
memcpy(outBuffer->data + NALUINFO_OFFSET + sizeCopiedHere,
(uint8_t *)mCurSegment->buf + mOffsetInSeg, nalSize + nalOffset);
sizeCopiedHere += nalSize + nalOffset;
mTotalSizeCopied += nalSize + nalOffset;
mOffsetInSeg += (nalSize + nalOffset);
} else {
outBuffer->dataSize = sizeCopiedHere;
// In case the start code is 3-byte length but we use 4-byte for length prefixed
// so the remainingSize size may larger than the remaining data size
outBuffer->remainingSize = mTotalSize - mTotalSizeCopied + 100;
outBuffer->flag |= ENCODE_BUFFERFLAG_PARTIALFRAME;
LOG_E("Buffer size too small\n");
return ENCODE_BUFFER_TOO_SMALL;
}
nalNum ++;
uint32_t *nalLength = (uint32_t *) (outBuffer->data + (nalNum+1) * 4);
*nalLength = nalSize + nalOffset;
// check if all data in current segment has been copied out
if (mCurSegment->size == mOffsetInSeg) {
if (mCurSegment->next != NULL) {
mCurSegment = (VACodedBufferSegment *)mCurSegment->next;
mOffsetInSeg = 0;
} else {
LOG_V("End of stream\n");
outBuffer->dataSize = sizeCopiedHere;
outBuffer->remainingSize = 0;
outBuffer->flag |= ENCODE_BUFFERFLAG_ENDOFFRAME;
mCurSegment = NULL;
break;
}
}
}
outBuffer->offset = NALUINFO_OFFSET;
uint32_t *nalHead = (uint32_t *) outBuffer->data;
*nalHead = 0x4E414C4C; //'nall'
*(++nalHead) = nalNum;
return ENCODE_SUCCESS;
}
Encode_Status VideoEncoderAVC::sendEncodeCommand(EncodeTask *task) {
Encode_Status ret = ENCODE_SUCCESS;
LOG_V( "Begin\n");
if (mFrameNum == 0 || mNewHeader) {
if (mRenderHrd) {
ret = renderHrd();
mRenderHrd = false;
CHECK_ENCODE_STATUS_RETURN("renderHrd");
}
mFrameNum = 0;
ret = renderSequenceParams(task);
CHECK_ENCODE_STATUS_RETURN("renderSequenceParams");
if (mNewHeader) {
mNewHeader = false; //Set to require new header filed to false
mFrameNum = 0; //reset mFrameNum to 0
updateFrameInfo(task); //recalculate frame info if mNewHeader is set true after PrepareFrameInfo in encode()
}
}
if (mRenderMaxSliceSize && mVideoParamsAVC.maxSliceSize != 0) {
ret = renderMaxSliceSize();
CHECK_ENCODE_STATUS_RETURN("renderMaxSliceSize");
mRenderMaxSliceSize = false;
}
if (mComParams.rcParams.enableIntraFrameQPControl && (task->type == FTYPE_IDR || task->type == FTYPE_I))
mRenderBitRate = true;
if (mRenderBitRate) {
ret = VideoEncoderBase::renderDynamicBitrate(task);
CHECK_ENCODE_STATUS_RETURN("renderDynamicBitrate");
}
if (mRenderAIR &&
(mComParams.refreshType == VIDEO_ENC_AIR ||
mComParams.refreshType == VIDEO_ENC_BOTH)) {
ret = renderAIR();
CHECK_ENCODE_STATUS_RETURN("renderAIR");
mRenderAIR = false;
}
if (mRenderCIR) {
ret = renderCIR();
CHECK_ENCODE_STATUS_RETURN("renderCIR");
mRenderCIR = false;
}
if (mRenderFrameRate) {
ret = VideoEncoderBase::renderDynamicFrameRate();
CHECK_ENCODE_STATUS_RETURN("renderDynamicFrameRate");
mRenderFrameRate = false;
}
ret = renderPictureParams(task);
CHECK_ENCODE_STATUS_RETURN("renderPictureParams");
if (mFrameNum == 0 && (mEncPackedHeaders != VA_ATTRIB_NOT_SUPPORTED)) {
ret = renderPackedSequenceParams(task);
CHECK_ENCODE_STATUS_RETURN("renderPackedSequenceParams");
ret = renderPackedPictureParams(task);
CHECK_ENCODE_STATUS_RETURN("renderPackedPictureParams");
}
ret = renderSliceParams(task);
CHECK_ENCODE_STATUS_RETURN("renderSliceParams");
LOG_V( "End\n");
return ENCODE_SUCCESS;
}
Encode_Status VideoEncoderAVC::renderMaxSliceSize() {
VAStatus vaStatus = VA_STATUS_SUCCESS;
LOG_V( "Begin\n\n");
if (mComParams.rcMode != RATE_CONTROL_VCM) {
LOG_W ("Not in VCM mode, but call send_max_slice_size\n");
return ENCODE_SUCCESS;
}
VAEncMiscParameterBuffer *miscEncParamBuf;
VAEncMiscParameterMaxSliceSize *maxSliceSizeParam;
VABufferID miscParamBufferID;
vaStatus = vaCreateBuffer(
mVADisplay, mVAContext,
VAEncMiscParameterBufferType,
sizeof(VAEncMiscParameterBuffer) + sizeof(VAEncMiscParameterMaxSliceSize),
1, NULL, &miscParamBufferID);
CHECK_VA_STATUS_RETURN("vaCreateBuffer");
vaStatus = vaMapBuffer(mVADisplay, miscParamBufferID, (void **)&miscEncParamBuf);
CHECK_VA_STATUS_RETURN("vaMapBuffer");
miscEncParamBuf->type = VAEncMiscParameterTypeMaxSliceSize;
maxSliceSizeParam = (VAEncMiscParameterMaxSliceSize *)miscEncParamBuf->data;
maxSliceSizeParam->max_slice_size = mVideoParamsAVC.maxSliceSize;
vaStatus = vaUnmapBuffer(mVADisplay, miscParamBufferID);
CHECK_VA_STATUS_RETURN("vaUnmapBuffer");
LOG_I( "max slice size = %d\n", maxSliceSizeParam->max_slice_size);
vaStatus = vaRenderPicture(mVADisplay, mVAContext, &miscParamBufferID, 1);
CHECK_VA_STATUS_RETURN("vaRenderPicture");
return ENCODE_SUCCESS;
}
Encode_Status VideoEncoderAVC::renderCIR(){
VAStatus vaStatus = VA_STATUS_SUCCESS;
LOG_V( "%s Begin\n", __FUNCTION__);
VABufferID miscParamBufferCIRid;
VAEncMiscParameterBuffer *misc_param;
VAEncMiscParameterCIR *misc_cir_param;
vaStatus = vaCreateBuffer(mVADisplay, mVAContext,
VAEncMiscParameterBufferType,
sizeof(VAEncMiscParameterBuffer) + sizeof(VAEncMiscParameterCIR),
1,
NULL,
&miscParamBufferCIRid);
CHECK_VA_STATUS_RETURN("vaCreateBuffer");
vaStatus = vaMapBuffer(mVADisplay, miscParamBufferCIRid, (void **)&misc_param);
CHECK_VA_STATUS_RETURN("vaMapBuffer");
misc_param->type = VAEncMiscParameterTypeCIR;
misc_cir_param = (VAEncMiscParameterCIR *)misc_param->data;
misc_cir_param->cir_num_mbs = mComParams.cirParams.cir_num_mbs;
LOG_I( "cir_num_mbs %d \n", misc_cir_param->cir_num_mbs);
vaUnmapBuffer(mVADisplay, miscParamBufferCIRid);
CHECK_VA_STATUS_RETURN("vaUnmapBuffer");
vaStatus = vaRenderPicture(mVADisplay, mVAContext, &miscParamBufferCIRid, 1);
CHECK_VA_STATUS_RETURN("vaRenderPicture");
return ENCODE_SUCCESS;
}
Encode_Status VideoEncoderAVC::renderAIR() {
VAStatus vaStatus = VA_STATUS_SUCCESS;
LOG_V( "Begin\n\n");
VAEncMiscParameterBuffer *miscEncParamBuf;
VAEncMiscParameterAIR *airParams;
VABufferID miscParamBufferID;
vaStatus = vaCreateBuffer(
mVADisplay, mVAContext,
VAEncMiscParameterBufferType,
sizeof(miscEncParamBuf) + sizeof(VAEncMiscParameterAIR),
1, NULL, &miscParamBufferID);
CHECK_VA_STATUS_RETURN("vaCreateBuffer");
vaStatus = vaMapBuffer(mVADisplay, miscParamBufferID, (void **)&miscEncParamBuf);
CHECK_VA_STATUS_RETURN("vaMapBuffer");
miscEncParamBuf->type = VAEncMiscParameterTypeAIR;
airParams = (VAEncMiscParameterAIR *)miscEncParamBuf->data;
airParams->air_num_mbs = mComParams.airParams.airMBs;
airParams->air_threshold= mComParams.airParams.airThreshold;
airParams->air_auto = mComParams.airParams.airAuto;
vaStatus = vaUnmapBuffer(mVADisplay, miscParamBufferID);
CHECK_VA_STATUS_RETURN("vaUnmapBuffer");
vaStatus = vaRenderPicture(mVADisplay, mVAContext, &miscParamBufferID, 1);
CHECK_VA_STATUS_RETURN("vaRenderPicture");
LOG_I( "airThreshold = %d\n", airParams->air_threshold);
return ENCODE_SUCCESS;
}
int VideoEncoderAVC::calcLevel(int numMbs) {
int level = 30;
if (numMbs < 1620) {
level = 30;
} else if (numMbs < 3600) {
level = 31;
} else if (numMbs < 5120) {
level = 32;
} else if (numMbs < 8192) {
level = 41;
} else if (numMbs < 8704) {
level = 42;
} else if (numMbs < 22080) {
level = 50;
} else if (numMbs < 36864) {
level = 51;
} else {
LOG_W("No such level can support that resolution");
level = 51;
}
return level;
}
Encode_Status VideoEncoderAVC::renderSequenceParams(EncodeTask *) {
VAStatus vaStatus = VA_STATUS_SUCCESS;
VAEncSequenceParameterBufferH264 avcSeqParams = VAEncSequenceParameterBufferH264();
VAEncMiscParameterBuffer *miscEncRCParamBuf;
VAEncMiscParameterBuffer *miscEncFrameRateParamBuf;
VAEncMiscParameterRateControl *rcMiscParam;
VAEncMiscParameterFrameRate *framerateParam;
int level;
uint32_t frameRateNum = mComParams.frameRate.frameRateNum;
uint32_t frameRateDenom = mComParams.frameRate.frameRateDenom;
LOG_V( "Begin\n\n");
vaStatus = vaCreateBuffer(mVADisplay, mVAContext,
VAEncMiscParameterBufferType,
sizeof (VAEncMiscParameterBuffer) + sizeof (VAEncMiscParameterRateControl),
1, NULL,
&mRcParamBuf);
CHECK_VA_STATUS_RETURN("vaCreateBuffer");
vaStatus = vaMapBuffer(mVADisplay, mRcParamBuf, (void **)&miscEncRCParamBuf);
CHECK_VA_STATUS_RETURN("vaMapBuffer");
vaStatus = vaCreateBuffer(mVADisplay, mVAContext,
VAEncMiscParameterBufferType,
sizeof (VAEncMiscParameterBuffer) + sizeof (VAEncMiscParameterFrameRate),
1, NULL,
&mFrameRateParamBuf);
CHECK_VA_STATUS_RETURN("vaCreateBuffer");
vaStatus = vaMapBuffer(mVADisplay, mFrameRateParamBuf, (void **)&miscEncFrameRateParamBuf);
CHECK_VA_STATUS_RETURN("vaMapBuffer");
miscEncRCParamBuf->type = VAEncMiscParameterTypeRateControl;
rcMiscParam = (VAEncMiscParameterRateControl *)miscEncRCParamBuf->data;
miscEncFrameRateParamBuf->type = VAEncMiscParameterTypeFrameRate;
framerateParam = (VAEncMiscParameterFrameRate *)miscEncFrameRateParamBuf->data;
// set up the sequence params for HW
// avcSeqParams.level_idc = mLevel;
avcSeqParams.intra_period = mComParams.intraPeriod;
avcSeqParams.intra_idr_period = mVideoParamsAVC.idrInterval;
avcSeqParams.ip_period = mVideoParamsAVC.ipPeriod;
avcSeqParams.picture_width_in_mbs = (mComParams.resolution.width + 15) / 16;
avcSeqParams.picture_height_in_mbs = (mComParams.resolution.height + 15) / 16;
level = calcLevel (avcSeqParams.picture_width_in_mbs * avcSeqParams.picture_height_in_mbs);
avcSeqParams.level_idc = level;
avcSeqParams.bits_per_second = mComParams.rcParams.bitRate;
framerateParam->framerate =
(unsigned int) (frameRateNum + frameRateDenom /2 ) / frameRateDenom;
rcMiscParam->initial_qp = mComParams.rcParams.initQP;
rcMiscParam->min_qp = mComParams.rcParams.minQP;
rcMiscParam->max_qp = mComParams.rcParams.maxQP;
if (mComParams.rcParams.enableIntraFrameQPControl) {
rcMiscParam->min_qp = mComParams.rcParams.I_minQP;
rcMiscParam->max_qp = mComParams.rcParams.I_maxQP;
}
rcMiscParam->window_size = mComParams.rcParams.windowSize;
//target bitrate is sent to libva through Sequence Parameter Buffer
rcMiscParam->bits_per_second = 0;
rcMiscParam->basic_unit_size = mVideoParamsAVC.basicUnitSize; //for rate control usage
avcSeqParams.intra_period = mComParams.intraPeriod;
//avcSeqParams.vui_flag = 248;
avcSeqParams.vui_parameters_present_flag = mVideoParamsAVC.VUIFlag;
avcSeqParams.num_units_in_tick = frameRateDenom;
avcSeqParams.time_scale = 2 * frameRateNum;
avcSeqParams.seq_parameter_set_id = 0;
if (mVideoParamsAVC.crop.LeftOffset ||
mVideoParamsAVC.crop.RightOffset ||
mVideoParamsAVC.crop.TopOffset ||
mVideoParamsAVC.crop.BottomOffset) {
avcSeqParams.frame_cropping_flag = true;
avcSeqParams.frame_crop_left_offset = mVideoParamsAVC.crop.LeftOffset;
avcSeqParams.frame_crop_right_offset = mVideoParamsAVC.crop.RightOffset;
avcSeqParams.frame_crop_top_offset = mVideoParamsAVC.crop.TopOffset;
avcSeqParams.frame_crop_bottom_offset = mVideoParamsAVC.crop.BottomOffset;
} else {
avcSeqParams.frame_cropping_flag = false;
if (mComParams.resolution.width & 0xf) {
avcSeqParams.frame_cropping_flag = true;
uint32_t AWidth = (mComParams.resolution.width + 0xf) & (~0xf);
avcSeqParams.frame_crop_right_offset = ( AWidth - mComParams.resolution.width ) / 2;
}
if (mComParams.resolution.height & 0xf) {
avcSeqParams.frame_cropping_flag = true;
uint32_t AHeight = (mComParams.resolution.height + 0xf) & (~0xf);
avcSeqParams.frame_crop_bottom_offset = ( AHeight - mComParams.resolution.height ) / 2;
}
}
if(avcSeqParams.vui_parameters_present_flag && (mVideoParamsAVC.SAR.SarWidth || mVideoParamsAVC.SAR.SarHeight)) {
avcSeqParams.vui_fields.bits.aspect_ratio_info_present_flag = true;
avcSeqParams.aspect_ratio_idc = 0xff /* Extended_SAR */;
avcSeqParams.sar_width = mVideoParamsAVC.SAR.SarWidth;
avcSeqParams.sar_height = mVideoParamsAVC.SAR.SarHeight;
}
avcSeqParams.max_num_ref_frames = 1;
if(avcSeqParams.ip_period > 1)
avcSeqParams.max_num_ref_frames = 2;
LOG_V("===h264 sequence params===\n");
LOG_V( "seq_parameter_set_id = %d\n", (uint32_t)avcSeqParams.seq_parameter_set_id);
LOG_V( "level_idc = %d\n", (uint32_t)avcSeqParams.level_idc);
LOG_V( "intra_period = %d\n", avcSeqParams.intra_period);
LOG_V( "idr_interval = %d\n", avcSeqParams.intra_idr_period);
LOG_V( "picture_width_in_mbs = %d\n", avcSeqParams.picture_width_in_mbs);
LOG_V( "picture_height_in_mbs = %d\n", avcSeqParams.picture_height_in_mbs);
LOG_V( "bitrate = %d\n", rcMiscParam->bits_per_second);
LOG_V( "frame_rate = %d\n", framerateParam->framerate);
LOG_V( "initial_qp = %d\n", rcMiscParam->initial_qp);
LOG_V( "min_qp = %d\n", rcMiscParam->min_qp);
LOG_V( "basic_unit_size = %d\n", rcMiscParam->basic_unit_size);
LOG_V( "bDirect8x8Inference = %d\n",mVideoParamsAVC.bDirect8x8Inference);
// Not sure whether these settings work for all drivers
avcSeqParams.seq_fields.bits.frame_mbs_only_flag = 1;
avcSeqParams.seq_fields.bits.pic_order_cnt_type = 0;
avcSeqParams.seq_fields.bits.direct_8x8_inference_flag = mVideoParamsAVC.bDirect8x8Inference;
avcSeqParams.seq_fields.bits.log2_max_frame_num_minus4 = 0;
avcSeqParams.seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 = 2;
// avcSeqParams.time_scale = 900;
// avcSeqParams.num_units_in_tick = 15; /* Tc = num_units_in_tick / time_sacle */
// Not sure whether these settings work for all drivers
vaStatus = vaUnmapBuffer(mVADisplay, mRcParamBuf);
CHECK_VA_STATUS_RETURN("vaUnmapBuffer");
vaStatus = vaUnmapBuffer(mVADisplay, mFrameRateParamBuf);
CHECK_VA_STATUS_RETURN("vaUnmapBuffer");
vaStatus = vaCreateBuffer(
mVADisplay, mVAContext,
VAEncSequenceParameterBufferType,
sizeof(avcSeqParams), 1, &avcSeqParams,
&mSeqParamBuf);
CHECK_VA_STATUS_RETURN("vaCreateBuffer");
vaStatus = vaRenderPicture(mVADisplay, mVAContext, &mFrameRateParamBuf, 1);
CHECK_VA_STATUS_RETURN("vaRenderPicture");
vaStatus = vaRenderPicture(mVADisplay, mVAContext, &mSeqParamBuf, 1);
CHECK_VA_STATUS_RETURN("vaRenderPicture");
vaStatus = vaRenderPicture(mVADisplay, mVAContext, &mRcParamBuf, 1);
CHECK_VA_STATUS_RETURN("vaRenderPicture");
return ENCODE_SUCCESS;
}
Encode_Status VideoEncoderAVC::renderPackedSequenceParams(EncodeTask *) {
VAStatus vaStatus = VA_STATUS_SUCCESS;
VAEncSequenceParameterBufferH264 *avcSeqParams;
VAEncPackedHeaderParameterBuffer packed_header_param_buffer;
unsigned char *packed_seq_buffer = NULL;
unsigned int length_in_bits;
LOG_V("Begin\n");
vaStatus = vaMapBuffer(mVADisplay, mSeqParamBuf, (void **)&avcSeqParams);
CHECK_VA_STATUS_RETURN("vaMapBuffer");
length_in_bits = build_packed_seq_buffer(&packed_seq_buffer, mComParams.profile, avcSeqParams);
packed_header_param_buffer.type = VAEncPackedHeaderSequence;
packed_header_param_buffer.bit_length = length_in_bits;
packed_header_param_buffer.has_emulation_bytes = 0;
vaStatus = vaCreateBuffer(mVADisplay, mVAContext,
VAEncPackedHeaderParameterBufferType,
sizeof(packed_header_param_buffer), 1, &packed_header_param_buffer,
&packed_seq_header_param_buf_id);
CHECK_VA_STATUS_RETURN("vaCreateBuffer");
vaStatus = vaCreateBuffer(mVADisplay, mVAContext,
VAEncPackedHeaderDataBufferType,
(length_in_bits + 7) / 8, 1, packed_seq_buffer,
&packed_seq_buf_id);
CHECK_VA_STATUS_RETURN("vaCreateBuffer");
vaStatus = vaRenderPicture(mVADisplay, mVAContext, &packed_seq_header_param_buf_id, 1);
CHECK_VA_STATUS_RETURN("vaRenderPicture");
vaStatus = vaRenderPicture(mVADisplay, mVAContext, &packed_seq_buf_id, 1);
CHECK_VA_STATUS_RETURN("vaRenderPicture");
vaStatus = vaUnmapBuffer(mVADisplay, mSeqParamBuf);
CHECK_VA_STATUS_RETURN("vaUnmapBuffer");
free(packed_seq_buffer);
LOG_V("End\n");
return vaStatus;
}
Encode_Status VideoEncoderAVC::renderPictureParams(EncodeTask *task) {
VAStatus vaStatus = VA_STATUS_SUCCESS;
VAEncPictureParameterBufferH264 avcPicParams = VAEncPictureParameterBufferH264();
uint32_t RefFrmIdx;
LOG_V( "Begin\n\n");
// set picture params for HW
if (mAutoReference == false) {
for (RefFrmIdx = 0; RefFrmIdx < 16; RefFrmIdx++) {
avcPicParams.ReferenceFrames[RefFrmIdx].picture_id = VA_INVALID_ID;
avcPicParams.ReferenceFrames[RefFrmIdx].flags = VA_PICTURE_H264_INVALID;
}
avcPicParams.ReferenceFrames[0].picture_id= task->ref_surface;
avcPicParams.ReferenceFrames[0].flags = VA_PICTURE_H264_SHORT_TERM_REFERENCE;
avcPicParams.CurrPic.picture_id= task->rec_surface;
// Not sure whether these settings work for all drivers
avcPicParams.CurrPic.TopFieldOrderCnt = mFrameNum * 2;
avcPicParams.pic_fields.bits.transform_8x8_mode_flag = 0;
avcPicParams.seq_parameter_set_id = 0;
avcPicParams.pic_parameter_set_id = 0;
avcPicParams.last_picture = 0;
avcPicParams.frame_num = 0;
avcPicParams.pic_init_qp = 26;
avcPicParams.num_ref_idx_l0_active_minus1 = 0;
avcPicParams.num_ref_idx_l1_active_minus1 = 0;
avcPicParams.pic_fields.bits.idr_pic_flag = 0;
avcPicParams.pic_fields.bits.reference_pic_flag = 0;
avcPicParams.pic_fields.bits.entropy_coding_mode_flag = 0;
avcPicParams.pic_fields.bits.weighted_pred_flag = 0;
avcPicParams.pic_fields.bits.weighted_bipred_idc = 0;
avcPicParams.pic_fields.bits.transform_8x8_mode_flag = 0;
avcPicParams.pic_fields.bits.deblocking_filter_control_present_flag = 1;
avcPicParams.frame_num = mFrameNum;
avcPicParams.pic_fields.bits.reference_pic_flag = 1;
// Not sure whether these settings work for all drivers
}else {
avcPicParams.CurrPic.picture_id= VA_INVALID_SURFACE;
for(uint32_t i =0; i< mAutoReferenceSurfaceNum; i++)
avcPicParams.ReferenceFrames[i].picture_id = mAutoRefSurfaces[i];
}
avcPicParams.pic_fields.bits.idr_pic_flag = (mFrameNum == 0);
avcPicParams.pic_fields.bits.entropy_coding_mode_flag = mVideoParamsAVC.bEntropyCodingCABAC;
avcPicParams.coded_buf = task->coded_buffer;
avcPicParams.last_picture = 0;
LOG_V("======h264 picture params======\n");
LOG_V( "reference_picture = 0x%08x\n", avcPicParams.ReferenceFrames[0].picture_id);
LOG_V( "reconstructed_picture = 0x%08x\n", avcPicParams.CurrPic.picture_id);
LOG_V( "coded_buf = 0x%08x\n", avcPicParams.coded_buf);
//LOG_I( "picture_width = %d\n", avcPicParams.picture_width);
//LOG_I( "picture_height = %d\n\n", avcPicParams.picture_height);
vaStatus = vaCreateBuffer(
mVADisplay, mVAContext,
VAEncPictureParameterBufferType,
sizeof(avcPicParams),
1,&avcPicParams,
&mPicParamBuf);
CHECK_VA_STATUS_RETURN("vaCreateBuffer");
vaStatus = vaRenderPicture(mVADisplay, mVAContext, &mPicParamBuf, 1);
CHECK_VA_STATUS_RETURN("vaRenderPicture");
LOG_V( "end\n");
return ENCODE_SUCCESS;
}
Encode_Status VideoEncoderAVC::renderPackedPictureParams(EncodeTask *) {
VAStatus vaStatus = VA_STATUS_SUCCESS;
VAEncPictureParameterBufferH264 *avcPicParams;
VAEncPackedHeaderParameterBuffer packed_header_param_buffer;
unsigned char *packed_pic_buffer = NULL;
unsigned int length_in_bits;
LOG_V("Begin\n");
vaStatus = vaMapBuffer(mVADisplay, mPicParamBuf, (void **)&avcPicParams);
CHECK_VA_STATUS_RETURN("vaMapBuffer");
length_in_bits = build_packed_pic_buffer(&packed_pic_buffer, avcPicParams);
packed_header_param_buffer.type = VAEncPackedHeaderPicture;
packed_header_param_buffer.bit_length = length_in_bits;
packed_header_param_buffer.has_emulation_bytes = 0;
vaStatus = vaCreateBuffer(mVADisplay, mVAContext,
VAEncPackedHeaderParameterBufferType,
sizeof(packed_header_param_buffer), 1, &packed_header_param_buffer,
&packed_pic_header_param_buf_id);
CHECK_VA_STATUS_RETURN("vaCreateBuffer");
vaStatus = vaCreateBuffer(mVADisplay, mVAContext,
VAEncPackedHeaderDataBufferType,
(length_in_bits + 7) / 8, 1, packed_pic_buffer,
&packed_pic_buf_id);
CHECK_VA_STATUS_RETURN("vaCreateBuffer");
vaStatus = vaRenderPicture(mVADisplay, mVAContext, &packed_pic_header_param_buf_id, 1);
CHECK_VA_STATUS_RETURN("vaRenderPicture");
vaStatus = vaRenderPicture(mVADisplay, mVAContext, &packed_pic_buf_id, 1);
CHECK_VA_STATUS_RETURN("vaRenderPicture");
vaStatus = vaUnmapBuffer(mVADisplay, mSeqParamBuf);
CHECK_VA_STATUS_RETURN("vaUnmapBuffer");
free(packed_pic_buffer);
LOG_V("End\n");
return vaStatus;
}
Encode_Status VideoEncoderAVC::renderSliceParams(EncodeTask *task) {
VAStatus vaStatus = VA_STATUS_SUCCESS;
uint32_t sliceNum = 0;
uint32_t sliceIndex = 0;
uint32_t sliceHeightInMB = 0;
uint32_t maxSliceNum = 0;
uint32_t minSliceNum = 0;
uint32_t actualSliceHeightInMB = 0;
uint32_t startRowInMB = 0;
uint32_t modulus = 0;
uint32_t RefFrmIdx;
LOG_V( "Begin\n\n");
maxSliceNum = (mComParams.resolution.height + 15) / 16;
minSliceNum = 1;
if (task->type == FTYPE_I || task->type == FTYPE_IDR) {
sliceNum = mVideoParamsAVC.sliceNum.iSliceNum;
} else {
sliceNum = mVideoParamsAVC.sliceNum.pSliceNum;
}
if (sliceNum < minSliceNum) {
LOG_W("Slice Number is too small");
sliceNum = minSliceNum;
}
if (sliceNum > maxSliceNum) {
LOG_W("Slice Number is too big");
sliceNum = maxSliceNum;
}
mSliceNum= sliceNum;
modulus = maxSliceNum % sliceNum;
sliceHeightInMB = (maxSliceNum - modulus) / sliceNum ;
vaStatus = vaCreateBuffer(
mVADisplay, mVAContext,
VAEncSliceParameterBufferType,
sizeof(VAEncSliceParameterBufferH264),
sliceNum, NULL,
&mSliceParamBuf);
CHECK_VA_STATUS_RETURN("vaCreateBuffer");
VAEncSliceParameterBufferH264 *sliceParams, *currentSlice;
vaStatus = vaMapBuffer(mVADisplay, mSliceParamBuf, (void **)&sliceParams);
CHECK_VA_STATUS_RETURN("vaMapBuffer");
if(!sliceParams)
return ENCODE_NULL_PTR;
memset(sliceParams, 0 , sizeof(VAEncSliceParameterBufferH264));
if(!sliceParams)
return ENCODE_NULL_PTR;
currentSlice = sliceParams;
startRowInMB = 0;
for (sliceIndex = 0; sliceIndex < sliceNum; sliceIndex++) {
currentSlice = sliceParams + sliceIndex;
actualSliceHeightInMB = sliceHeightInMB;
if (sliceIndex < modulus) {
actualSliceHeightInMB ++;
}
// starting MB row number for this slice, suppose macroblock 16x16
currentSlice->macroblock_address = startRowInMB * ((mComParams.resolution.width + 0xf) & ~0xf) / 16;
// slice height measured in MB
currentSlice->num_macroblocks = actualSliceHeightInMB * ((mComParams.resolution.width + 0xf) & ~0xf) / 16;
if(task->type == FTYPE_I||task->type == FTYPE_IDR)
currentSlice->slice_type = 2;
else if(task->type == FTYPE_P)
currentSlice->slice_type = 0;
else if(task->type == FTYPE_B)
currentSlice->slice_type = 1;
currentSlice->disable_deblocking_filter_idc = mComParams.disableDeblocking;
// This is a temporary fix suggested by Binglin for bad encoding quality issue
// TODO: We need a long term design for this field
//currentSlice->slice_flags.bits.uses_long_term_ref = 0;
//currentSlice->slice_flags.bits.is_long_term_ref = 0;
LOG_V("======AVC slice params======\n");
LOG_V( "slice_index = %d\n", (int) sliceIndex);
LOG_V( "macroblock_address = %d\n", (int) currentSlice->macroblock_address);
LOG_V( "slice_height_in_mb = %d\n", (int) currentSlice->num_macroblocks);
LOG_V( "slice.type = %d\n", (int) currentSlice->slice_type);
LOG_V("disable_deblocking_filter_idc = %d\n\n", (int) currentSlice->disable_deblocking_filter_idc);
// Not sure whether these settings work for all drivers
currentSlice->pic_parameter_set_id = 0;
currentSlice->pic_order_cnt_lsb = mFrameNum * 2;
currentSlice->direct_spatial_mv_pred_flag = 0;
currentSlice->num_ref_idx_l0_active_minus1 = 0; /* FIXME: ??? */
currentSlice->num_ref_idx_l1_active_minus1 = 0;
currentSlice->cabac_init_idc = 0;
currentSlice->slice_qp_delta = 0;
currentSlice->disable_deblocking_filter_idc = 0;
currentSlice->slice_alpha_c0_offset_div2 = 2;
currentSlice->slice_beta_offset_div2 = 2;
currentSlice->idr_pic_id = 0;
for (RefFrmIdx = 0; RefFrmIdx < 32; RefFrmIdx++) {
currentSlice->RefPicList0[RefFrmIdx].picture_id = VA_INVALID_ID;
currentSlice->RefPicList0[RefFrmIdx].flags = VA_PICTURE_H264_INVALID;
}
currentSlice->RefPicList0[0].picture_id = task->ref_surface;
currentSlice->RefPicList0[0].flags = VA_PICTURE_H264_SHORT_TERM_REFERENCE;
// Not sure whether these settings work for all drivers
startRowInMB += actualSliceHeightInMB;
}
vaStatus = vaUnmapBuffer(mVADisplay, mSliceParamBuf);
CHECK_VA_STATUS_RETURN("vaUnmapBuffer");
vaStatus = vaRenderPicture(mVADisplay, mVAContext, &mSliceParamBuf, 1);
CHECK_VA_STATUS_RETURN("vaRenderPicture");
LOG_V( "end\n");
return ENCODE_SUCCESS;
}