Chromium Code Reviews| Index: content/common/gpu/media/h264_decoder.cc |
| diff --git a/content/common/gpu/media/h264_decoder.cc b/content/common/gpu/media/h264_decoder.cc |
| index 6f9c91804828a7f51a99ea78f81f135c55b58df6..6caedb5894dd16211248d8656ba58c833f31ba18 100644 |
| --- a/content/common/gpu/media/h264_decoder.cc |
| +++ b/content/common/gpu/media/h264_decoder.cc |
| @@ -21,13 +21,10 @@ H264Decoder::H264Accelerator::~H264Accelerator() { |
| } |
| H264Decoder::H264Decoder(H264Accelerator* accelerator) |
| - : max_pic_order_cnt_lsb_(0), |
| - max_frame_num_(0), |
| + : max_frame_num_(0), |
| max_pic_num_(0), |
| max_long_term_frame_idx_(0), |
| max_num_reorder_frames_(0), |
| - curr_sps_id_(-1), |
| - curr_pps_id_(-1), |
| accelerator_(accelerator) { |
| DCHECK(accelerator_); |
| Reset(); |
| @@ -41,10 +38,13 @@ void H264Decoder::Reset() { |
| curr_pic_ = nullptr; |
| curr_nalu_ = nullptr; |
| curr_slice_hdr_ = nullptr; |
| + curr_sps_id_ = -1; |
| + curr_pps_id_ = -1; |
| - frame_num_ = 0; |
| prev_frame_num_ = -1; |
| + prev_ref_frame_num_ = -1; |
| prev_frame_num_offset_ = -1; |
| + prev_has_memmgmnt5_ = false; |
| prev_ref_has_memmgmnt5_ = false; |
| prev_ref_top_field_order_cnt_ = -1; |
| @@ -65,14 +65,15 @@ void H264Decoder::Reset() { |
| state_ = kAfterReset; |
| } |
| -void H264Decoder::PrepareRefPicLists(media::H264SliceHeader* slice_hdr) { |
| +void H264Decoder::PrepareRefPicLists(const media::H264SliceHeader* slice_hdr) { |
| ConstructReferencePicListsP(slice_hdr); |
| ConstructReferencePicListsB(slice_hdr); |
| } |
| -bool H264Decoder::ModifyReferencePicLists(media::H264SliceHeader* slice_hdr, |
| - H264Picture::Vector* ref_pic_list0, |
| - H264Picture::Vector* ref_pic_list1) { |
| +bool H264Decoder::ModifyReferencePicLists( |
| + const media::H264SliceHeader* slice_hdr, |
| + H264Picture::Vector* ref_pic_list0, |
| + H264Picture::Vector* ref_pic_list1) { |
| ref_pic_list0->clear(); |
| ref_pic_list1->clear(); |
| @@ -97,10 +98,25 @@ bool H264Decoder::DecodePicture() { |
| return accelerator_->SubmitDecode(curr_pic_); |
| } |
| -bool H264Decoder::InitCurrPicture(media::H264SliceHeader* slice_hdr) { |
| +bool H264Decoder::InitNonexistingPicture(scoped_refptr<H264Picture> pic, |
|
xhwang
2015/10/12 18:01:17
nit: in media code, we pass const-ref of scoped_re
Pawel Osciak
2015/10/13 01:23:22
Yes, I'd like to do that too, but for now wanted t
|
| + int frame_num) { |
| + pic->nonexisting = true; |
| + pic->nal_ref_idc = 1; |
| + pic->frame_num = pic->pic_num = frame_num; |
| + pic->adaptive_ref_pic_marking_mode_flag = false; |
| + pic->ref = true; |
| + pic->long_term_reference_flag = false; |
| + pic->field = H264Picture::FIELD_NONE; |
| + |
| + return CalculatePicOrderCounts(pic); |
| +} |
| + |
| +bool H264Decoder::InitCurrPicture(const media::H264SliceHeader* slice_hdr) { |
| DCHECK(curr_pic_.get()); |
| curr_pic_->idr = slice_hdr->idr_pic_flag; |
| + if (curr_pic_->idr) |
| + curr_pic_->idr_pic_id = slice_hdr->idr_pic_id; |
| if (slice_hdr->field_pic_flag) { |
| curr_pic_->field = slice_hdr->bottom_field_flag ? H264Picture::FIELD_BOTTOM |
| @@ -109,11 +125,43 @@ bool H264Decoder::InitCurrPicture(media::H264SliceHeader* slice_hdr) { |
| curr_pic_->field = H264Picture::FIELD_NONE; |
| } |
| + if (curr_pic_->field != H264Picture::FIELD_NONE) { |
| + DVLOG(1) << "Interlaced video not supported."; |
| + return false; |
| + } |
| + |
| + curr_pic_->nal_ref_idc = slice_hdr->nal_ref_idc; |
| curr_pic_->ref = slice_hdr->nal_ref_idc != 0; |
| // This assumes non-interlaced stream. |
| curr_pic_->frame_num = curr_pic_->pic_num = slice_hdr->frame_num; |
| - if (!CalculatePicOrderCounts(slice_hdr)) |
| + DCHECK_NE(curr_sps_id_, -1); |
| + const media::H264SPS* sps = parser_.GetSPS(curr_sps_id_); |
| + if (!sps) |
| + return false; |
| + |
| + curr_pic_->pic_order_cnt_type = sps->pic_order_cnt_type; |
| + switch (curr_pic_->pic_order_cnt_type) { |
|
xhwang
2015/10/12 18:01:17
nit: can you have an enum for |pic_order_cnt_type|
Pawel Osciak
2015/10/13 01:23:22
This is intentional, as the H264 spec uses "pic or
|
| + case 0: |
| + curr_pic_->pic_order_cnt_lsb = slice_hdr->pic_order_cnt_lsb; |
| + curr_pic_->delta_pic_order_cnt_bottom = |
| + slice_hdr->delta_pic_order_cnt_bottom; |
| + break; |
| + |
| + case 1: |
| + curr_pic_->delta_pic_order_cnt0 = slice_hdr->delta_pic_order_cnt0; |
| + curr_pic_->delta_pic_order_cnt1 = slice_hdr->delta_pic_order_cnt1; |
| + break; |
| + |
| + case 2: |
| + break; |
| + |
| + default: |
| + NOTREACHED(); |
| + return false; |
| + } |
| + |
| + if (!CalculatePicOrderCounts(curr_pic_)) |
| return false; |
| curr_pic_->long_term_reference_flag = slice_hdr->long_term_reference_flag; |
| @@ -134,18 +182,17 @@ bool H264Decoder::InitCurrPicture(media::H264SliceHeader* slice_hdr) { |
| return true; |
| } |
| -bool H264Decoder::CalculatePicOrderCounts(media::H264SliceHeader* slice_hdr) { |
| - DCHECK_NE(curr_sps_id_, -1); |
| +bool H264Decoder::CalculatePicOrderCounts(scoped_refptr<H264Picture> pic) { |
| const media::H264SPS* sps = parser_.GetSPS(curr_sps_id_); |
| + if (!sps) |
| + return false; |
| - int pic_order_cnt_lsb = slice_hdr->pic_order_cnt_lsb; |
| - curr_pic_->pic_order_cnt_lsb = pic_order_cnt_lsb; |
| - |
| - switch (sps->pic_order_cnt_type) { |
| - case 0: |
| + switch (pic->pic_order_cnt_type) { |
| + case 0: { |
|
xhwang
2015/10/12 18:01:17
ditto
|
| // See spec 8.2.1.1. |
| int prev_pic_order_cnt_msb, prev_pic_order_cnt_lsb; |
| - if (slice_hdr->idr_pic_flag) { |
| + |
| + if (pic->idr) { |
| prev_pic_order_cnt_msb = prev_pic_order_cnt_lsb = 0; |
| } else { |
| if (prev_ref_has_memmgmnt5_) { |
| @@ -162,57 +209,57 @@ bool H264Decoder::CalculatePicOrderCounts(media::H264SliceHeader* slice_hdr) { |
| } |
| } |
| - DCHECK_NE(max_pic_order_cnt_lsb_, 0); |
| - if ((pic_order_cnt_lsb < prev_pic_order_cnt_lsb) && |
| - (prev_pic_order_cnt_lsb - pic_order_cnt_lsb >= |
| - max_pic_order_cnt_lsb_ / 2)) { |
| - curr_pic_->pic_order_cnt_msb = prev_pic_order_cnt_msb + |
| - max_pic_order_cnt_lsb_; |
| - } else if ((pic_order_cnt_lsb > prev_pic_order_cnt_lsb) && |
| - (pic_order_cnt_lsb - prev_pic_order_cnt_lsb > |
| - max_pic_order_cnt_lsb_ / 2)) { |
| - curr_pic_->pic_order_cnt_msb = prev_pic_order_cnt_msb - |
| - max_pic_order_cnt_lsb_; |
| + int max_pic_order_cnt_lsb = |
| + 1 << (sps->log2_max_pic_order_cnt_lsb_minus4 + 4); |
| + DCHECK_NE(max_pic_order_cnt_lsb, 0); |
| + if ((pic->pic_order_cnt_lsb < prev_pic_order_cnt_lsb) && |
| + (prev_pic_order_cnt_lsb - pic->pic_order_cnt_lsb >= |
| + max_pic_order_cnt_lsb / 2)) { |
| + pic->pic_order_cnt_msb = prev_pic_order_cnt_msb + max_pic_order_cnt_lsb; |
| + } else if ((pic->pic_order_cnt_lsb > prev_pic_order_cnt_lsb) && |
| + (pic->pic_order_cnt_lsb - prev_pic_order_cnt_lsb > |
| + max_pic_order_cnt_lsb / 2)) { |
| + pic->pic_order_cnt_msb = prev_pic_order_cnt_msb - max_pic_order_cnt_lsb; |
| } else { |
| - curr_pic_->pic_order_cnt_msb = prev_pic_order_cnt_msb; |
| + pic->pic_order_cnt_msb = prev_pic_order_cnt_msb; |
| } |
| - if (curr_pic_->field != H264Picture::FIELD_BOTTOM) { |
| - curr_pic_->top_field_order_cnt = curr_pic_->pic_order_cnt_msb + |
| - pic_order_cnt_lsb; |
| + if (pic->field != H264Picture::FIELD_BOTTOM) { |
| + pic->top_field_order_cnt = |
| + pic->pic_order_cnt_msb + pic->pic_order_cnt_lsb; |
| } |
| - if (curr_pic_->field != H264Picture::FIELD_TOP) { |
| - // TODO posciak: perhaps replace with pic->field? |
| - if (!slice_hdr->field_pic_flag) { |
| - curr_pic_->bottom_field_order_cnt = curr_pic_->top_field_order_cnt + |
| - slice_hdr->delta_pic_order_cnt_bottom; |
| + if (pic->field != H264Picture::FIELD_TOP) { |
| + if (pic->field == H264Picture::FIELD_NONE) { |
| + pic->bottom_field_order_cnt = |
| + pic->top_field_order_cnt + pic->delta_pic_order_cnt_bottom; |
| } else { |
| - curr_pic_->bottom_field_order_cnt = curr_pic_->pic_order_cnt_msb + |
| - pic_order_cnt_lsb; |
| + pic->bottom_field_order_cnt = |
| + pic->pic_order_cnt_msb + pic->pic_order_cnt_lsb; |
| } |
| } |
| break; |
| + } |
| case 1: { |
| // See spec 8.2.1.2. |
| if (prev_has_memmgmnt5_) |
| prev_frame_num_offset_ = 0; |
| - if (slice_hdr->idr_pic_flag) |
| - curr_pic_->frame_num_offset = 0; |
| - else if (prev_frame_num_ > slice_hdr->frame_num) |
| - curr_pic_->frame_num_offset = prev_frame_num_offset_ + max_frame_num_; |
| + if (pic->idr) |
| + pic->frame_num_offset = 0; |
| + else if (prev_frame_num_ > pic->frame_num) |
| + pic->frame_num_offset = prev_frame_num_offset_ + max_frame_num_; |
| else |
| - curr_pic_->frame_num_offset = prev_frame_num_offset_; |
| + pic->frame_num_offset = prev_frame_num_offset_; |
| int abs_frame_num = 0; |
| if (sps->num_ref_frames_in_pic_order_cnt_cycle != 0) |
| - abs_frame_num = curr_pic_->frame_num_offset + slice_hdr->frame_num; |
| + abs_frame_num = pic->frame_num_offset + pic->frame_num; |
| else |
| abs_frame_num = 0; |
| - if (slice_hdr->nal_ref_idc == 0 && abs_frame_num > 0) |
| + if (pic->nal_ref_idc == 0 && abs_frame_num > 0) |
| --abs_frame_num; |
| int expected_pic_order_cnt = 0; |
| @@ -235,91 +282,90 @@ bool H264Decoder::CalculatePicOrderCounts(media::H264SliceHeader* slice_hdr) { |
| expected_pic_order_cnt += sps->offset_for_ref_frame[i]; |
| } |
| - if (!slice_hdr->nal_ref_idc) |
| + if (!pic->nal_ref_idc) |
| expected_pic_order_cnt += sps->offset_for_non_ref_pic; |
| - if (!slice_hdr->field_pic_flag) { |
| - curr_pic_->top_field_order_cnt = expected_pic_order_cnt + |
| - slice_hdr->delta_pic_order_cnt0; |
| - curr_pic_->bottom_field_order_cnt = curr_pic_->top_field_order_cnt + |
| - sps->offset_for_top_to_bottom_field + |
| - slice_hdr->delta_pic_order_cnt1; |
| - } else if (!slice_hdr->bottom_field_flag) { |
| - curr_pic_->top_field_order_cnt = expected_pic_order_cnt + |
| - slice_hdr->delta_pic_order_cnt0; |
| + if (pic->field == H264Picture::FIELD_NONE) { |
| + pic->top_field_order_cnt = |
| + expected_pic_order_cnt + pic->delta_pic_order_cnt0; |
| + pic->bottom_field_order_cnt = pic->top_field_order_cnt + |
| + sps->offset_for_top_to_bottom_field + |
| + pic->delta_pic_order_cnt1; |
| + } else if (pic->field != H264Picture::FIELD_BOTTOM) { |
| + pic->top_field_order_cnt = |
| + expected_pic_order_cnt + pic->delta_pic_order_cnt0; |
| } else { |
| - curr_pic_->bottom_field_order_cnt = expected_pic_order_cnt + |
| - sps->offset_for_top_to_bottom_field + |
| - slice_hdr->delta_pic_order_cnt0; |
| + pic->bottom_field_order_cnt = expected_pic_order_cnt + |
| + sps->offset_for_top_to_bottom_field + |
| + pic->delta_pic_order_cnt0; |
| } |
| break; |
| } |
| - case 2: |
| + case 2: { |
| // See spec 8.2.1.3. |
| if (prev_has_memmgmnt5_) |
| prev_frame_num_offset_ = 0; |
| - if (slice_hdr->idr_pic_flag) |
| - curr_pic_->frame_num_offset = 0; |
| - else if (prev_frame_num_ > slice_hdr->frame_num) |
| - curr_pic_->frame_num_offset = prev_frame_num_offset_ + max_frame_num_; |
| + if (pic->idr) |
| + pic->frame_num_offset = 0; |
| + else if (prev_frame_num_ > pic->frame_num) |
| + pic->frame_num_offset = prev_frame_num_offset_ + max_frame_num_; |
| else |
| - curr_pic_->frame_num_offset = prev_frame_num_offset_; |
| + pic->frame_num_offset = prev_frame_num_offset_; |
| int temp_pic_order_cnt; |
| - if (slice_hdr->idr_pic_flag) { |
| + if (pic->idr) { |
| temp_pic_order_cnt = 0; |
| - } else if (!slice_hdr->nal_ref_idc) { |
| - temp_pic_order_cnt = |
| - 2 * (curr_pic_->frame_num_offset + slice_hdr->frame_num) - 1; |
| + } else if (!pic->nal_ref_idc) { |
| + temp_pic_order_cnt = 2 * (pic->frame_num_offset + pic->frame_num) - 1; |
| } else { |
| - temp_pic_order_cnt = 2 * (curr_pic_->frame_num_offset + |
| - slice_hdr->frame_num); |
| + temp_pic_order_cnt = 2 * (pic->frame_num_offset + pic->frame_num); |
| } |
| - if (!slice_hdr->field_pic_flag) { |
| - curr_pic_->top_field_order_cnt = temp_pic_order_cnt; |
| - curr_pic_->bottom_field_order_cnt = temp_pic_order_cnt; |
| - } else if (slice_hdr->bottom_field_flag) { |
| - curr_pic_->bottom_field_order_cnt = temp_pic_order_cnt; |
| + if (pic->field == H264Picture::FIELD_NONE) { |
| + pic->top_field_order_cnt = temp_pic_order_cnt; |
| + pic->bottom_field_order_cnt = temp_pic_order_cnt; |
| + } else if (pic->field == H264Picture::FIELD_BOTTOM) { |
| + pic->bottom_field_order_cnt = temp_pic_order_cnt; |
| } else { |
| - curr_pic_->top_field_order_cnt = temp_pic_order_cnt; |
| + pic->top_field_order_cnt = temp_pic_order_cnt; |
| } |
| break; |
| + } |
| default: |
| DVLOG(1) << "Invalid pic_order_cnt_type: " << sps->pic_order_cnt_type; |
| return false; |
| } |
| - switch (curr_pic_->field) { |
| + switch (pic->field) { |
| case H264Picture::FIELD_NONE: |
| - curr_pic_->pic_order_cnt = std::min(curr_pic_->top_field_order_cnt, |
| - curr_pic_->bottom_field_order_cnt); |
| + pic->pic_order_cnt = |
| + std::min(pic->top_field_order_cnt, pic->bottom_field_order_cnt); |
| break; |
| case H264Picture::FIELD_TOP: |
| - curr_pic_->pic_order_cnt = curr_pic_->top_field_order_cnt; |
| + pic->pic_order_cnt = pic->top_field_order_cnt; |
| break; |
| case H264Picture::FIELD_BOTTOM: |
| - curr_pic_->pic_order_cnt = curr_pic_->bottom_field_order_cnt; |
| + pic->pic_order_cnt = pic->bottom_field_order_cnt; |
| break; |
| } |
| return true; |
| } |
| -void H264Decoder::UpdatePicNums() { |
| +void H264Decoder::UpdatePicNums(int frame_num) { |
| for (auto& pic : dpb_) { |
| if (!pic->ref) |
| continue; |
| - // Below assumes non-interlaced stream. |
| + // 8.2.4.1. Assumes non-interlaced stream. |
| DCHECK_EQ(pic->field, H264Picture::FIELD_NONE); |
| if (pic->long_term) { |
| pic->long_term_pic_num = pic->long_term_frame_idx; |
| } else { |
| - if (pic->frame_num > frame_num_) |
| + if (pic->frame_num > frame_num) |
| pic->frame_num_wrap = pic->frame_num - max_frame_num_; |
| else |
| pic->frame_num_wrap = pic->frame_num; |
| @@ -344,7 +390,7 @@ struct LongTermPicNumAscCompare { |
| }; |
| void H264Decoder::ConstructReferencePicListsP( |
| - media::H264SliceHeader* slice_hdr) { |
| + const media::H264SliceHeader* slice_hdr) { |
| // RefPicList0 (8.2.4.2.1) [[1] [2]], where: |
| // [1] shortterm ref pics sorted by descending pic_num, |
| // [2] longterm ref pics by ascending long_term_pic_num. |
| @@ -379,7 +425,7 @@ struct POCDescCompare { |
| }; |
| void H264Decoder::ConstructReferencePicListsB( |
| - media::H264SliceHeader* slice_hdr) { |
| + const media::H264SliceHeader* slice_hdr) { |
| // RefPicList0 (8.2.4.2.3) [[1] [2] [3]], where: |
| // [1] shortterm ref pics with POC < curr_pic's POC sorted by descending POC, |
| // [2] shortterm ref pics with POC > curr_pic's POC by ascending POC, |
| @@ -477,12 +523,13 @@ static void ShiftRightAndInsert(H264Picture::Vector* v, |
| (*v)[from] = pic; |
| } |
| -bool H264Decoder::ModifyReferencePicList(media::H264SliceHeader* slice_hdr, |
| - int list, |
| - H264Picture::Vector* ref_pic_listx) { |
| +bool H264Decoder::ModifyReferencePicList( |
| + const media::H264SliceHeader* slice_hdr, |
| + int list, |
| + H264Picture::Vector* ref_pic_listx) { |
| bool ref_pic_list_modification_flag_lX; |
| int num_ref_idx_lX_active_minus1; |
| - media::H264ModificationOfPicNum* list_mod; |
| + const media::H264ModificationOfPicNum* list_mod; |
| // This can process either ref_pic_list0 or ref_pic_list1, depending on |
| // the list argument. Set up pointers to proper list to be processed here. |
| @@ -618,8 +665,14 @@ void H264Decoder::OutputPic(scoped_refptr<H264Picture> pic) { |
| DCHECK(!pic->outputted); |
| pic->outputted = true; |
| + if (pic->nonexisting) { |
| + DVLOG(4) << "Skipping output, non-existing frame_num: " << pic->frame_num; |
| + return; |
| + } |
| + |
| DVLOG_IF(1, pic->pic_order_cnt < last_output_poc_) |
| - << "Outputting out of order, likely a broken stream"; |
| + << "Outputting out of order, likely a broken stream: " |
| + << last_output_poc_ << " -> " << pic->pic_order_cnt; |
| last_output_poc_ = pic->pic_order_cnt; |
| DVLOG(4) << "Posting output task for POC: " << pic->pic_order_cnt; |
| @@ -657,23 +710,38 @@ bool H264Decoder::Flush() { |
| return true; |
| } |
| -bool H264Decoder::StartNewFrame(media::H264SliceHeader* slice_hdr) { |
| +bool H264Decoder::StartNewFrame(const media::H264SliceHeader* slice_hdr) { |
| // TODO posciak: add handling of max_num_ref_frames per spec. |
| CHECK(curr_pic_.get()); |
| + DCHECK(slice_hdr); |
| - if (!InitCurrPicture(slice_hdr)) |
| + curr_pps_id_ = slice_hdr->pic_parameter_set_id; |
| + const media::H264PPS* pps = parser_.GetPPS(curr_pps_id_); |
| + if (!pps) |
| return false; |
| - DCHECK_GT(max_frame_num_, 0); |
| + curr_sps_id_ = pps->seq_parameter_set_id; |
| + const media::H264SPS* sps = parser_.GetSPS(curr_sps_id_); |
| + if (!sps) |
| + return false; |
| - UpdatePicNums(); |
| - DCHECK(slice_hdr); |
| - PrepareRefPicLists(slice_hdr); |
| + max_frame_num_ = 1 << (sps->log2_max_frame_num_minus4 + 4); |
| + int frame_num = slice_hdr->frame_num; |
| + if (slice_hdr->idr_pic_flag) |
| + prev_ref_frame_num_ = 0; |
| + |
| + // 7.4.3 |
| + if (frame_num != prev_ref_frame_num_ && |
| + frame_num != (prev_ref_frame_num_ + 1) % max_frame_num_) { |
| + if (!HandleFrameNumGap(frame_num)) |
| + return false; |
| + } |
| - const media::H264PPS* pps = parser_.GetPPS(curr_pps_id_); |
| - DCHECK(pps); |
| - const media::H264SPS* sps = parser_.GetSPS(pps->seq_parameter_set_id); |
| - DCHECK(sps); |
| + if (!InitCurrPicture(slice_hdr)) |
| + return false; |
| + |
| + UpdatePicNums(frame_num); |
| + PrepareRefPicLists(slice_hdr); |
| if (!accelerator_->SubmitFrameMetadata(sps, pps, dpb_, ref_pic_list_p0_, |
| ref_pic_list_b0_, ref_pic_list_b1_, |
| @@ -683,12 +751,11 @@ bool H264Decoder::StartNewFrame(media::H264SliceHeader* slice_hdr) { |
| return true; |
| } |
| -bool H264Decoder::HandleMemoryManagementOps() { |
| +bool H264Decoder::HandleMemoryManagementOps(scoped_refptr<H264Picture> pic) { |
| // 8.2.5.4 |
| - for (unsigned int i = 0; i < arraysize(curr_pic_->ref_pic_marking); ++i) { |
| + for (size_t i = 0; i < arraysize(pic->ref_pic_marking); ++i) { |
| // Code below does not support interlaced stream (per-field pictures). |
| - media::H264DecRefPicMarking* ref_pic_marking = |
| - &curr_pic_->ref_pic_marking[i]; |
| + media::H264DecRefPicMarking* ref_pic_marking = &pic->ref_pic_marking[i]; |
| scoped_refptr<H264Picture> to_mark; |
| int pic_num_x; |
| @@ -700,8 +767,8 @@ bool H264Decoder::HandleMemoryManagementOps() { |
| case 1: |
| // Mark a short term reference picture as unused so it can be removed |
| // if outputted. |
| - pic_num_x = curr_pic_->pic_num - |
| - (ref_pic_marking->difference_of_pic_nums_minus1 + 1); |
| + pic_num_x = |
| + pic->pic_num - (ref_pic_marking->difference_of_pic_nums_minus1 + 1); |
| to_mark = dpb_.GetShortRefPicByPicNum(pic_num_x); |
| if (to_mark) { |
| to_mark->ref = false; |
| @@ -726,8 +793,8 @@ bool H264Decoder::HandleMemoryManagementOps() { |
| case 3: |
| // Mark a short term reference picture as long term reference. |
| - pic_num_x = curr_pic_->pic_num - |
| - (ref_pic_marking->difference_of_pic_nums_minus1 + 1); |
| + pic_num_x = |
| + pic->pic_num - (ref_pic_marking->difference_of_pic_nums_minus1 + 1); |
| to_mark = dpb_.GetShortRefPicByPicNum(pic_num_x); |
| if (to_mark) { |
| DCHECK(to_mark->ref && !to_mark->long_term); |
| @@ -746,12 +813,12 @@ bool H264Decoder::HandleMemoryManagementOps() { |
| H264Picture::Vector long_terms; |
| dpb_.GetLongTermRefPicsAppending(&long_terms); |
| for (size_t i = 0; i < long_terms.size(); ++i) { |
| - scoped_refptr<H264Picture>& pic = long_terms[i]; |
| - DCHECK(pic->ref && pic->long_term); |
| + scoped_refptr<H264Picture>& long_term_pic = long_terms[i]; |
| + DCHECK(long_term_pic->ref && long_term_pic->long_term); |
| // Ok to cast, max_long_term_frame_idx is much smaller than 16bit. |
| - if (pic->long_term_frame_idx > |
| + if (long_term_pic->long_term_frame_idx > |
| static_cast<int>(max_long_term_frame_idx_)) |
| - pic->ref = false; |
| + long_term_pic->ref = false; |
| } |
| break; |
| } |
| @@ -760,7 +827,7 @@ bool H264Decoder::HandleMemoryManagementOps() { |
| // Unmark all reference pictures. |
| dpb_.MarkAllUnusedForRef(); |
| max_long_term_frame_idx_ = -1; |
| - curr_pic_->mem_mgmt_5 = true; |
| + pic->mem_mgmt_5 = true; |
| break; |
| case 6: { |
| @@ -769,18 +836,18 @@ bool H264Decoder::HandleMemoryManagementOps() { |
| H264Picture::Vector long_terms; |
| dpb_.GetLongTermRefPicsAppending(&long_terms); |
| for (size_t i = 0; i < long_terms.size(); ++i) { |
| - scoped_refptr<H264Picture>& pic = long_terms[i]; |
| - DCHECK(pic->ref && pic->long_term); |
| + scoped_refptr<H264Picture>& long_term_pic = long_terms[i]; |
| + DCHECK(long_term_pic->ref && long_term_pic->long_term); |
| // Ok to cast, long_term_frame_idx is much smaller than 16bit. |
| - if (pic->long_term_frame_idx == |
| + if (long_term_pic->long_term_frame_idx == |
| static_cast<int>(ref_pic_marking->long_term_frame_idx)) |
| - pic->ref = false; |
| + long_term_pic->ref = false; |
| } |
| // and mark the current one instead. |
| - curr_pic_->ref = true; |
| - curr_pic_->long_term = true; |
| - curr_pic_->long_term_frame_idx = ref_pic_marking->long_term_frame_idx; |
| + pic->ref = true; |
| + pic->long_term = true; |
| + pic->long_term_frame_idx = ref_pic_marking->long_term_frame_idx; |
| break; |
| } |
| @@ -798,91 +865,86 @@ bool H264Decoder::HandleMemoryManagementOps() { |
| // procedure to remove the oldest one. |
| // It also performs marking and unmarking pictures as reference. |
| // See spac 8.2.5.1. |
| -void H264Decoder::ReferencePictureMarking() { |
| - if (curr_pic_->idr) { |
| - // If current picture is an IDR, all reference pictures are unmarked. |
| +bool H264Decoder::ReferencePictureMarking(scoped_refptr<H264Picture> pic) { |
| + // If the current picture is an IDR, all reference pictures are unmarked. |
| + if (pic->idr) { |
| dpb_.MarkAllUnusedForRef(); |
| - if (curr_pic_->long_term_reference_flag) { |
| - curr_pic_->long_term = true; |
| - curr_pic_->long_term_frame_idx = 0; |
| + if (pic->long_term_reference_flag) { |
| + pic->long_term = true; |
| + pic->long_term_frame_idx = 0; |
| max_long_term_frame_idx_ = 0; |
| } else { |
| - curr_pic_->long_term = false; |
| + pic->long_term = false; |
| max_long_term_frame_idx_ = -1; |
| } |
| + |
| + return true; |
| + } |
| + |
| + // Not an IDR. If the stream contains instructions on how to discard pictures |
| + // from DPB and how to mark/unmark existing reference pictures, do so. |
| + // Otherwise, fall back to default sliding window process. |
| + if (pic->adaptive_ref_pic_marking_mode_flag) { |
| + DCHECK(!pic->nonexisting); |
| + return HandleMemoryManagementOps(pic); |
| } else { |
| - if (!curr_pic_->adaptive_ref_pic_marking_mode_flag) { |
| - // If non-IDR, and the stream does not indicate what we should do to |
| - // ensure DPB doesn't overflow, discard oldest picture. |
| - // See spec 8.2.5.3. |
| - if (curr_pic_->field == H264Picture::FIELD_NONE) { |
| - DCHECK_LE( |
| - dpb_.CountRefPics(), |
| - std::max<int>(parser_.GetSPS(curr_sps_id_)->max_num_ref_frames, 1)); |
| - if (dpb_.CountRefPics() == |
| - std::max<int>(parser_.GetSPS(curr_sps_id_)->max_num_ref_frames, |
| - 1)) { |
| - // Max number of reference pics reached, |
| - // need to remove one of the short term ones. |
| - // Find smallest frame_num_wrap short reference picture and mark |
| - // it as unused. |
| - scoped_refptr<H264Picture> to_unmark = |
| - dpb_.GetLowestFrameNumWrapShortRefPic(); |
| - if (to_unmark == NULL) { |
| - DVLOG(1) << "Couldn't find a short ref picture to unmark"; |
| - return; |
| - } |
| - to_unmark->ref = false; |
| - } |
| - } else { |
| - // Shouldn't get here. |
| - DVLOG(1) << "Interlaced video not supported."; |
| - } |
| - } else { |
| - // Stream has instructions how to discard pictures from DPB and how |
| - // to mark/unmark existing reference pictures. Do it. |
| - // Spec 8.2.5.4. |
| - if (curr_pic_->field == H264Picture::FIELD_NONE) { |
| - HandleMemoryManagementOps(); |
| - } else { |
| - // Shouldn't get here. |
| - DVLOG(1) << "Interlaced video not supported."; |
| - } |
| - } |
| + return SlidingWindowPictureMarking(); |
| } |
| } |
| -bool H264Decoder::FinishPicture() { |
| - DCHECK(curr_pic_.get()); |
| +bool H264Decoder::SlidingWindowPictureMarking() { |
| + const media::H264SPS* sps = parser_.GetSPS(curr_sps_id_); |
| + if (!sps) |
| + return false; |
| - // Finish processing previous picture. |
| - // Start by storing previous reference picture data for later use, |
| - // if picture being finished is a reference picture. |
| - if (curr_pic_->ref) { |
| - ReferencePictureMarking(); |
| - prev_ref_has_memmgmnt5_ = curr_pic_->mem_mgmt_5; |
| - prev_ref_top_field_order_cnt_ = curr_pic_->top_field_order_cnt; |
| - prev_ref_pic_order_cnt_msb_ = curr_pic_->pic_order_cnt_msb; |
| - prev_ref_pic_order_cnt_lsb_ = curr_pic_->pic_order_cnt_lsb; |
| - prev_ref_field_ = curr_pic_->field; |
| + // 8.2.5.3. Ensure the DPB doesn't overflow by discarding the oldest picture. |
| + int num_ref_pics = dpb_.CountRefPics(); |
| + DCHECK_LE(num_ref_pics, std::max<int>(sps->max_num_ref_frames, 1)); |
| + if (num_ref_pics == std::max<int>(sps->max_num_ref_frames, 1)) { |
| + // Max number of reference pics reached, need to remove one of the short |
| + // term ones. Find smallest frame_num_wrap short reference picture and mark |
| + // it as unused. |
| + scoped_refptr<H264Picture> to_unmark = |
| + dpb_.GetLowestFrameNumWrapShortRefPic(); |
| + if (!to_unmark) { |
| + DVLOG(1) << "Couldn't find a short ref picture to unmark"; |
| + return false; |
| + } |
| + |
| + to_unmark->ref = false; |
| + } |
| + |
| + return true; |
| +} |
| + |
| +bool H264Decoder::FinishPicture(scoped_refptr<H264Picture> pic) { |
| + // Finish processing the picture. |
| + // Start by storing previous picture data for later use. |
| + if (pic->ref) { |
| + ReferencePictureMarking(pic); |
| + prev_ref_has_memmgmnt5_ = pic->mem_mgmt_5; |
| + prev_ref_top_field_order_cnt_ = pic->top_field_order_cnt; |
| + prev_ref_pic_order_cnt_msb_ = pic->pic_order_cnt_msb; |
| + prev_ref_pic_order_cnt_lsb_ = pic->pic_order_cnt_lsb; |
| + prev_ref_field_ = pic->field; |
| + prev_ref_frame_num_ = pic->frame_num; |
| } |
| - prev_has_memmgmnt5_ = curr_pic_->mem_mgmt_5; |
| - prev_frame_num_offset_ = curr_pic_->frame_num_offset; |
| + prev_frame_num_ = pic->frame_num; |
| + prev_has_memmgmnt5_ = pic->mem_mgmt_5; |
| + prev_frame_num_offset_ = pic->frame_num_offset; |
| // Remove unused (for reference or later output) pictures from DPB, marking |
| // them as such. |
| dpb_.DeleteUnused(); |
| - DVLOG(4) << "Finishing picture, entries in DPB: " << dpb_.size(); |
| + DVLOG(4) << "Finishing picture frame_num: " << pic->frame_num |
| + << ", entries in DPB: " << dpb_.size(); |
| - // Whatever happens below, curr_pic_ will stop managing the pointer to the |
| - // picture after this. The ownership will either be transferred to DPB, if |
| - // the image is still needed (for output and/or reference), or the memory |
| - // will be released if we manage to output it here without having to store |
| - // it for future reference. |
| - scoped_refptr<H264Picture> pic = curr_pic_; |
| - curr_pic_ = nullptr; |
| + // The ownership of pic will either be transferred to DPB - if the picture is |
| + // still needed (for output and/or reference) - or we will release it |
| + // immediately if we manage to output it here and won't have to store it for |
| + // future reference. |
| // Get all pictures that haven't been outputted yet. |
| H264Picture::Vector not_outputted; |
| @@ -1004,9 +1066,11 @@ bool H264Decoder::UpdateMaxNumReorderFrames(const media::H264SPS* sps) { |
| } |
| bool H264Decoder::ProcessSPS(int sps_id, bool* need_new_buffers) { |
| + DVLOG(4) << "Processing SPS id:" << sps_id; |
| + |
| const media::H264SPS* sps = parser_.GetSPS(sps_id); |
| - DCHECK(sps); |
| - DVLOG(4) << "Processing SPS"; |
| + if (!sps) |
| + return false; |
|
xhwang
2015/10/12 18:01:17
In what case could this fail? (wondering why we re
Pawel Osciak
2015/10/13 01:23:22
Previously we were using std::map::operator[], whi
|
| *need_new_buffers = false; |
| @@ -1015,13 +1079,6 @@ bool H264Decoder::ProcessSPS(int sps_id, bool* need_new_buffers) { |
| return false; |
| } |
| - if (sps->gaps_in_frame_num_value_allowed_flag) { |
| - DVLOG(1) << "Gaps in frame numbers not supported"; |
| - return false; |
| - } |
| - |
| - curr_sps_id_ = sps->seq_parameter_set_id; |
| - |
| // Calculate picture height/width in macroblocks and pixels |
| // (spec 7.4.2.1.1, 7.4.3). |
| int width_mb = sps->pic_width_in_mbs_minus1 + 1; |
| @@ -1043,9 +1100,6 @@ bool H264Decoder::ProcessSPS(int sps_id, bool* need_new_buffers) { |
| pic_size_ = new_pic_size; |
| DVLOG(1) << "New picture size: " << pic_size_.ToString(); |
| - max_pic_order_cnt_lsb_ = 1 << (sps->log2_max_pic_order_cnt_lsb_minus4 + 4); |
| - max_frame_num_ = 1 << (sps->log2_max_frame_num_minus4 + 4); |
| - |
| int level = sps->level_idc; |
| int max_dpb_mbs = LevelToMaxDpbMbs(level); |
| if (max_dpb_mbs == 0) |
| @@ -1069,81 +1123,143 @@ bool H264Decoder::ProcessSPS(int sps_id, bool* need_new_buffers) { |
| return true; |
| } |
| -bool H264Decoder::ProcessPPS(int pps_id) { |
| - const media::H264PPS* pps = parser_.GetPPS(pps_id); |
| - DCHECK(pps); |
| - |
| - curr_pps_id_ = pps->pic_parameter_set_id; |
| - |
| - return true; |
| -} |
| - |
| bool H264Decoder::FinishPrevFrameIfPresent() { |
| // If we already have a frame waiting to be decoded, decode it and finish. |
| if (curr_pic_ != NULL) { |
| if (!DecodePicture()) |
| return false; |
| - return FinishPicture(); |
| + |
| + scoped_refptr<H264Picture> pic = curr_pic_; |
| + curr_pic_ = nullptr; |
| + return FinishPicture(pic); |
| } |
| return true; |
| } |
| -bool H264Decoder::PreprocessSlice(media::H264SliceHeader* slice_hdr) { |
| - prev_frame_num_ = frame_num_; |
| - frame_num_ = slice_hdr->frame_num; |
| +bool H264Decoder::HandleFrameNumGap(int frame_num) { |
| + const media::H264SPS* sps = parser_.GetSPS(curr_sps_id_); |
| + if (!sps) |
| + return false; |
| - if (prev_frame_num_ > 0 && prev_frame_num_ < frame_num_ - 1) { |
| - DVLOG(1) << "Gap in frame_num!"; |
| + if (!sps->gaps_in_frame_num_value_allowed_flag) { |
| + DVLOG(1) << "Invalid frame_num: " << frame_num; |
| return false; |
| } |
| - if (slice_hdr->field_pic_flag == 0) |
| - max_pic_num_ = max_frame_num_; |
| - else |
| - max_pic_num_ = 2 * max_frame_num_; |
| + DVLOG(2) << "Handling frame_num gap: " << prev_ref_frame_num_ << "->" |
| + << frame_num; |
| + |
| + // 7.4.3/7-23 |
| + int unused_short_term_frame_num = (prev_ref_frame_num_ + 1) % max_frame_num_; |
| + while (unused_short_term_frame_num != frame_num) { |
| + scoped_refptr<H264Picture> pic = new H264Picture(); |
| + if (!InitNonexistingPicture(pic, unused_short_term_frame_num)) |
|
xhwang
2015/10/12 18:01:17
Can we follow the RAII (https://en.wikipedia.org/w
Pawel Osciak
2015/10/13 01:23:22
My intention with H264Picture was to keep it as PO
|
| + return false; |
| + |
| + UpdatePicNums(unused_short_term_frame_num); |
| + |
| + if (!FinishPicture(pic)) |
| + return false; |
| + |
| + unused_short_term_frame_num++; |
| + unused_short_term_frame_num %= max_frame_num_; |
| + } |
| + |
| + return true; |
| +} |
| - // TODO posciak: switch to new picture detection per 7.4.1.2.4. |
| - if (curr_pic_ != NULL && slice_hdr->first_mb_in_slice != 0) { |
| - // More slice data of the current picture. |
| +bool H264Decoder::IsNewPrimaryCodedPicture( |
| + const media::H264SliceHeader* slice_hdr) const { |
| + if (!curr_pic_) |
| return true; |
| - } else { |
| - // A new frame, so first finish the previous one before processing it... |
| + |
| + // 7.4.1.2.4, assumes non-interlaced. |
| + if (slice_hdr->frame_num != curr_pic_->frame_num || |
| + slice_hdr->pic_parameter_set_id != curr_pps_id_ || |
| + slice_hdr->nal_ref_idc != curr_pic_->nal_ref_idc || |
| + slice_hdr->idr_pic_flag != curr_pic_->idr || |
| + (slice_hdr->idr_pic_flag && |
| + slice_hdr->idr_pic_id != curr_pic_->idr_pic_id)) |
| + return true; |
| + |
| + const media::H264SPS* sps = parser_.GetSPS(curr_sps_id_); |
| + if (!sps) |
| + return false; |
| + |
| + if (sps->pic_order_cnt_type == curr_pic_->pic_order_cnt_type) { |
| + if (curr_pic_->pic_order_cnt_type == 0) { |
| + if (slice_hdr->pic_order_cnt_lsb != curr_pic_->pic_order_cnt_lsb || |
| + slice_hdr->delta_pic_order_cnt_bottom != |
| + curr_pic_->delta_pic_order_cnt_bottom) |
| + return true; |
| + } else if (curr_pic_->pic_order_cnt_type == 1) { |
| + if (slice_hdr->delta_pic_order_cnt0 != curr_pic_->delta_pic_order_cnt0 || |
| + slice_hdr->delta_pic_order_cnt1 != curr_pic_->delta_pic_order_cnt1) |
| + return true; |
| + } |
| + } |
| + |
| + return false; |
| +} |
| + |
| +bool H264Decoder::PreprocessCurrentSlice() { |
| + const media::H264SliceHeader* slice_hdr = curr_slice_hdr_.get(); |
| + DCHECK(slice_hdr); |
| + |
| + if (IsNewPrimaryCodedPicture(slice_hdr)) { |
| + // New picture, so first finish the previous one before processing it. |
| if (!FinishPrevFrameIfPresent()) |
| return false; |
| - } |
| - // If the new frame is an IDR, output what's left to output and clear DPB |
| - if (slice_hdr->idr_pic_flag) { |
| - // (unless we are explicitly instructed not to do so). |
| - if (!slice_hdr->no_output_of_prior_pics_flag) { |
| - // Output DPB contents. |
| - if (!Flush()) |
| - return false; |
| + DCHECK(!curr_pic_); |
| + |
| + if (slice_hdr->first_mb_in_slice != 0) { |
| + DVLOG(1) << "ASO/invalid stream, first_mb_in_slice: " |
| + << slice_hdr->first_mb_in_slice; |
| + return false; |
| + } |
| + |
| + // If the new picture is an IDR, flush DPB. |
| + if (slice_hdr->idr_pic_flag) { |
| + // Output all remaining pictures, unless we are explicitly instructed |
| + // not to do so. |
| + if (!slice_hdr->no_output_of_prior_pics_flag) { |
| + if (!Flush()) |
| + return false; |
| + } |
| + dpb_.Clear(); |
| + last_output_poc_ = std::numeric_limits<int>::min(); |
| } |
| - dpb_.Clear(); |
| - last_output_poc_ = std::numeric_limits<int>::min(); |
| } |
| return true; |
| } |
| -bool H264Decoder::ProcessSlice(media::H264SliceHeader* slice_hdr) { |
| - DCHECK(curr_pic_.get()); |
| - H264Picture::Vector ref_pic_list0, ref_pic_list1; |
| +bool H264Decoder::ProcessCurrentSlice() { |
| + DCHECK(curr_pic_); |
| + const media::H264SliceHeader* slice_hdr = curr_slice_hdr_.get(); |
| + DCHECK(slice_hdr); |
| + |
| + if (slice_hdr->field_pic_flag == 0) |
| + max_pic_num_ = max_frame_num_; |
| + else |
| + max_pic_num_ = 2 * max_frame_num_; |
| + |
| + H264Picture::Vector ref_pic_list0, ref_pic_list1; |
| if (!ModifyReferencePicLists(slice_hdr, &ref_pic_list0, &ref_pic_list1)) |
| return false; |
| - const media::H264PPS* pps = parser_.GetPPS(slice_hdr->pic_parameter_set_id); |
| - DCHECK(pps); |
| + const media::H264PPS* pps = parser_.GetPPS(curr_pps_id_); |
| + if (!pps) |
| + return false; |
| if (!accelerator_->SubmitSlice(pps, slice_hdr, ref_pic_list0, ref_pic_list1, |
| curr_pic_.get(), slice_hdr->nalu_data, |
| slice_hdr->nalu_size)) |
| return false; |
| - curr_slice_hdr_.reset(); |
| return true; |
| } |
| @@ -1163,7 +1279,10 @@ void H264Decoder::SetStream(const uint8_t* ptr, size_t size) { |
| } |
| H264Decoder::DecodeResult H264Decoder::Decode() { |
| - DCHECK_NE(state_, kError); |
| + if (state_ == kError) { |
| + DVLOG(1) << "Decoder in error state"; |
| + return kDecodeError; |
| + } |
| while (1) { |
| media::H264Parser::Result par_res; |
| @@ -1175,9 +1294,9 @@ H264Decoder::DecodeResult H264Decoder::Decode() { |
| return kRanOutOfStreamData; |
| else if (par_res != media::H264Parser::kOk) |
| SET_ERROR_AND_RETURN(); |
| - } |
| - DVLOG(4) << "NALU found: " << static_cast<int>(curr_nalu_->nal_unit_type); |
| + DVLOG(4) << "New NALU: " << static_cast<int>(curr_nalu_->nal_unit_type); |
| + } |
| switch (curr_nalu_->nal_unit_type) { |
| case media::H264NALU::kNonIDRSlice: |
| @@ -1195,6 +1314,8 @@ H264Decoder::DecodeResult H264Decoder::Decode() { |
| } |
| // If after reset, we should be able to recover from an IDR. |
| + state_ = kDecoding; |
| + |
| if (!curr_slice_hdr_) { |
| curr_slice_hdr_.reset(new media::H264SliceHeader()); |
| par_res = |
| @@ -1202,7 +1323,7 @@ H264Decoder::DecodeResult H264Decoder::Decode() { |
| if (par_res != media::H264Parser::kOk) |
| SET_ERROR_AND_RETURN(); |
| - if (!PreprocessSlice(curr_slice_hdr_.get())) |
| + if (!PreprocessCurrentSlice()) |
| SET_ERROR_AND_RETURN(); |
| } |
| @@ -1217,10 +1338,10 @@ H264Decoder::DecodeResult H264Decoder::Decode() { |
| SET_ERROR_AND_RETURN(); |
| } |
| - if (!ProcessSlice(curr_slice_hdr_.get())) |
| + if (!ProcessCurrentSlice()) |
| SET_ERROR_AND_RETURN(); |
| - state_ = kDecoding; |
| + curr_slice_hdr_.reset(); |
| break; |
| } |
| @@ -1268,8 +1389,6 @@ H264Decoder::DecodeResult H264Decoder::Decode() { |
| if (par_res != media::H264Parser::kOk) |
| SET_ERROR_AND_RETURN(); |
| - if (!ProcessPPS(pps_id)) |
| - SET_ERROR_AND_RETURN(); |
| break; |
| } |
| @@ -1278,7 +1397,7 @@ H264Decoder::DecodeResult H264Decoder::Decode() { |
| break; |
| } |
| - DVLOG(4) << "Dropping nalu"; |
| + DVLOG(4) << "NALU done"; |
| curr_nalu_.reset(); |
| } |
| } |