/* * Copyright (C) 2009 The Android Open Source Project * * 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. */ /*------------------------------------------------------------------------------ Table of contents 1. Include headers 2. External compiler flags 3. Module defines 4. Local function prototypes 5. Functions ComparePictures h264bsdReorderRefPicList Mmcop1 Mmcop2 Mmcop3 Mmcop4 Mmcop5 Mmcop6 h264bsdMarkDecRefPic h264bsdGetRefPicData h264bsdAllocateDpbImage SlidingWindowRefPicMarking h264bsdInitDpb h264bsdResetDpb h264bsdInitRefPicList FindDpbPic SetPicNums h264bsdCheckGapsInFrameNum FindSmallestPicOrderCnt OutputPicture h264bsdDpbOutputPicture h264bsdFlushDpb h264bsdFreeDpb ------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------ 1. Include headers ------------------------------------------------------------------------------*/ #include "h264bsd_cfg.h" #include "h264bsd_dpb.h" #include "h264bsd_slice_header.h" #include "h264bsd_image.h" #include "h264bsd_util.h" #include "basetype.h" #include /*------------------------------------------------------------------------------ 2. External compiler flags -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- 3. Module defines ------------------------------------------------------------------------------*/ /* macros to determine picture status. Note that IS_SHORT_TERM macro returns * true also for non-existing pictures because non-existing pictures are * regarded short term pictures according to H.264 standard */ #define IS_REFERENCE(a) ((a).status) #define IS_EXISTING(a) ((a).status > NON_EXISTING) #define IS_SHORT_TERM(a) \ ((a).status == NON_EXISTING || (a).status == SHORT_TERM) #define IS_LONG_TERM(a) ((a).status == LONG_TERM) /* macro to set a picture unused for reference */ #define SET_UNUSED(a) (a).status = UNUSED; #define MAX_NUM_REF_IDX_L0_ACTIVE 16 /*------------------------------------------------------------------------------ 4. Local function prototypes ------------------------------------------------------------------------------*/ static i32 ComparePictures(const void *ptr1, const void *ptr2); static u32 Mmcop1(dpbStorage_t *dpb, u32 currPicNum, u32 differenceOfPicNums); static u32 Mmcop2(dpbStorage_t *dpb, u32 longTermPicNum); static u32 Mmcop3(dpbStorage_t *dpb, u32 currPicNum, u32 differenceOfPicNums, u32 longTermFrameIdx); static u32 Mmcop4(dpbStorage_t *dpb, u32 maxLongTermFrameIdx); static u32 Mmcop5(dpbStorage_t *dpb); static u32 Mmcop6(dpbStorage_t *dpb, u32 frameNum, i32 picOrderCnt, u32 longTermFrameIdx); static u32 SlidingWindowRefPicMarking(dpbStorage_t *dpb); static i32 FindDpbPic(dpbStorage_t *dpb, i32 picNum, u32 isShortTerm); static void SetPicNums(dpbStorage_t *dpb, u32 currFrameNum); static dpbPicture_t* FindSmallestPicOrderCnt(dpbStorage_t *dpb); static u32 OutputPicture(dpbStorage_t *dpb); static void ShellSort(dpbPicture_t *pPic, u32 num); /*------------------------------------------------------------------------------ Function: ComparePictures Functional description: Function to compare dpb pictures, used by the ShellSort() function. Order of the pictures after sorting shall be as follows: 1) short term reference pictures starting with the largest picNum 2) long term reference pictures starting with the smallest longTermPicNum 3) pictures unused for reference but needed for display 4) other pictures Returns: -1 pic 1 is greater than pic 2 0 equal from comparison point of view 1 pic 2 is greater then pic 1 ------------------------------------------------------------------------------*/ static i32 ComparePictures(const void *ptr1, const void *ptr2) { /* Variables */ dpbPicture_t *pic1, *pic2; /* Code */ ASSERT(ptr1); ASSERT(ptr2); pic1 = (dpbPicture_t*)ptr1; pic2 = (dpbPicture_t*)ptr2; /* both are non-reference pictures, check if needed for display */ if (!IS_REFERENCE(*pic1) && !IS_REFERENCE(*pic2)) { if (pic1->toBeDisplayed && !pic2->toBeDisplayed) return(-1); else if (!pic1->toBeDisplayed && pic2->toBeDisplayed) return(1); else return(0); } /* only pic 1 needed for reference -> greater */ else if (!IS_REFERENCE(*pic2)) return(-1); /* only pic 2 needed for reference -> greater */ else if (!IS_REFERENCE(*pic1)) return(1); /* both are short term reference pictures -> check picNum */ else if (IS_SHORT_TERM(*pic1) && IS_SHORT_TERM(*pic2)) { if (pic1->picNum > pic2->picNum) return(-1); else if (pic1->picNum < pic2->picNum) return(1); else return(0); } /* only pic 1 is short term -> greater */ else if (IS_SHORT_TERM(*pic1)) return(-1); /* only pic 2 is short term -> greater */ else if (IS_SHORT_TERM(*pic2)) return(1); /* both are long term reference pictures -> check picNum (contains the * longTermPicNum */ else { if (pic1->picNum > pic2->picNum) return(1); else if (pic1->picNum < pic2->picNum) return(-1); else return(0); } } /*------------------------------------------------------------------------------ Function: h264bsdReorderRefPicList Functional description: Function to perform reference picture list reordering based on reordering commands received in the slice header. See details of the process in the H.264 standard. Inputs: dpb pointer to dpb storage structure order pointer to reordering commands currFrameNum current frame number numRefIdxActive number of active reference indices for current picture Outputs: dpb 'list' field of the structure reordered Returns: HANTRO_OK success HANTRO_NOK if non-existing pictures referred to in the reordering commands ------------------------------------------------------------------------------*/ u32 h264bsdReorderRefPicList( dpbStorage_t *dpb, refPicListReordering_t *order, u32 currFrameNum, u32 numRefIdxActive) { /* Variables */ u32 i, j, k, picNumPred, refIdx; i32 picNum, picNumNoWrap, index; u32 isShortTerm; /* Code */ ASSERT(order); ASSERT(currFrameNum <= dpb->maxFrameNum); ASSERT(numRefIdxActive <= MAX_NUM_REF_IDX_L0_ACTIVE); /* set dpb picture numbers for sorting */ SetPicNums(dpb, currFrameNum); if (!order->refPicListReorderingFlagL0) return(HANTRO_OK); refIdx = 0; picNumPred = currFrameNum; i = 0; while (order->command[i].reorderingOfPicNumsIdc < 3) { /* short term */ if (order->command[i].reorderingOfPicNumsIdc < 2) { if (order->command[i].reorderingOfPicNumsIdc == 0) { picNumNoWrap = (i32)picNumPred - (i32)order->command[i].absDiffPicNum; if (picNumNoWrap < 0) picNumNoWrap += (i32)dpb->maxFrameNum; } else { picNumNoWrap = (i32)(picNumPred + order->command[i].absDiffPicNum); if (picNumNoWrap >= (i32)dpb->maxFrameNum) picNumNoWrap -= (i32)dpb->maxFrameNum; } picNumPred = (u32)picNumNoWrap; picNum = picNumNoWrap; if ((u32)picNumNoWrap > currFrameNum) picNum -= (i32)dpb->maxFrameNum; isShortTerm = HANTRO_TRUE; } /* long term */ else { picNum = (i32)order->command[i].longTermPicNum; isShortTerm = HANTRO_FALSE; } /* find corresponding picture from dpb */ index = FindDpbPic(dpb, picNum, isShortTerm); if (index < 0 || !IS_EXISTING(dpb->buffer[index])) return(HANTRO_NOK); /* shift pictures */ for (j = numRefIdxActive; j > refIdx; j--) dpb->list[j] = dpb->list[j-1]; /* put picture into the list */ dpb->list[refIdx++] = &dpb->buffer[index]; /* remove later references to the same picture */ for (j = k = refIdx; j <= numRefIdxActive; j++) if(dpb->list[j] != &dpb->buffer[index]) dpb->list[k++] = dpb->list[j]; i++; } return(HANTRO_OK); } /*------------------------------------------------------------------------------ Function: Mmcop1 Functional description: Function to mark a short-term reference picture unused for reference, memory_management_control_operation equal to 1 Returns: HANTRO_OK success HANTRO_NOK failure, picture does not exist in the buffer ------------------------------------------------------------------------------*/ static u32 Mmcop1(dpbStorage_t *dpb, u32 currPicNum, u32 differenceOfPicNums) { /* Variables */ i32 index, picNum; /* Code */ ASSERT(currPicNum < dpb->maxFrameNum); picNum = (i32)currPicNum - (i32)differenceOfPicNums; index = FindDpbPic(dpb, picNum, HANTRO_TRUE); if (index < 0) return(HANTRO_NOK); SET_UNUSED(dpb->buffer[index]); dpb->numRefFrames--; if (!dpb->buffer[index].toBeDisplayed) dpb->fullness--; return(HANTRO_OK); } /*------------------------------------------------------------------------------ Function: Mmcop2 Functional description: Function to mark a long-term reference picture unused for reference, memory_management_control_operation equal to 2 Returns: HANTRO_OK success HANTRO_NOK failure, picture does not exist in the buffer ------------------------------------------------------------------------------*/ static u32 Mmcop2(dpbStorage_t *dpb, u32 longTermPicNum) { /* Variables */ i32 index; /* Code */ index = FindDpbPic(dpb, (i32)longTermPicNum, HANTRO_FALSE); if (index < 0) return(HANTRO_NOK); SET_UNUSED(dpb->buffer[index]); dpb->numRefFrames--; if (!dpb->buffer[index].toBeDisplayed) dpb->fullness--; return(HANTRO_OK); } /*------------------------------------------------------------------------------ Function: Mmcop3 Functional description: Function to assing a longTermFrameIdx to a short-term reference frame (i.e. to change it to a long-term reference picture), memory_management_control_operation equal to 3 Returns: HANTRO_OK success HANTRO_NOK failure, short-term picture does not exist in the buffer or is a non-existing picture, or invalid longTermFrameIdx given ------------------------------------------------------------------------------*/ static u32 Mmcop3(dpbStorage_t *dpb, u32 currPicNum, u32 differenceOfPicNums, u32 longTermFrameIdx) { /* Variables */ i32 index, picNum; u32 i; /* Code */ ASSERT(dpb); ASSERT(currPicNum < dpb->maxFrameNum); if ( (dpb->maxLongTermFrameIdx == NO_LONG_TERM_FRAME_INDICES) || (longTermFrameIdx > dpb->maxLongTermFrameIdx) ) return(HANTRO_NOK); /* check if a long term picture with the same longTermFrameIdx already * exist and remove it if necessary */ for (i = 0; i < dpb->maxRefFrames; i++) if (IS_LONG_TERM(dpb->buffer[i]) && (u32)dpb->buffer[i].picNum == longTermFrameIdx) { SET_UNUSED(dpb->buffer[i]); dpb->numRefFrames--; if (!dpb->buffer[i].toBeDisplayed) dpb->fullness--; break; } picNum = (i32)currPicNum - (i32)differenceOfPicNums; index = FindDpbPic(dpb, picNum, HANTRO_TRUE); if (index < 0) return(HANTRO_NOK); if (!IS_EXISTING(dpb->buffer[index])) return(HANTRO_NOK); dpb->buffer[index].status = LONG_TERM; dpb->buffer[index].picNum = (i32)longTermFrameIdx; return(HANTRO_OK); } /*------------------------------------------------------------------------------ Function: Mmcop4 Functional description: Function to set maxLongTermFrameIdx, memory_management_control_operation equal to 4 Returns: HANTRO_OK success ------------------------------------------------------------------------------*/ static u32 Mmcop4(dpbStorage_t *dpb, u32 maxLongTermFrameIdx) { /* Variables */ u32 i; /* Code */ dpb->maxLongTermFrameIdx = maxLongTermFrameIdx; for (i = 0; i < dpb->maxRefFrames; i++) if (IS_LONG_TERM(dpb->buffer[i]) && ( ((u32)dpb->buffer[i].picNum > maxLongTermFrameIdx) || (dpb->maxLongTermFrameIdx == NO_LONG_TERM_FRAME_INDICES) ) ) { SET_UNUSED(dpb->buffer[i]); dpb->numRefFrames--; if (!dpb->buffer[i].toBeDisplayed) dpb->fullness--; } return(HANTRO_OK); } /*------------------------------------------------------------------------------ Function: Mmcop5 Functional description: Function to mark all reference pictures unused for reference and set maxLongTermFrameIdx to NO_LONG_TERM_FRAME_INDICES, memory_management_control_operation equal to 5. Function flushes the buffer and places all pictures that are needed for display into the output buffer. Returns: HANTRO_OK success ------------------------------------------------------------------------------*/ static u32 Mmcop5(dpbStorage_t *dpb) { /* Variables */ u32 i; /* Code */ for (i = 0; i < 16; i++) { if (IS_REFERENCE(dpb->buffer[i])) { SET_UNUSED(dpb->buffer[i]); if (!dpb->buffer[i].toBeDisplayed) dpb->fullness--; } } /* output all pictures */ while (OutputPicture(dpb) == HANTRO_OK) ; dpb->numRefFrames = 0; dpb->maxLongTermFrameIdx = NO_LONG_TERM_FRAME_INDICES; dpb->prevRefFrameNum = 0; return(HANTRO_OK); } /*------------------------------------------------------------------------------ Function: Mmcop6 Functional description: Function to assign longTermFrameIdx to the current picture, memory_management_control_operation equal to 6 Returns: HANTRO_OK success HANTRO_NOK invalid longTermFrameIdx or no room for current picture in the buffer ------------------------------------------------------------------------------*/ static u32 Mmcop6(dpbStorage_t *dpb, u32 frameNum, i32 picOrderCnt, u32 longTermFrameIdx) { /* Variables */ u32 i; /* Code */ ASSERT(frameNum < dpb->maxFrameNum); if ( (dpb->maxLongTermFrameIdx == NO_LONG_TERM_FRAME_INDICES) || (longTermFrameIdx > dpb->maxLongTermFrameIdx) ) return(HANTRO_NOK); /* check if a long term picture with the same longTermFrameIdx already * exist and remove it if necessary */ for (i = 0; i < dpb->maxRefFrames; i++) if (IS_LONG_TERM(dpb->buffer[i]) && (u32)dpb->buffer[i].picNum == longTermFrameIdx) { SET_UNUSED(dpb->buffer[i]); dpb->numRefFrames--; if (!dpb->buffer[i].toBeDisplayed) dpb->fullness--; break; } if (dpb->numRefFrames < dpb->maxRefFrames) { dpb->currentOut->frameNum = frameNum; dpb->currentOut->picNum = (i32)longTermFrameIdx; dpb->currentOut->picOrderCnt = picOrderCnt; dpb->currentOut->status = LONG_TERM; if (dpb->noReordering) dpb->currentOut->toBeDisplayed = HANTRO_FALSE; else dpb->currentOut->toBeDisplayed = HANTRO_TRUE; dpb->numRefFrames++; dpb->fullness++; return(HANTRO_OK); } /* if there is no room, return an error */ else return(HANTRO_NOK); } /*------------------------------------------------------------------------------ Function: h264bsdMarkDecRefPic Functional description: Function to perform reference picture marking process. This function should be called both for reference and non-reference pictures. Non-reference pictures shall have mark pointer set to NULL. Inputs: dpb pointer to the DPB data structure mark pointer to reference picture marking commands image pointer to current picture to be placed in the buffer frameNum frame number of the current picture picOrderCnt picture order count for the current picture isIdr flag to indicate if the current picture is an IDR picture currentPicId identifier for the current picture, from the application, stored along with the picture numErrMbs number of concealed macroblocks in the current picture, stored along with the picture Outputs: dpb 'buffer' modified, possible output frames placed into 'outBuf' Returns: HANTRO_OK success HANTRO_NOK failure ------------------------------------------------------------------------------*/ u32 h264bsdMarkDecRefPic( dpbStorage_t *dpb, decRefPicMarking_t *mark, image_t *image, u32 frameNum, i32 picOrderCnt, u32 isIdr, u32 currentPicId, u32 numErrMbs) { /* Variables */ u32 i, status; u32 markedAsLongTerm; u32 toBeDisplayed; /* Code */ ASSERT(dpb); ASSERT(mark || !isIdr); ASSERT(!isIdr || (frameNum == 0 && picOrderCnt == 0)); ASSERT(frameNum < dpb->maxFrameNum); if (image->data != dpb->currentOut->data) { EPRINT("TRYING TO MARK NON-ALLOCATED IMAGE"); return(HANTRO_NOK); } dpb->lastContainsMmco5 = HANTRO_FALSE; status = HANTRO_OK; toBeDisplayed = dpb->noReordering ? HANTRO_FALSE : HANTRO_TRUE; /* non-reference picture, stored for display reordering purposes */ if (mark == NULL) { dpb->currentOut->status = UNUSED; dpb->currentOut->frameNum = frameNum; dpb->currentOut->picNum = (i32)frameNum; dpb->currentOut->picOrderCnt = picOrderCnt; dpb->currentOut->toBeDisplayed = toBeDisplayed; if (!dpb->noReordering) dpb->fullness++; } /* IDR picture */ else if (isIdr) { /* h264bsdCheckGapsInFrameNum not called for IDR pictures -> have to * reset numOut and outIndex here */ dpb->numOut = dpb->outIndex = 0; /* flush the buffer */ Mmcop5(dpb); /* if noOutputOfPriorPicsFlag was set -> the pictures preceding the * IDR picture shall not be output -> set output buffer empty */ if (mark->noOutputOfPriorPicsFlag || dpb->noReordering) { dpb->numOut = 0; dpb->outIndex = 0; } if (mark->longTermReferenceFlag) { dpb->currentOut->status = LONG_TERM; dpb->maxLongTermFrameIdx = 0; } else { dpb->currentOut->status = SHORT_TERM; dpb->maxLongTermFrameIdx = NO_LONG_TERM_FRAME_INDICES; } dpb->currentOut->frameNum = 0; dpb->currentOut->picNum = 0; dpb->currentOut->picOrderCnt = 0; dpb->currentOut->toBeDisplayed = toBeDisplayed; dpb->fullness = 1; dpb->numRefFrames = 1; } /* reference picture */ else { markedAsLongTerm = HANTRO_FALSE; if (mark->adaptiveRefPicMarkingModeFlag) { i = 0; while (mark->operation[i].memoryManagementControlOperation) { switch (mark->operation[i].memoryManagementControlOperation) { case 1: status = Mmcop1( dpb, frameNum, mark->operation[i].differenceOfPicNums); break; case 2: status = Mmcop2(dpb, mark->operation[i].longTermPicNum); break; case 3: status = Mmcop3( dpb, frameNum, mark->operation[i].differenceOfPicNums, mark->operation[i].longTermFrameIdx); break; case 4: status = Mmcop4( dpb, mark->operation[i].maxLongTermFrameIdx); break; case 5: status = Mmcop5(dpb); dpb->lastContainsMmco5 = HANTRO_TRUE; frameNum = 0; break; case 6: status = Mmcop6( dpb, frameNum, picOrderCnt, mark->operation[i].longTermFrameIdx); if (status == HANTRO_OK) markedAsLongTerm = HANTRO_TRUE; break; default: /* invalid memory management control operation */ status = HANTRO_NOK; break; } if (status != HANTRO_OK) { break; } i++; } } else { status = SlidingWindowRefPicMarking(dpb); } /* if current picture was not marked as long-term reference by * memory management control operation 6 -> mark current as short * term and insert it into dpb (if there is room) */ if (!markedAsLongTerm) { if (dpb->numRefFrames < dpb->maxRefFrames) { dpb->currentOut->frameNum = frameNum; dpb->currentOut->picNum = (i32)frameNum; dpb->currentOut->picOrderCnt = picOrderCnt; dpb->currentOut->status = SHORT_TERM; dpb->currentOut->toBeDisplayed = toBeDisplayed; dpb->fullness++; dpb->numRefFrames++; } /* no room */ else { status = HANTRO_NOK; } } } dpb->currentOut->isIdr = isIdr; dpb->currentOut->picId = currentPicId; dpb->currentOut->numErrMbs = numErrMbs; /* dpb was initialized to not to reorder the pictures -> output current * picture immediately */ if (dpb->noReordering) { ASSERT(dpb->numOut == 0); ASSERT(dpb->outIndex == 0); dpb->outBuf[dpb->numOut].data = dpb->currentOut->data; dpb->outBuf[dpb->numOut].isIdr = dpb->currentOut->isIdr; dpb->outBuf[dpb->numOut].picId = dpb->currentOut->picId; dpb->outBuf[dpb->numOut].numErrMbs = dpb->currentOut->numErrMbs; dpb->numOut++; } else { /* output pictures if buffer full */ while (dpb->fullness > dpb->dpbSize) { i = OutputPicture(dpb); ASSERT(i == HANTRO_OK); } } /* sort dpb */ ShellSort(dpb->buffer, dpb->dpbSize+1); return(status); } /*------------------------------------------------------------------------------ Function: h264bsdGetRefPicData Functional description: Function to get reference picture data from the reference picture list Returns: pointer to desired reference picture data NULL if invalid index or non-existing picture referred ------------------------------------------------------------------------------*/ u8* h264bsdGetRefPicData(dpbStorage_t *dpb, u32 index) { /* Variables */ /* Code */ if(index > 16 || dpb->list[index] == NULL) return(NULL); else if(!IS_EXISTING(*dpb->list[index])) return(NULL); else return(dpb->list[index]->data); } /*------------------------------------------------------------------------------ Function: h264bsdAllocateDpbImage Functional description: function to allocate memory for a image. This function does not really allocate any memory but reserves one of the buffer positions for decoding of current picture Returns: pointer to memory area for the image ------------------------------------------------------------------------------*/ u8* h264bsdAllocateDpbImage(dpbStorage_t *dpb) { /* Variables */ /* Code */ ASSERT( !dpb->buffer[dpb->dpbSize].toBeDisplayed && !IS_REFERENCE(dpb->buffer[dpb->dpbSize]) ); ASSERT(dpb->fullness <= dpb->dpbSize); dpb->currentOut = dpb->buffer + dpb->dpbSize; return(dpb->currentOut->data); } /*------------------------------------------------------------------------------ Function: SlidingWindowRefPicMarking Functional description: Function to perform sliding window refence picture marking process. Outputs: HANTRO_OK success HANTRO_NOK failure, no short-term reference frame found that could be marked unused ------------------------------------------------------------------------------*/ static u32 SlidingWindowRefPicMarking(dpbStorage_t *dpb) { /* Variables */ i32 index, picNum; u32 i; /* Code */ if (dpb->numRefFrames < dpb->maxRefFrames) { return(HANTRO_OK); } else { index = -1; picNum = 0; /* find the oldest short term picture */ for (i = 0; i < dpb->numRefFrames; i++) if (IS_SHORT_TERM(dpb->buffer[i])) if (dpb->buffer[i].picNum < picNum || index == -1) { index = (i32)i; picNum = dpb->buffer[i].picNum; } if (index >= 0) { SET_UNUSED(dpb->buffer[index]); dpb->numRefFrames--; if (!dpb->buffer[index].toBeDisplayed) dpb->fullness--; return(HANTRO_OK); } } return(HANTRO_NOK); } /*------------------------------------------------------------------------------ Function: h264bsdInitDpb Functional description: Function to initialize DPB. Reserves memories for the buffer, reference picture list and output buffer. dpbSize indicates the maximum DPB size indicated by the levelIdc in the stream. If noReordering flag is FALSE the DPB stores dpbSize pictures for display reordering purposes. On the other hand, if the flag is TRUE the DPB only stores maxRefFrames reference pictures and outputs all the pictures immediately. Inputs: picSizeInMbs picture size in macroblocks dpbSize size of the DPB (number of pictures) maxRefFrames max number of reference frames maxFrameNum max frame number noReordering flag to indicate that DPB does not have to prepare to reorder frames for display Outputs: dpb pointer to dpb data storage Returns: HANTRO_OK success MEMORY_ALLOCATION_ERROR if memory allocation failed ------------------------------------------------------------------------------*/ u32 h264bsdInitDpb( dpbStorage_t *dpb, u32 picSizeInMbs, u32 dpbSize, u32 maxRefFrames, u32 maxFrameNum, u32 noReordering) { /* Variables */ u32 i; /* Code */ ASSERT(picSizeInMbs); ASSERT(maxRefFrames <= MAX_NUM_REF_PICS); ASSERT(maxRefFrames <= dpbSize); ASSERT(maxFrameNum); ASSERT(dpbSize); // see comment in loop below about size calculation if (picSizeInMbs > (UINT32_MAX - 32 - 15) / 384) { ALOGE("b/28533562"); android_errorWriteLog(0x534e4554, "28533562"); return(MEMORY_ALLOCATION_ERROR); } dpb->maxLongTermFrameIdx = NO_LONG_TERM_FRAME_INDICES; dpb->maxRefFrames = MAX(maxRefFrames, 1); if (noReordering) dpb->dpbSize = dpb->maxRefFrames; else dpb->dpbSize = dpbSize; dpb->maxFrameNum = maxFrameNum; dpb->noReordering = noReordering; dpb->fullness = 0; dpb->numRefFrames = 0; dpb->prevRefFrameNum = 0; ALLOCATE(dpb->buffer, MAX_NUM_REF_IDX_L0_ACTIVE + 1, dpbPicture_t); if (dpb->buffer == NULL) return(MEMORY_ALLOCATION_ERROR); H264SwDecMemset(dpb->buffer, 0, (MAX_NUM_REF_IDX_L0_ACTIVE + 1)*sizeof(dpbPicture_t)); for (i = 0; i < dpb->dpbSize + 1; i++) { /* Allocate needed amount of memory, which is: * image size + 32 + 15, where 32 cames from the fact that in ARM OpenMax * DL implementation Functions may read beyond the end of an array, * by a maximum of 32 bytes. And +15 cames for the need to align memory * to 16-byte boundary */ ALLOCATE(dpb->buffer[i].pAllocatedData, (picSizeInMbs*384 + 32+15), u8); if (dpb->buffer[i].pAllocatedData == NULL) return(MEMORY_ALLOCATION_ERROR); dpb->buffer[i].data = ALIGN(dpb->buffer[i].pAllocatedData, 16); } ALLOCATE(dpb->list, MAX_NUM_REF_IDX_L0_ACTIVE + 1, dpbPicture_t*); ALLOCATE(dpb->outBuf, dpb->dpbSize+1, dpbOutPicture_t); if (dpb->list == NULL || dpb->outBuf == NULL) return(MEMORY_ALLOCATION_ERROR); H264SwDecMemset(dpb->list, 0, ((MAX_NUM_REF_IDX_L0_ACTIVE + 1) * sizeof(dpbPicture_t*)) ); dpb->numOut = dpb->outIndex = 0; return(HANTRO_OK); } /*------------------------------------------------------------------------------ Function: h264bsdResetDpb Functional description: Function to reset DPB. This function should be called when an IDR slice (other than the first) activates new sequence parameter set. Function calls h264bsdFreeDpb to free old allocated memories and h264bsdInitDpb to re-initialize the DPB. Same inputs, outputs and returns as for h264bsdInitDpb. ------------------------------------------------------------------------------*/ u32 h264bsdResetDpb( dpbStorage_t *dpb, u32 picSizeInMbs, u32 dpbSize, u32 maxRefFrames, u32 maxFrameNum, u32 noReordering) { /* Code */ ASSERT(picSizeInMbs); ASSERT(maxRefFrames <= MAX_NUM_REF_PICS); ASSERT(maxRefFrames <= dpbSize); ASSERT(maxFrameNum); ASSERT(dpbSize); h264bsdFreeDpb(dpb); return h264bsdInitDpb(dpb, picSizeInMbs, dpbSize, maxRefFrames, maxFrameNum, noReordering); } /*------------------------------------------------------------------------------ Function: h264bsdInitRefPicList Functional description: Function to initialize reference picture list. Function just sets pointers in the list according to pictures in the buffer. The buffer is assumed to contain pictures sorted according to what the H.264 standard says about initial reference picture list. Inputs: dpb pointer to dpb data structure Outputs: dpb 'list' field initialized Returns: none ------------------------------------------------------------------------------*/ void h264bsdInitRefPicList(dpbStorage_t *dpb) { /* Variables */ u32 i; /* Code */ for (i = 0; i < dpb->numRefFrames; i++) dpb->list[i] = &dpb->buffer[i]; } /*------------------------------------------------------------------------------ Function: FindDpbPic Functional description: Function to find a reference picture from the buffer. The picture to be found is identified by picNum and isShortTerm flag. Returns: index of the picture in the buffer -1 if the specified picture was not found in the buffer ------------------------------------------------------------------------------*/ static i32 FindDpbPic(dpbStorage_t *dpb, i32 picNum, u32 isShortTerm) { /* Variables */ u32 i = 0; u32 found = HANTRO_FALSE; /* Code */ if (isShortTerm) { while (i < dpb->maxRefFrames && !found) { if (IS_SHORT_TERM(dpb->buffer[i]) && dpb->buffer[i].picNum == picNum) found = HANTRO_TRUE; else i++; } } else { ASSERT(picNum >= 0); while (i < dpb->maxRefFrames && !found) { if (IS_LONG_TERM(dpb->buffer[i]) && dpb->buffer[i].picNum == picNum) found = HANTRO_TRUE; else i++; } } if (found) return((i32)i); else return(-1); } /*------------------------------------------------------------------------------ Function: SetPicNums Functional description: Function to set picNum values for short-term pictures in the buffer. Numbering of pictures is based on frame numbers and as frame numbers are modulo maxFrameNum -> frame numbers of older pictures in the buffer may be bigger than the currFrameNum. picNums will be set so that current frame has the largest picNum and all the short-term frames in the buffer will get smaller picNum representing their "distance" from the current frame. This function kind of maps the modulo arithmetic back to normal. ------------------------------------------------------------------------------*/ static void SetPicNums(dpbStorage_t *dpb, u32 currFrameNum) { /* Variables */ u32 i; i32 frameNumWrap; /* Code */ ASSERT(dpb); ASSERT(currFrameNum < dpb->maxFrameNum); for (i = 0; i < dpb->numRefFrames; i++) if (IS_SHORT_TERM(dpb->buffer[i])) { if (dpb->buffer[i].frameNum > currFrameNum) frameNumWrap = (i32)dpb->buffer[i].frameNum - (i32)dpb->maxFrameNum; else frameNumWrap = (i32)dpb->buffer[i].frameNum; dpb->buffer[i].picNum = frameNumWrap; } } /*------------------------------------------------------------------------------ Function: h264bsdCheckGapsInFrameNum Functional description: Function to check gaps in frame_num and generate non-existing (short term) reference pictures if necessary. This function should be called only for non-IDR pictures. Inputs: dpb pointer to dpb data structure frameNum frame number of the current picture isRefPic flag to indicate if current picture is a reference or non-reference picture gapsAllowed Flag which indicates active SPS stance on whether to allow gaps Outputs: dpb 'buffer' possibly modified by inserting non-existing pictures with sliding window marking process Returns: HANTRO_OK success HANTRO_NOK error in sliding window reference picture marking or frameNum equal to previous reference frame used for a reference picture ------------------------------------------------------------------------------*/ u32 h264bsdCheckGapsInFrameNum(dpbStorage_t *dpb, u32 frameNum, u32 isRefPic, u32 gapsAllowed) { /* Variables */ u32 unUsedShortTermFrameNum; u8 *tmp; /* Code */ ASSERT(dpb); ASSERT(dpb->fullness <= dpb->dpbSize); ASSERT(frameNum < dpb->maxFrameNum); dpb->numOut = 0; dpb->outIndex = 0; if(!gapsAllowed) return(HANTRO_OK); if ( (frameNum != dpb->prevRefFrameNum) && (frameNum != ((dpb->prevRefFrameNum + 1) % dpb->maxFrameNum))) { unUsedShortTermFrameNum = (dpb->prevRefFrameNum + 1) % dpb->maxFrameNum; /* store data pointer of last buffer position to be used as next * "allocated" data pointer if last buffer position after this process * contains data pointer located in outBuf (buffer placed in the output * shall not be overwritten by the current picture) */ tmp = dpb->buffer[dpb->dpbSize].data; do { SetPicNums(dpb, unUsedShortTermFrameNum); if (SlidingWindowRefPicMarking(dpb) != HANTRO_OK) { return(HANTRO_NOK); } /* output pictures if buffer full */ while (dpb->fullness >= dpb->dpbSize) { #ifdef _ASSERT_USED ASSERT(!dpb->noReordering); ASSERT(OutputPicture(dpb) == HANTRO_OK); #else OutputPicture(dpb); #endif } /* add to end of list */ ASSERT( !dpb->buffer[dpb->dpbSize].toBeDisplayed && !IS_REFERENCE(dpb->buffer[dpb->dpbSize]) ); dpb->buffer[dpb->dpbSize].status = NON_EXISTING; dpb->buffer[dpb->dpbSize].frameNum = unUsedShortTermFrameNum; dpb->buffer[dpb->dpbSize].picNum = (i32)unUsedShortTermFrameNum; dpb->buffer[dpb->dpbSize].picOrderCnt = 0; dpb->buffer[dpb->dpbSize].toBeDisplayed = HANTRO_FALSE; dpb->fullness++; dpb->numRefFrames++; /* sort the buffer */ ShellSort(dpb->buffer, dpb->dpbSize+1); unUsedShortTermFrameNum = (unUsedShortTermFrameNum + 1) % dpb->maxFrameNum; } while (unUsedShortTermFrameNum != frameNum); /* pictures placed in output buffer -> check that 'data' in * buffer position dpbSize is not in the output buffer (this will be * "allocated" by h264bsdAllocateDpbImage). If it is -> exchange data * pointer with the one stored in the beginning */ if (dpb->numOut) { u32 i; for (i = 0; i < dpb->numOut; i++) { if (dpb->outBuf[i].data == dpb->buffer[dpb->dpbSize].data) { /* find buffer position containing data pointer stored in * tmp */ for (i = 0; i < dpb->dpbSize; i++) { if (dpb->buffer[i].data == tmp) { dpb->buffer[i].data = dpb->buffer[dpb->dpbSize].data; dpb->buffer[dpb->dpbSize].data = tmp; break; } } ASSERT(i < dpb->dpbSize); break; } } } } /* frameNum for reference pictures shall not be the same as for previous * reference picture, otherwise accesses to pictures in the buffer cannot * be solved unambiguously */ else if (isRefPic && frameNum == dpb->prevRefFrameNum) { return(HANTRO_NOK); } /* save current frame_num in prevRefFrameNum. For non-reference frame * prevFrameNum is set to frame number of last non-existing frame above */ if (isRefPic) dpb->prevRefFrameNum = frameNum; else if (frameNum != dpb->prevRefFrameNum) { dpb->prevRefFrameNum = (frameNum + dpb->maxFrameNum - 1) % dpb->maxFrameNum; } return(HANTRO_OK); } /*------------------------------------------------------------------------------ Function: FindSmallestPicOrderCnt Functional description: Function to find picture with smallest picture order count. This will be the next picture in display order. Returns: pointer to the picture, NULL if no pictures to be displayed ------------------------------------------------------------------------------*/ dpbPicture_t* FindSmallestPicOrderCnt(dpbStorage_t *dpb) { /* Variables */ u32 i; i32 picOrderCnt; dpbPicture_t *tmp; /* Code */ ASSERT(dpb); picOrderCnt = 0x7FFFFFFF; tmp = NULL; for (i = 0; i <= dpb->dpbSize; i++) { if (dpb->buffer[i].toBeDisplayed && (dpb->buffer[i].picOrderCnt < picOrderCnt)) { tmp = dpb->buffer + i; picOrderCnt = dpb->buffer[i].picOrderCnt; } } return(tmp); } /*------------------------------------------------------------------------------ Function: OutputPicture Functional description: Function to put next display order picture into the output buffer. Returns: HANTRO_OK success HANTRO_NOK no pictures to display ------------------------------------------------------------------------------*/ u32 OutputPicture(dpbStorage_t *dpb) { /* Variables */ dpbPicture_t *tmp; /* Code */ ASSERT(dpb); if (dpb->noReordering) return(HANTRO_NOK); tmp = FindSmallestPicOrderCnt(dpb); /* no pictures to be displayed */ if (tmp == NULL) return(HANTRO_NOK); dpb->outBuf[dpb->numOut].data = tmp->data; dpb->outBuf[dpb->numOut].isIdr = tmp->isIdr; dpb->outBuf[dpb->numOut].picId = tmp->picId; dpb->outBuf[dpb->numOut].numErrMbs = tmp->numErrMbs; dpb->numOut++; tmp->toBeDisplayed = HANTRO_FALSE; if (!IS_REFERENCE(*tmp)) { dpb->fullness--; } return(HANTRO_OK); } /*------------------------------------------------------------------------------ Function: h264bsdDpbOutputPicture Functional description: Function to get next display order picture from the output buffer. Return: pointer to output picture structure, NULL if no pictures to display ------------------------------------------------------------------------------*/ dpbOutPicture_t* h264bsdDpbOutputPicture(dpbStorage_t *dpb) { /* Variables */ /* Code */ ASSERT(dpb); if (dpb->outIndex < dpb->numOut) return(dpb->outBuf + dpb->outIndex++); else return(NULL); } /*------------------------------------------------------------------------------ Function: h264bsdFlushDpb Functional description: Function to flush the DPB. Function puts all pictures needed for display into the output buffer. This function shall be called in the end of the stream to obtain pictures buffered for display re-ordering purposes. ------------------------------------------------------------------------------*/ void h264bsdFlushDpb(dpbStorage_t *dpb) { /* don't do anything if buffer not reserved */ if (dpb->buffer) { dpb->flushed = 1; /* output all pictures */ while (OutputPicture(dpb) == HANTRO_OK) ; } } /*------------------------------------------------------------------------------ Function: h264bsdFreeDpb Functional description: Function to free memories reserved for the DPB. ------------------------------------------------------------------------------*/ void h264bsdFreeDpb(dpbStorage_t *dpb) { /* Variables */ u32 i; /* Code */ ASSERT(dpb); if (dpb->buffer) { for (i = 0; i < dpb->dpbSize+1; i++) { FREE(dpb->buffer[i].pAllocatedData); } } FREE(dpb->buffer); FREE(dpb->list); FREE(dpb->outBuf); } /*------------------------------------------------------------------------------ Function: ShellSort Functional description: Sort pictures in the buffer. Function implements Shell's method, i.e. diminishing increment sort. See e.g. "Numerical Recipes in C" for more information. ------------------------------------------------------------------------------*/ static void ShellSort(dpbPicture_t *pPic, u32 num) { u32 i, j; u32 step; dpbPicture_t tmpPic; step = 7; while (step) { for (i = step; i < num; i++) { tmpPic = pPic[i]; j = i; while (j >= step && ComparePictures(pPic + j - step, &tmpPic) > 0) { pPic[j] = pPic[j-step]; j -= step; } pPic[j] = tmpPic; } step >>= 1; } }