Permalink
Cannot retrieve contributors at this time
| /* | |
| * vaapidecoder_h265.cpp - h265 decoder | |
| * | |
| * Copyright (C) 2013-2014 Intel Corporation | |
| * Author: XuGuangxin<Guangxin.Xu@intel.com> | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public License | |
| * as published by the Free Software Foundation; either version 2.1 | |
| * of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free | |
| * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
| * Boston, MA 02110-1301 USA | |
| */ | |
| #ifdef HAVE_CONFIG_H | |
| #include "config.h" | |
| #endif | |
| #include "codecparsers/h265parser.h" | |
| #include "common/log.h" | |
| #include "common/nalreader.h" | |
| #include "vaapidecoder_factory.h" | |
| #include "vaapi/vaapiptrs.h" | |
| #include "vaapi/vaapicontext.h" | |
| #include "vaapi/vaapidisplay.h" | |
| #include "vaapidecpicture.h" | |
| #include <algorithm> | |
| #include "vaapidecoder_h265.h" | |
| namespace YamiMediaCodec{ | |
| typedef VaapiDecoderH265::PicturePtr PicturePtr; | |
| using std::tr1::bind; | |
| using std::tr1::placeholders::_1; | |
| using std::tr1::ref; | |
| void h265SliceHdrFree(H265SliceHdr* pSlice) | |
| { | |
| h265_slice_hdr_free(pSlice); | |
| delete pSlice; | |
| } | |
| bool isIdr(const H265NalUnit* const nalu) | |
| { | |
| return nalu->type == H265_NAL_SLICE_IDR_W_RADL | |
| || nalu->type ==H265_NAL_SLICE_IDR_N_LP; | |
| } | |
| bool isBla(const H265NalUnit* const nalu) | |
| { | |
| return nalu->type == H265_NAL_SLICE_BLA_W_LP | |
| || nalu->type == H265_NAL_SLICE_BLA_W_RADL | |
| || nalu->type == H265_NAL_SLICE_BLA_N_LP; | |
| } | |
| #ifndef H265_NAL_SLICE_RSV_IRAP_VCL23 | |
| #define H265_NAL_SLICE_RSV_IRAP_VCL23 23 | |
| #endif | |
| bool isIrap(const H265NalUnit* const nalu) | |
| { | |
| return nalu->type >= H265_NAL_SLICE_BLA_W_LP | |
| && nalu->type <= H265_NAL_SLICE_RSV_IRAP_VCL23; | |
| } | |
| bool isRasl(const H265NalUnit* const nalu) | |
| { | |
| return nalu->type == H265_NAL_SLICE_RASL_R | |
| || nalu->type == H265_NAL_SLICE_RASL_N; | |
| } | |
| bool isRadl(const H265NalUnit* const nalu) | |
| { | |
| return nalu->type == H265_NAL_SLICE_RADL_R | |
| || nalu->type == H265_NAL_SLICE_RADL_N; | |
| } | |
| bool isCra(const H265NalUnit* const nalu) | |
| { | |
| return nalu->type == H265_NAL_SLICE_CRA_NUT; | |
| } | |
| #ifndef H265_NAL_SLICE_RSV_VCL_N10 | |
| #define H265_NAL_SLICE_RSV_VCL_N10 10 | |
| #endif | |
| #ifndef H265_NAL_SLICE_RSV_VCL_N12 | |
| #define H265_NAL_SLICE_RSV_VCL_N12 12 | |
| #endif | |
| #ifndef H265_NAL_SLICE_RSV_VCL_N14 | |
| #define H265_NAL_SLICE_RSV_VCL_N14 14 | |
| #endif | |
| bool isSublayerNoRef(const H265NalUnit* const nalu) | |
| { | |
| static const uint8_t noRef[] = { | |
| H265_NAL_SLICE_TRAIL_N, | |
| H265_NAL_SLICE_TSA_N, | |
| H265_NAL_SLICE_STSA_N, | |
| H265_NAL_SLICE_RADL_N, | |
| H265_NAL_SLICE_RASL_N, | |
| H265_NAL_SLICE_RSV_VCL_N10, | |
| H265_NAL_SLICE_RSV_VCL_N12, | |
| H265_NAL_SLICE_RSV_VCL_N14 | |
| }; | |
| static const uint8_t* end = noRef + N_ELEMENTS(noRef); | |
| return std::binary_search(noRef, end, nalu->type); | |
| } | |
| class VaapiDecPictureH265 : public VaapiDecPicture | |
| { | |
| public: | |
| VaapiDecPictureH265(const ContextPtr& context, const SurfacePtr& surface, int64_t timeStamp): | |
| VaapiDecPicture(context, surface, timeStamp) | |
| { | |
| } | |
| VaapiDecPictureH265() | |
| { | |
| } | |
| int32_t m_poc; | |
| uint16_t m_pocLsb; | |
| bool m_noRaslOutputFlag; | |
| bool m_picOutputFlag; | |
| uint32_t m_picLatencyCount; | |
| //is unused reference picture | |
| bool m_isUnusedReference; | |
| //is this picture ref able? | |
| bool m_isReference; | |
| }; | |
| inline bool VaapiDecoderH265::DPB::PocLess::operator()(const PicturePtr& left, const PicturePtr& right) const | |
| { | |
| return left->m_poc < right->m_poc; | |
| } | |
| VaapiDecoderH265::DPB::DPB(OutputCallback output): | |
| m_output(output), | |
| m_dummy(new VaapiDecPictureH265) | |
| { | |
| } | |
| bool VaapiDecoderH265::DPB::initShortTermRef(RefSet& ref, int32_t currPoc, | |
| const int32_t* delta, const uint8_t* used, uint8_t num) | |
| { | |
| if (num > 16) | |
| return false; | |
| ref.clear(); | |
| for (uint8_t i = 0; i < num; i++) { | |
| int32_t poc = currPoc + delta[i]; | |
| VaapiDecPictureH265* pic = getPic(poc); | |
| if (!pic) { | |
| ERROR("can't find short ref %d for %d", poc, currPoc); | |
| } else { | |
| if (used[i]) | |
| ref.push_back(pic); | |
| else | |
| m_stFoll.push_back(pic); | |
| } | |
| } | |
| return true; | |
| } | |
| bool VaapiDecoderH265::DPB::initShortTermRef(const PicturePtr& picture, | |
| const H265SliceHdr* const slice) | |
| { | |
| const H265PPS *const pps = slice->pps; | |
| const H265SPS *const sps = pps->sps; | |
| const H265ShortTermRefPicSet* stRef; | |
| if (!slice->short_term_ref_pic_set_sps_flag) | |
| stRef = &slice->short_term_ref_pic_sets; | |
| else | |
| stRef = &sps->short_term_ref_pic_set[slice->short_term_ref_pic_set_idx]; | |
| //clear it here | |
| m_stFoll.clear(); | |
| if (!initShortTermRef(m_stCurrBefore, picture->m_poc, | |
| stRef->DeltaPocS0, stRef->UsedByCurrPicS0, stRef->NumNegativePics)) | |
| return false; | |
| if (!initShortTermRef(m_stCurrAfter, picture->m_poc, | |
| stRef->DeltaPocS1, stRef->UsedByCurrPicS1, stRef->NumPositivePics)) | |
| return false; | |
| return true; | |
| } | |
| bool VaapiDecoderH265::DPB::initLongTermRef(const PicturePtr& picture, const H265SliceHdr *const slice) | |
| { | |
| const H265PPS *const pps = slice->pps; | |
| const H265SPS *const sps = pps->sps; | |
| int32_t deltaPocMsbCycleLt[16]; | |
| const int32_t maxPicOrderCntLsb =1 << (sps->log2_max_pic_order_cnt_lsb_minus4 + 4); | |
| uint16_t num = slice->num_long_term_sps + slice->num_long_term_pics; | |
| //(7-38) | |
| for (int i = 0; i < num; i++) { | |
| if( i == 0 || i == slice->num_long_term_sps) | |
| deltaPocMsbCycleLt[ i ] = slice->delta_poc_msb_cycle_lt[i]; | |
| else | |
| deltaPocMsbCycleLt[ i ] = slice->delta_poc_msb_cycle_lt[ i ] + deltaPocMsbCycleLt[i-1]; | |
| } | |
| //(8-5) | |
| for (int i = 0; i < num; i++) { | |
| uint16_t poc; | |
| bool used; | |
| if (i < slice->num_long_term_sps) { | |
| poc = sps->lt_ref_pic_poc_lsb_sps[slice->lt_idx_sps[i]]; | |
| used = sps->used_by_curr_pic_lt_sps_flag[slice->lt_idx_sps[i]]; | |
| } else { | |
| poc = slice->poc_lsb_lt[i]; | |
| used = slice->used_by_curr_pic_lt_flag[i]; | |
| } | |
| if (slice->delta_poc_msb_present_flag[i]) { | |
| poc += picture->m_poc - deltaPocMsbCycleLt[i] * maxPicOrderCntLsb | |
| - slice->pic_order_cnt_lsb; | |
| } | |
| VaapiDecPictureH265* pic = getPic(poc, slice->delta_poc_msb_present_flag[i]); | |
| if (!pic) { | |
| ERROR("can't find long ref %d for %d", poc, picture->m_poc); | |
| } else { | |
| if (used) | |
| m_ltCurr.push_back(pic); | |
| else | |
| m_ltFoll.push_back(pic); | |
| } | |
| } | |
| return true; | |
| } | |
| void markUnusedReference(const PicturePtr& picture) | |
| { | |
| picture->m_isUnusedReference = true; | |
| } | |
| void clearReference(const PicturePtr& picture) | |
| { | |
| if (picture->m_isUnusedReference) | |
| picture->m_isReference = false; | |
| } | |
| inline bool isOutputNeeded(const PicturePtr& picture) | |
| { | |
| return picture->m_picOutputFlag; | |
| } | |
| inline bool isReference(const PicturePtr& picture) | |
| { | |
| return picture->m_isReference; | |
| } | |
| inline bool isUnusedPicture(const PicturePtr& picture) | |
| { | |
| return !isReference(picture) && !isOutputNeeded(picture); | |
| } | |
| void VaapiDecoderH265::DPB::removeUnused() | |
| { | |
| forEach(clearReference); | |
| /* Remove unused pictures from DPB */ | |
| PictureList::iterator it; | |
| for (it = m_pictures.begin(); it != m_pictures.end();) { | |
| if (isUnusedPicture(*it)) | |
| m_pictures.erase(it++); | |
| else | |
| ++it; | |
| } | |
| } | |
| void VaapiDecoderH265::DPB::clearRefSet() | |
| { | |
| m_stCurrBefore.clear(); | |
| m_stCurrAfter.clear(); | |
| m_stFoll.clear(); | |
| m_ltCurr.clear(); | |
| m_ltFoll.clear(); | |
| } | |
| /*8.3.2*/ | |
| bool VaapiDecoderH265::DPB::initReference(const PicturePtr& picture, | |
| const H265SliceHdr *const slice, const H265NalUnit *const nalu, bool newStream) | |
| { | |
| clearRefSet(); | |
| if (isIdr(nalu)) | |
| return true; | |
| if (!initShortTermRef(picture, slice)) | |
| return false; | |
| if (!initLongTermRef(picture, slice)) | |
| return false; | |
| return true; | |
| } | |
| bool matchPocLsb(const PicturePtr& picture, int32_t poc) | |
| { | |
| return picture->m_pocLsb == poc; | |
| } | |
| VaapiDecPictureH265* VaapiDecoderH265::DPB::getPic(int32_t poc, bool hasMsb) | |
| { | |
| PictureList::iterator it; | |
| if (hasMsb) { | |
| m_dummy->m_poc = poc; | |
| it = m_pictures.find(m_dummy); | |
| } else { | |
| it = find_if(m_pictures.begin(), m_pictures.end(), bind(matchPocLsb, _1, poc)); | |
| } | |
| if (it != m_pictures.end()) { | |
| const PicturePtr& picture = *it; | |
| if (picture->m_isReference) { | |
| //use by current decode picture | |
| picture->m_isUnusedReference = false; | |
| return picture.get(); | |
| } | |
| } | |
| return NULL; | |
| } | |
| void VaapiDecoderH265::DPB::forEach(ForEachFunction fn) | |
| { | |
| std::for_each(m_pictures.begin(), m_pictures.end(), fn); | |
| } | |
| bool VaapiDecoderH265::DPB::checkReorderPics(const H265SPS* const sps) | |
| { | |
| uint32_t num = count_if(m_pictures.begin(), m_pictures.end(), isOutputNeeded); | |
| return num > sps->max_num_reorder_pics[sps->max_sub_layers_minus1]; | |
| } | |
| bool checkPicLatencyCount(const PicturePtr& picture, uint32_t spsMaxLatencyPictures) | |
| { | |
| return isOutputNeeded(picture) && (picture->m_picLatencyCount >= spsMaxLatencyPictures); | |
| } | |
| bool VaapiDecoderH265::DPB::checkLatency(const H265SPS* const sps) | |
| { | |
| uint8_t highestTid = sps->max_sub_layers_minus1; | |
| if (!sps->max_latency_increase_plus1[highestTid]) | |
| return false; | |
| uint16_t spsMaxLatencyPictures = sps->max_num_reorder_pics[highestTid] | |
| + sps->max_latency_increase_plus1[highestTid] - 1; | |
| return find_if(m_pictures.begin(), | |
| m_pictures.end(), | |
| bind(checkPicLatencyCount, _1, spsMaxLatencyPictures) | |
| ) != m_pictures.end(); | |
| } | |
| bool VaapiDecoderH265::DPB::checkDpbSize(const H265SPS* const sps) | |
| { | |
| uint8_t highestTid = sps->max_sub_layers_minus1; | |
| return m_pictures.size() >= (size_t)(sps->max_dec_pic_buffering_minus1[highestTid] + 1); | |
| } | |
| //C.5.2.2 | |
| bool VaapiDecoderH265::DPB::init(const PicturePtr& picture, | |
| const H265SliceHdr *const slice, const H265NalUnit *const nalu, bool newStream) | |
| { | |
| forEach(markUnusedReference); | |
| if (!initReference(picture, slice, nalu, newStream)) | |
| return false; | |
| if (isIrap(nalu) && picture->m_noRaslOutputFlag && !newStream) { | |
| bool noOutputOfPriorPicsFlag; | |
| //TODO how to check C.5.2.2 item 1's second otherwise | |
| if (isCra(nalu)) | |
| noOutputOfPriorPicsFlag = true; | |
| else | |
| noOutputOfPriorPicsFlag = slice->no_output_of_prior_pics_flag; | |
| clearRefSet(); | |
| if (!noOutputOfPriorPicsFlag) { | |
| removeUnused(); | |
| bumpAll(); | |
| } | |
| m_pictures.clear(); | |
| return true; | |
| } | |
| removeUnused(); | |
| const H265PPS* const pps = slice->pps; | |
| const H265SPS* const sps = pps->sps; | |
| while (checkReorderPics(sps) || checkLatency(sps) || checkDpbSize(sps)) | |
| bump(); | |
| return true; | |
| } | |
| bool VaapiDecoderH265::DPB::output(const PicturePtr& picture) | |
| { | |
| picture->m_picOutputFlag = false; | |
| // ERROR("DPB: output picture(Poc:%d)", picture->m_poc); | |
| return m_output(picture) == DECODE_SUCCESS; | |
| } | |
| bool VaapiDecoderH265::DPB::bump() | |
| { | |
| PictureList::iterator it = | |
| find_if(m_pictures.begin(),m_pictures.end(), isOutputNeeded); | |
| if (it == m_pictures.end()) | |
| return false; | |
| bool success = output(*it); | |
| if (!isReference(*it)) | |
| m_pictures.erase(it); | |
| return success; | |
| } | |
| void VaapiDecoderH265::DPB::bumpAll() | |
| { | |
| while (bump()) | |
| /* nothing */; | |
| } | |
| void addLatency(const PicturePtr& picture) | |
| { | |
| if (picture->m_picOutputFlag) | |
| picture->m_picLatencyCount++; | |
| } | |
| bool VaapiDecoderH265::DPB::add(const PicturePtr& picture, const H265SliceHdr* const lastSlice) | |
| { | |
| const H265PPS* const pps = lastSlice->pps; | |
| const H265SPS* const sps = pps->sps; | |
| forEach(addLatency); | |
| picture->m_picLatencyCount = 0; | |
| picture->m_isReference = true; | |
| m_pictures.insert(picture); | |
| while (checkReorderPics(sps) || checkLatency(sps)) | |
| bump(); | |
| return true; | |
| } | |
| void VaapiDecoderH265::DPB::flush() | |
| { | |
| bumpAll(); | |
| clearRefSet(); | |
| m_pictures.clear(); | |
| } | |
| VaapiDecoderH265::VaapiDecoderH265(): | |
| m_prevPicOrderCntMsb(0), | |
| m_prevPicOrderCntLsb(0), | |
| m_newStream(true), | |
| m_endOfSequence(false), | |
| m_dpb(bind(&VaapiDecoderH265::outputPicture, this, _1)) | |
| { | |
| m_parser = h265_parser_new(); | |
| m_prevSlice.reset(new H265SliceHdr(), h265SliceHdrFree); | |
| } | |
| VaapiDecoderH265::~VaapiDecoderH265() | |
| { | |
| stop(); | |
| h265_parser_free(m_parser); | |
| } | |
| Decode_Status VaapiDecoderH265::start(VideoConfigBuffer * buffer) | |
| { | |
| return DECODE_SUCCESS; | |
| } | |
| Decode_Status VaapiDecoderH265::decodeParamSet(H265NalUnit *nalu) | |
| { | |
| H265ParserResult result = h265_parser_parse_nal(m_parser, nalu); | |
| return result == H265_PARSER_OK ? DECODE_SUCCESS : DECODE_INVALID_DATA; | |
| } | |
| Decode_Status VaapiDecoderH265::outputPicture(const PicturePtr& picture) | |
| { | |
| VaapiDecoderBase::PicturePtr base = std::tr1::static_pointer_cast<VaapiDecPicture>(picture); | |
| return VaapiDecoderBase::outputPicture(base); | |
| } | |
| Decode_Status VaapiDecoderH265::decodeCurrent() | |
| { | |
| Decode_Status status = DECODE_SUCCESS; | |
| if (!m_current) | |
| return status; | |
| if (!m_current->decode()) { | |
| ERROR("decode %d failed", m_current->m_poc); | |
| //ignore it | |
| return status; | |
| } | |
| if (!m_dpb.add(m_current, m_prevSlice.get())) | |
| return DECODE_INVALID_DATA; | |
| m_current.reset(); | |
| m_newStream = false; | |
| return status; | |
| } | |
| #define FILL_SCALING_LIST(mxm) \ | |
| void fillScalingList##mxm(VAIQMatrixBufferHEVC* iqMatrix, const H265ScalingList* const scalingList) \ | |
| { \ | |
| for (size_t i = 0; i < N_ELEMENTS(iqMatrix->ScalingList##mxm); i++) { \ | |
| h265_quant_matrix_##mxm##_get_raster_from_uprightdiagonal(iqMatrix->ScalingList##mxm[i], \ | |
| scalingList->scaling_lists_##mxm[i]); \ | |
| } \ | |
| } | |
| FILL_SCALING_LIST(4x4) | |
| FILL_SCALING_LIST(8x8) | |
| FILL_SCALING_LIST(16x16) | |
| FILL_SCALING_LIST(32x32) | |
| #define FILL_SCALING_LIST_DC(mxm) \ | |
| void fillScalingListDc##mxm(VAIQMatrixBufferHEVC* iqMatrix, const H265ScalingList* const scalingList) \ | |
| { \ | |
| for (size_t i = 0; i < N_ELEMENTS(iqMatrix->ScalingListDC##mxm); i++) { \ | |
| iqMatrix->ScalingListDC##mxm[i] = \ | |
| scalingList->scaling_list_dc_coef_minus8_##mxm[i] + 8; \ | |
| } \ | |
| } | |
| FILL_SCALING_LIST_DC(16x16) | |
| FILL_SCALING_LIST_DC(32x32) | |
| bool VaapiDecoderH265::fillIqMatrix(const PicturePtr& picture, const H265SliceHdr* const slice) | |
| { | |
| H265PPS* pps = slice->pps; | |
| H265SPS* sps = pps->sps; | |
| H265ScalingList* scalingList; | |
| if (pps->scaling_list_data_present_flag) { | |
| scalingList = &pps->scaling_list; | |
| } else if(sps->scaling_list_enabled_flag) { | |
| if(sps->scaling_list_data_present_flag) { | |
| scalingList = &sps->scaling_list; | |
| } else { | |
| scalingList = &pps->scaling_list; | |
| } | |
| } else { | |
| //default scaling list | |
| return true; | |
| } | |
| VAIQMatrixBufferHEVC* iqMatrix; | |
| if (!picture->editIqMatrix(iqMatrix)) | |
| return false; | |
| fillScalingList4x4(iqMatrix, scalingList); | |
| fillScalingList8x8(iqMatrix, scalingList); | |
| fillScalingList16x16(iqMatrix, scalingList); | |
| fillScalingList32x32(iqMatrix, scalingList); | |
| fillScalingListDc16x16(iqMatrix, scalingList); | |
| fillScalingListDc32x32(iqMatrix, scalingList); | |
| return true; | |
| } | |
| void VaapiDecoderH265::fillReference(VAPictureHEVC* refs, int32_t& n, | |
| const RefSet& refset, uint32_t flags) | |
| { | |
| //ERROR("fill ref(%x):", flags); | |
| for (size_t i = 0; i < refset.size(); i++) { | |
| VAPictureHEVC* r = refs + n; | |
| const VaapiDecPictureH265* pic = refset[i]; | |
| r->picture_id = refset[i]->getSurfaceID(); | |
| r->pic_order_cnt = pic->m_poc; | |
| r->flags = flags; | |
| //record for late use | |
| m_pocToIndex[pic->m_poc] = n; | |
| // ERROR("%d", pic->m_poc); | |
| n++; | |
| } | |
| } | |
| void VaapiDecoderH265::fillReference(VAPictureHEVC* refs, int32_t size) | |
| { | |
| int32_t n = 0; | |
| //clear index map | |
| m_pocToIndex.clear(); | |
| fillReference(refs, n, m_dpb.m_stCurrBefore, VA_PICTURE_HEVC_RPS_ST_CURR_BEFORE); | |
| fillReference(refs, n, m_dpb.m_stCurrAfter, VA_PICTURE_HEVC_RPS_ST_CURR_AFTER); | |
| fillReference(refs, n, m_dpb.m_stFoll, 0); | |
| fillReference(refs, n, m_dpb.m_ltCurr, VA_PICTURE_HEVC_LONG_TERM_REFERENCE | VA_PICTURE_HEVC_RPS_LT_CURR); | |
| fillReference(refs, n, m_dpb.m_ltFoll, VA_PICTURE_HEVC_LONG_TERM_REFERENCE); | |
| for (int i = n; i < size; i++) { | |
| VAPictureHEVC* ref = refs + i; | |
| ref->picture_id = VA_INVALID_SURFACE; | |
| ref->pic_order_cnt = 0; | |
| ref->flags = VA_PICTURE_HEVC_INVALID; | |
| } | |
| } | |
| bool VaapiDecoderH265::fillPicture(const PicturePtr& picture, const H265SliceHdr* const slice) | |
| { | |
| VAPictureParameterBufferHEVC* param; | |
| if (!picture->editPicture(param)) | |
| return false; | |
| param->CurrPic.picture_id = picture->getSurfaceID(); | |
| param->CurrPic.pic_order_cnt = picture->m_poc; | |
| fillReference(param->ReferenceFrames, N_ELEMENTS(param->ReferenceFrames)); | |
| H265PPS* pps = slice->pps; | |
| H265SPS* sps = pps->sps; | |
| #define FILL(h, f) param->f = h->f | |
| FILL(sps, pic_width_in_luma_samples); | |
| FILL(sps, pic_height_in_luma_samples); | |
| #define FILL_PIC(h, f) param->pic_fields.bits.f = h->f | |
| FILL_PIC(sps, chroma_format_idc); | |
| FILL_PIC(sps, separate_colour_plane_flag); | |
| FILL_PIC(sps, pcm_enabled_flag); | |
| FILL_PIC(sps, scaling_list_enabled_flag); | |
| FILL_PIC(pps, transform_skip_enabled_flag); | |
| FILL_PIC(sps, amp_enabled_flag); | |
| FILL_PIC(sps, strong_intra_smoothing_enabled_flag); | |
| FILL_PIC(pps, sign_data_hiding_enabled_flag); | |
| FILL_PIC(pps, constrained_intra_pred_flag); | |
| FILL_PIC(pps, cu_qp_delta_enabled_flag); | |
| FILL_PIC(pps, weighted_pred_flag); | |
| FILL_PIC(pps, weighted_bipred_flag); | |
| FILL_PIC(pps, transquant_bypass_enabled_flag); | |
| FILL_PIC(pps, tiles_enabled_flag); | |
| FILL_PIC(pps, entropy_coding_sync_enabled_flag); | |
| param->pic_fields.bits.pps_loop_filter_across_slices_enabled_flag | |
| = pps->loop_filter_across_slices_enabled_flag; | |
| FILL_PIC(pps, loop_filter_across_tiles_enabled_flag); | |
| FILL_PIC(sps, pcm_loop_filter_disabled_flag); | |
| //how to fill this? | |
| //NoPicReorderingFlag | |
| //NoBiPredFlag | |
| param->sps_max_dec_pic_buffering_minus1 = | |
| sps->max_dec_pic_buffering_minus1[0]; | |
| FILL(sps, bit_depth_luma_minus8); | |
| FILL(sps, bit_depth_chroma_minus8); | |
| FILL(sps, pcm_sample_bit_depth_luma_minus1); | |
| FILL(sps, pcm_sample_bit_depth_chroma_minus1); | |
| FILL(sps, log2_min_luma_coding_block_size_minus3); | |
| FILL(sps, log2_diff_max_min_luma_coding_block_size); | |
| FILL(sps, log2_min_transform_block_size_minus2); | |
| FILL(sps, log2_diff_max_min_transform_block_size); | |
| FILL(sps, log2_min_pcm_luma_coding_block_size_minus3); | |
| FILL(sps, log2_diff_max_min_pcm_luma_coding_block_size); | |
| FILL(sps, max_transform_hierarchy_depth_intra); | |
| FILL(sps, max_transform_hierarchy_depth_inter); | |
| FILL(pps, init_qp_minus26); | |
| FILL(pps, diff_cu_qp_delta_depth); | |
| param->pps_cb_qp_offset = pps->cb_qp_offset; | |
| param->pps_cr_qp_offset = pps->cr_qp_offset; | |
| FILL(pps, log2_parallel_merge_level_minus2); | |
| FILL(pps, num_tile_columns_minus1); | |
| FILL(pps, num_tile_rows_minus1); | |
| for (int i = 0; i <= pps->num_tile_columns_minus1; i++) { | |
| param->column_width_minus1[i] = pps->column_width_minus1[i]; | |
| } | |
| for (int i = 0; i <= pps->num_tile_rows_minus1; i++) { | |
| param->row_height_minus1[i] = pps->row_height_minus1[i]; | |
| } | |
| #define FILL_SLICE(h, f) param->slice_parsing_fields.bits.f = h->f | |
| #define FILL_SLICE_1(h, f) param->slice_parsing_fields.bits.h##_##f = h->f | |
| FILL_SLICE(pps, lists_modification_present_flag); | |
| FILL_SLICE(sps, long_term_ref_pics_present_flag); | |
| FILL_SLICE_1(sps, temporal_mvp_enabled_flag); | |
| FILL_SLICE(pps, cabac_init_present_flag); | |
| FILL_SLICE(pps, output_flag_present_flag); | |
| FILL_SLICE(pps, dependent_slice_segments_enabled_flag); | |
| FILL_SLICE_1(pps, slice_chroma_qp_offsets_present_flag); | |
| FILL_SLICE(sps, sample_adaptive_offset_enabled_flag); | |
| FILL_SLICE(pps, deblocking_filter_override_enabled_flag); | |
| param->slice_parsing_fields.bits.pps_disable_deblocking_filter_flag = | |
| pps->deblocking_filter_disabled_flag; | |
| FILL_SLICE(pps, slice_segment_header_extension_present_flag); | |
| /* how to fill following fields | |
| RapPicFlag | |
| IdrPicFlag | |
| IntraPicFlag */ | |
| FILL(sps, log2_max_pic_order_cnt_lsb_minus4); | |
| FILL(sps, num_short_term_ref_pic_sets); | |
| param->num_long_term_ref_pic_sps = sps->num_long_term_ref_pics_sps; | |
| FILL(pps, num_ref_idx_l0_default_active_minus1); | |
| FILL(pps, num_ref_idx_l1_default_active_minus1); | |
| param->pps_beta_offset_div2 = pps->beta_offset_div2; | |
| param->pps_tc_offset_div2 = pps->tc_offset_div2; | |
| FILL(pps, num_extra_slice_header_bits); | |
| /* how to fill this | |
| st_rps_bits*/ | |
| #undef FILL | |
| #undef FILL_PIC | |
| #undef FILL_SLICE | |
| #undef FILL_SLICE_1 | |
| return true; | |
| } | |
| bool VaapiDecoderH265::getRefPicList(RefSet& refset, const RefSet& stCurr0, const RefSet& stCurr1, | |
| uint8_t numActive, bool modify, const uint32_t* modiList) | |
| { | |
| if (numActive > 15) { | |
| ERROR("bug: reference picutre can't large than 15"); | |
| return false; | |
| } | |
| const RefSet& ltCurr = m_dpb.m_ltCurr; | |
| uint8_t numPocTotalCurr = stCurr0.size() + stCurr1.size() + ltCurr.size(); | |
| if (numActive && !numPocTotalCurr) { | |
| ERROR("active refs is %d, but num numPocTotalCurr is %d", numActive, numPocTotalCurr); | |
| return false; | |
| } | |
| uint8_t numRpsCurrTempList = std::max(numPocTotalCurr, numActive); | |
| RefSet temp; | |
| temp.reserve(numRpsCurrTempList); | |
| uint32_t rIdx = 0; | |
| //(8-8) and (8-10) | |
| while (rIdx < numRpsCurrTempList) { | |
| for(uint32_t i = 0; i < stCurr0.size() && rIdx < numRpsCurrTempList; rIdx++, i++ ) | |
| temp.push_back(stCurr0[i]); | |
| for(uint32_t i = 0; i < stCurr1.size() && rIdx < numRpsCurrTempList; rIdx++, i++ ) | |
| temp.push_back(stCurr1[i]); | |
| for(uint32_t i = 0; i < ltCurr.size() && rIdx < numRpsCurrTempList; rIdx++, i++ ) | |
| temp.push_back(ltCurr[i]); | |
| } | |
| refset.clear(); | |
| refset.reserve(numActive); | |
| //(8-9) and (8-11) | |
| for( rIdx = 0; rIdx < numActive; rIdx++) { | |
| uint8_t idx = modify ? modiList[rIdx] : rIdx; | |
| if (idx < temp.size()) { | |
| refset.push_back(temp[idx]); | |
| } else { | |
| ERROR("can't get idx from temp ref, modify = %d, idx = %d, iIdx = %d", modify, idx, rIdx); | |
| } | |
| } | |
| return true; | |
| } | |
| uint8_t VaapiDecoderH265::getIndex(int32_t poc) | |
| { | |
| return m_pocToIndex[poc]; | |
| } | |
| void VaapiDecoderH265::fillReferenceIndexForList(VASliceParameterBufferHEVC* sliceParam, | |
| const RefSet& refset, bool isList0) | |
| { | |
| int n = isList0?0:1; | |
| uint32_t i; | |
| for (i = 0; i < refset.size(); i++) { | |
| sliceParam->RefPicList[n][i] = getIndex(refset[i]->m_poc); | |
| } | |
| for ( ; i < N_ELEMENTS(sliceParam->RefPicList[n]); i++) { | |
| sliceParam->RefPicList[n][i] = 0xFF; | |
| } | |
| } | |
| // 8.3.4 | |
| bool VaapiDecoderH265::fillReferenceIndex(VASliceParameterBufferHEVC* sliceParam, const H265SliceHdr* const slice) | |
| { | |
| RefSet& before = m_dpb.m_stCurrBefore; | |
| RefSet& after = m_dpb.m_stCurrAfter; | |
| RefSet refset; | |
| if (!H265_IS_I_SLICE(slice)) { | |
| if (!getRefPicList(refset, before, after, | |
| slice->num_ref_idx_l0_active_minus1 + 1, | |
| slice->ref_pic_list_modification.ref_pic_list_modification_flag_l0, | |
| slice->ref_pic_list_modification.list_entry_l0)) { | |
| return false; | |
| } | |
| } | |
| fillReferenceIndexForList(sliceParam, refset, true); | |
| refset.clear(); | |
| if (H265_IS_B_SLICE(slice)) { | |
| if (!getRefPicList(refset, after, before, | |
| slice->num_ref_idx_l1_active_minus1 + 1, | |
| slice->ref_pic_list_modification.ref_pic_list_modification_flag_l1, | |
| slice->ref_pic_list_modification.list_entry_l1)) { | |
| return false; | |
| } | |
| } | |
| fillReferenceIndexForList(sliceParam, refset, false); | |
| sliceParam->num_ref_idx_l0_active_minus1 = slice->num_ref_idx_l0_active_minus1; | |
| sliceParam->num_ref_idx_l1_active_minus1 = slice->num_ref_idx_l1_active_minus1; | |
| return true; | |
| } | |
| inline int32_t clip3(int32_t x, int32_t y, int32_t z) | |
| { | |
| if (z < x) | |
| return x; | |
| if (z > y) | |
| return y; | |
| return z; | |
| } | |
| #define FILL_WEIGHT_TABLE(n) \ | |
| void fillPredWedightTableL##n(VASliceParameterBufferHEVC* sliceParam, \ | |
| const H265SliceHdr* slice, uint8_t chromaLog2WeightDenom) \ | |
| { \ | |
| const H265PredWeightTable& w = slice->pred_weight_table; \ | |
| for (int i = 0; i <= sliceParam->num_ref_idx_l##n##_active_minus1; i++) { \ | |
| if (w.luma_weight_l##n##_flag[i]) { \ | |
| sliceParam->delta_luma_weight_l##n[i] = w.delta_luma_weight_l##n[i]; \ | |
| sliceParam->luma_offset_l##n[i] = w.luma_offset_l##n[i];\ | |
| } \ | |
| if (w.chroma_weight_l##n##_flag[i]) { \ | |
| for (int j = 0; j < 2; j++) { \ | |
| int8_t deltaWeight = w.delta_chroma_weight_l##n[i][j]; \ | |
| int32_t chromaWeight = (1 << chromaLog2WeightDenom) + deltaWeight; \ | |
| int16_t deltaOffset = w.delta_chroma_offset_l##n[i][j]; \ | |
| int32_t chromaOffset = \ | |
| 128 + deltaOffset - ((128*chromaWeight)>>chromaLog2WeightDenom);\ | |
| \ | |
| sliceParam->delta_chroma_weight_l##n[i][j] = deltaWeight; \ | |
| sliceParam->ChromaOffsetL##n[i][j]= (int8_t)clip3(-128, 127, chromaOffset); \ | |
| } \ | |
| } \ | |
| } \ | |
| } | |
| FILL_WEIGHT_TABLE(0) | |
| FILL_WEIGHT_TABLE(1) | |
| bool VaapiDecoderH265::fillPredWeightTable(VASliceParameterBufferHEVC* sliceParam, const H265SliceHdr* const slice) | |
| { | |
| H265PPS* pps = slice->pps; | |
| H265SPS* sps = pps->sps; | |
| const H265PredWeightTable& w = slice->pred_weight_table; | |
| if ((pps->weighted_pred_flag && H265_IS_P_SLICE (slice)) || | |
| (pps->weighted_bipred_flag && H265_IS_B_SLICE (slice))) { | |
| uint8_t chromaLog2WeightDenom = w.luma_log2_weight_denom; | |
| sliceParam->luma_log2_weight_denom = w.luma_log2_weight_denom; | |
| if (sps->chroma_format_idc != 0) { | |
| sliceParam->delta_chroma_log2_weight_denom | |
| = w.delta_chroma_log2_weight_denom; | |
| chromaLog2WeightDenom | |
| += w.delta_chroma_log2_weight_denom; | |
| } | |
| fillPredWedightTableL0(sliceParam, slice, chromaLog2WeightDenom); | |
| if (pps->weighted_bipred_flag && H265_IS_B_SLICE (slice)) | |
| fillPredWedightTableL1(sliceParam, slice, chromaLog2WeightDenom); | |
| } | |
| return true; | |
| } | |
| static inline uint32_t | |
| getSliceDataByteOffset(const H265SliceHdr* const sliceHdr, uint32_t nalHeaderBytes) | |
| { | |
| return nalHeaderBytes + (sliceHdr->header_size + 7) / 8 | |
| - sliceHdr->n_emulation_prevention_bytes; | |
| } | |
| bool VaapiDecoderH265::fillSlice(const PicturePtr& picture, | |
| const H265SliceHdr* const theSlice, const H265NalUnit* const nalu) | |
| { | |
| const H265SliceHdr* slice = theSlice; | |
| VASliceParameterBufferHEVC* sliceParam; | |
| if (!picture->newSlice(sliceParam, nalu->data + nalu->offset, nalu->size)) | |
| return false; | |
| sliceParam->slice_data_byte_offset = | |
| getSliceDataByteOffset(slice, nalu->header_bytes); | |
| sliceParam->slice_segment_address = slice->segment_address; | |
| #define FILL_LONG(f) sliceParam->LongSliceFlags.fields.f = slice->f | |
| #define FILL_LONG_SLICE(f) sliceParam->LongSliceFlags.fields.slice_##f = slice->f | |
| //how to fill this | |
| //LastSliceOfPic | |
| FILL_LONG(dependent_slice_segment_flag); | |
| //follow spec | |
| if (slice->dependent_slice_segment_flag) { | |
| slice = m_prevSlice.get(); | |
| } | |
| if (!fillReferenceIndex(sliceParam, slice)) | |
| return false; | |
| FILL_LONG_SLICE(type); | |
| sliceParam->LongSliceFlags.fields.color_plane_id = slice->colour_plane_id; | |
| FILL_LONG_SLICE(sao_luma_flag); | |
| FILL_LONG_SLICE(sao_chroma_flag); | |
| FILL_LONG(mvd_l1_zero_flag); | |
| FILL_LONG(cabac_init_flag); | |
| FILL_LONG_SLICE(temporal_mvp_enabled_flag); | |
| if (slice->deblocking_filter_override_flag) | |
| FILL_LONG_SLICE(deblocking_filter_disabled_flag); | |
| else | |
| sliceParam->LongSliceFlags.fields.slice_deblocking_filter_disabled_flag= | |
| slice->pps->deblocking_filter_disabled_flag; | |
| FILL_LONG(collocated_from_l0_flag); | |
| FILL_LONG_SLICE(loop_filter_across_slices_enabled_flag); | |
| #define FILL(f) sliceParam->f = slice->f | |
| #define FILL_SLICE(f) sliceParam->slice_##f = slice->f | |
| FILL(collocated_ref_idx); | |
| /* following fields fill in fillReference | |
| num_ref_idx_l0_active_minus1 | |
| num_ref_idx_l1_active_minus1*/ | |
| FILL_SLICE(qp_delta); | |
| FILL_SLICE(cb_qp_offset); | |
| FILL_SLICE(cr_qp_offset); | |
| FILL_SLICE(beta_offset_div2); | |
| FILL_SLICE(tc_offset_div2); | |
| if (!fillPredWeightTable(sliceParam, slice)) | |
| return false; | |
| FILL(five_minus_max_num_merge_cand); | |
| return true; | |
| } | |
| Decode_Status VaapiDecoderH265::ensureContext(const H265SPS* const sps) | |
| { | |
| uint8_t surfaceNumber = sps->max_dec_pic_buffering_minus1[0] + 1 + H265_EXTRA_SURFACE_NUMBER; | |
| if (m_configBuffer.surfaceWidth < sps->width | |
| || m_configBuffer.surfaceHeight < sps->height | |
| || m_configBuffer.surfaceNumber < surfaceNumber) { | |
| INFO("frame size changed, reconfig codec. orig size %d x %d, new size: %d x %d", | |
| m_configBuffer.width, m_configBuffer.height, sps->width, sps->height); | |
| Decode_Status status = VaapiDecoderBase::terminateVA(); | |
| if (status != DECODE_SUCCESS) | |
| return status; | |
| m_configBuffer.width = sps->conformance_window_flag ? sps->crop_rect_width : sps->width; | |
| m_configBuffer.height = sps->conformance_window_flag ? sps->crop_rect_height : sps->height; | |
| m_configBuffer.surfaceWidth = sps->width; | |
| m_configBuffer.surfaceHeight =sps->height; | |
| m_configBuffer.flag |= HAS_SURFACE_NUMBER; | |
| m_configBuffer.profile = VAProfileHEVCMain; | |
| m_configBuffer.flag &= ~USE_NATIVE_GRAPHIC_BUFFER; | |
| m_configBuffer.surfaceNumber = surfaceNumber; | |
| status = VaapiDecoderBase::start(&m_configBuffer); | |
| if (status != DECODE_SUCCESS) | |
| return status; | |
| return DECODE_FORMAT_CHANGE; | |
| } | |
| return (m_context) ? DECODE_SUCCESS : DECODE_FAIL; | |
| } | |
| /* 8.3.1 */ | |
| void VaapiDecoderH265::getPoc(const PicturePtr& picture, | |
| const H265SliceHdr* const slice, | |
| const H265NalUnit* const nalu) | |
| { | |
| const H265PPS* const pps = slice->pps; | |
| const H265SPS* const sps = pps->sps; | |
| const uint16_t pocLsb = slice->pic_order_cnt_lsb; | |
| const int32_t MaxPicOrderCntLsb = 1 << (sps->log2_max_pic_order_cnt_lsb_minus4 + 4); | |
| int32_t picOrderCntMsb; | |
| if (isIrap(nalu) && picture->m_noRaslOutputFlag) { | |
| picOrderCntMsb = 0; | |
| } else { | |
| if((pocLsb < m_prevPicOrderCntLsb) | |
| && ((m_prevPicOrderCntLsb - pocLsb) >= (MaxPicOrderCntLsb / 2))) { | |
| picOrderCntMsb = m_prevPicOrderCntMsb + MaxPicOrderCntLsb; | |
| } else if((pocLsb > m_prevPicOrderCntLsb) | |
| && ((pocLsb - m_prevPicOrderCntLsb) > (MaxPicOrderCntLsb / 2))) { | |
| picOrderCntMsb = m_prevPicOrderCntMsb - MaxPicOrderCntLsb; | |
| } else { | |
| picOrderCntMsb = m_prevPicOrderCntMsb; | |
| } | |
| } | |
| picture->m_poc = picOrderCntMsb + pocLsb; | |
| picture->m_pocLsb = pocLsb; | |
| //ERROR("poc = %d", picture->m_poc); | |
| uint8_t temporalID = nalu->temporal_id_plus1 - 1; | |
| //fixme:sub-layer non-reference picture. | |
| if (!temporalID && !isRasl(nalu) && !isRadl(nalu) && !isSublayerNoRef(nalu)) { | |
| m_prevPicOrderCntMsb = picOrderCntMsb; | |
| m_prevPicOrderCntLsb = pocLsb; | |
| } | |
| } | |
| PicturePtr VaapiDecoderH265::createPicture(const H265SliceHdr* const slice, | |
| const H265NalUnit* const nalu) | |
| { | |
| PicturePtr picture; | |
| SurfacePtr surface = createSurface(); | |
| if (!surface) | |
| return picture; | |
| picture.reset(new VaapiDecPictureH265(m_context, surface, m_currentPTS)); | |
| picture->m_noRaslOutputFlag = isIdr(nalu) || isBla(nalu) || | |
| m_newStream || m_endOfSequence; | |
| m_noRaslOutputFlag = picture->m_noRaslOutputFlag; | |
| if (isIrap(nalu)) | |
| m_associatedIrapNoRaslOutputFlag = picture->m_noRaslOutputFlag; | |
| picture->m_picOutputFlag | |
| = (isRasl(nalu) && m_associatedIrapNoRaslOutputFlag) ? false : slice->pic_output_flag; | |
| getPoc(picture, slice, nalu); | |
| return picture; | |
| } | |
| Decode_Status VaapiDecoderH265::decodeSlice(H265NalUnit *nalu) | |
| { | |
| SharedPtr<H265SliceHdr> currSlice(new H265SliceHdr(), h265SliceHdrFree); | |
| H265SliceHdr* slice = currSlice.get(); | |
| H265ParserResult result; | |
| Decode_Status status; | |
| memset(slice, 0, sizeof(H265SliceHdr)); | |
| result = h265_parser_parse_slice_hdr(m_parser, nalu, slice); | |
| if (result == H265_PARSER_ERROR) { | |
| return DECODE_INVALID_DATA; | |
| } | |
| if (result == H265_PARSER_BROKEN_LINK) { | |
| return DECODE_SUCCESS; | |
| } | |
| status = ensureContext(slice->pps->sps); | |
| if (status != DECODE_SUCCESS) { | |
| return status; | |
| } | |
| if (slice->first_slice_segment_in_pic_flag) { | |
| status = decodeCurrent(); | |
| if (status != DECODE_SUCCESS) | |
| return status; | |
| m_current = createPicture(slice, nalu); | |
| if (m_noRaslOutputFlag && isRasl(nalu)) | |
| return DECODE_SUCCESS; | |
| if (!m_current || !m_dpb.init(m_current, slice, nalu, m_newStream)) | |
| return DECODE_INVALID_DATA; | |
| if (!fillPicture(m_current, slice) || !fillIqMatrix(m_current, slice)) | |
| return DECODE_FAIL; | |
| } | |
| if (!m_current) | |
| return DECODE_FAIL; | |
| if (!fillSlice(m_current, slice, nalu)) | |
| return DECODE_FAIL; | |
| if (!slice->dependent_slice_segment_flag) | |
| std::swap(currSlice, m_prevSlice); | |
| return status; | |
| } | |
| Decode_Status VaapiDecoderH265::decodeNalu(H265NalUnit *nalu) | |
| { | |
| uint8_t type = nalu->type; | |
| Decode_Status status = DECODE_SUCCESS; | |
| if (H265_NAL_SLICE_TRAIL_N <= type && type <= H265_NAL_SLICE_CRA_NUT) { | |
| status = decodeSlice(nalu); | |
| if (status == DECODE_INVALID_DATA) { | |
| //ignore invalid data while decoding slice | |
| m_current.reset(); | |
| status = DECODE_SUCCESS; | |
| } | |
| } else { | |
| status = decodeCurrent(); | |
| if (status != DECODE_SUCCESS) | |
| return status; | |
| switch (type) { | |
| case H265_NAL_VPS: | |
| case H265_NAL_SPS: | |
| case H265_NAL_PPS: | |
| status = decodeParamSet(nalu); | |
| break; | |
| case H265_NAL_EOB: | |
| m_newStream = true; | |
| break; | |
| case H265_NAL_EOS: | |
| m_endOfSequence = true; | |
| break; | |
| case H265_NAL_AUD: | |
| case H265_NAL_FD: | |
| case H265_NAL_PREFIX_SEI: | |
| case H265_NAL_SUFFIX_SEI: | |
| default: | |
| break; | |
| } | |
| } | |
| return status; | |
| } | |
| Decode_Status VaapiDecoderH265::decode(VideoDecodeBuffer *buffer) | |
| { | |
| if (!buffer || !buffer->data) { | |
| decodeCurrent(); | |
| m_dpb.flush(); | |
| m_prevPicOrderCntMsb = 0; | |
| m_prevPicOrderCntLsb = 0; | |
| m_newStream = true; | |
| m_endOfSequence = false; | |
| return DECODE_SUCCESS; | |
| } | |
| m_currentPTS = buffer->timeStamp; | |
| NalReader nr(buffer->data, buffer->size); | |
| const uint8_t* nal; | |
| int32_t size; | |
| Decode_Status status; | |
| while (nr.read(nal, size)) { | |
| H265NalUnit nalu; | |
| if (H265_PARSER_OK == h265_parser_identify_nalu_unchecked(m_parser, nal, 0, size, &nalu)) { | |
| status = decodeNalu(&nalu); | |
| if (status != DECODE_SUCCESS) | |
| return status; | |
| } | |
| } | |
| return DECODE_SUCCESS; | |
| } | |
| const bool VaapiDecoderH265::s_registered = | |
| VaapiDecoderFactory::register_<VaapiDecoderH265>(YAMI_MIME_H265); | |
| } | |