diff --git a/libavcodec/h264.c b/libavcodec/h264.c index f1cac2cac1..d2bbe465eb 100644 --- a/libavcodec/h264.c +++ b/libavcodec/h264.c @@ -2904,7 +2904,7 @@ static int decode_slice_header(H264Context *h, H264Context *h0) s->current_picture_ptr->frame_num = h->prev_frame_num; ff_thread_report_progress(&s->current_picture_ptr->f, INT_MAX, 0); ff_thread_report_progress(&s->current_picture_ptr->f, INT_MAX, 1); - ff_generate_sliding_window_mmcos(h); + ff_generate_sliding_window_mmcos(h, 1); if (ff_h264_execute_ref_pic_marking(h, h->mmco, h->mmco_index) < 0 && (s->avctx->err_recognition & AV_EF_EXPLODE)) return AVERROR_INVALIDDATA; @@ -3082,7 +3082,15 @@ static int decode_slice_header(H264Context *h, H264Context *h0) } } - if (h->nal_ref_idc && ff_h264_decode_ref_pic_marking(h0, &s->gb) < 0 && + // If frame-mt is enabled, only update mmco tables for the first slice + // in a field. Subsequent slices can temporarily clobber h->mmco_index + // or h->mmco, which will cause ref list mix-ups and decoding errors + // further down the line. This may break decoding if the first slice is + // corrupt, thus we only do this if frame-mt is enabled. + if (h->nal_ref_idc && + ff_h264_decode_ref_pic_marking(h0, &s->gb, + !(s->avctx->active_thread_type & FF_THREAD_FRAME) || + h0->current_slice == 0) < 0 && (s->avctx->err_recognition & AV_EF_EXPLODE)) return AVERROR_INVALIDDATA; diff --git a/libavcodec/h264.h b/libavcodec/h264.h index 8596121aab..ad4732e1df 100644 --- a/libavcodec/h264.h +++ b/libavcodec/h264.h @@ -645,9 +645,10 @@ void ff_h264_remove_all_refs(H264Context *h); */ int ff_h264_execute_ref_pic_marking(H264Context *h, MMCO *mmco, int mmco_count); -int ff_h264_decode_ref_pic_marking(H264Context *h, GetBitContext *gb); +int ff_h264_decode_ref_pic_marking(H264Context *h, GetBitContext *gb, + int first_slice); -void ff_generate_sliding_window_mmcos(H264Context *h); +void ff_generate_sliding_window_mmcos(H264Context *h, int first_slice); /** * Check if the top & left blocks are available if needed & change the diff --git a/libavcodec/h264_refs.c b/libavcodec/h264_refs.c index 2a71ac1f06..62c2a5758e 100644 --- a/libavcodec/h264_refs.c +++ b/libavcodec/h264_refs.c @@ -476,23 +476,51 @@ static void print_long_term(H264Context *h) { } } -void ff_generate_sliding_window_mmcos(H264Context *h) { +static int check_opcodes(MMCO *mmco1, MMCO *mmco2, int n_mmcos) +{ + int i; + + for (i = 0; i < n_mmcos; i++) { + if (mmco1[i].opcode != mmco2[i].opcode) + return -1; + } + + return 0; +} + +void ff_generate_sliding_window_mmcos(H264Context *h, int first_slice) +{ MpegEncContext * const s = &h->s; + MMCO mmco_temp[MAX_MMCO_COUNT], *mmco = first_slice ? h->mmco : mmco_temp; + int mmco_index = 0, i; + assert(h->long_ref_count + h->short_ref_count <= h->sps.ref_frame_count); - h->mmco_index= 0; - if(h->short_ref_count && h->long_ref_count + h->short_ref_count == h->sps.ref_frame_count && - !(FIELD_PICTURE && !s->first_field && s->current_picture_ptr->f.reference)) { - h->mmco[0].opcode= MMCO_SHORT2UNUSED; - h->mmco[0].short_pic_num= h->short_ref[ h->short_ref_count - 1 ]->frame_num; - h->mmco_index= 1; + if (h->short_ref_count && + h->long_ref_count + h->short_ref_count == h->sps.ref_frame_count && + !(FIELD_PICTURE && !s->first_field && + s->current_picture_ptr->f.reference)) { + mmco[0].opcode = MMCO_SHORT2UNUSED; + mmco[0].short_pic_num = h->short_ref[h->short_ref_count - 1]->frame_num; + mmco_index = 1; if (FIELD_PICTURE) { - h->mmco[0].short_pic_num *= 2; - h->mmco[1].opcode= MMCO_SHORT2UNUSED; - h->mmco[1].short_pic_num= h->mmco[0].short_pic_num + 1; - h->mmco_index= 2; + mmco[0].short_pic_num *= 2; + mmco[1].opcode = MMCO_SHORT2UNUSED; + mmco[1].short_pic_num = mmco[0].short_pic_num + 1; + mmco_index = 2; } } + + if (first_slice) { + h->mmco_index = mmco_index; + } else if (!first_slice && mmco_index >= 0 && + (mmco_index != h->mmco_index || + (i = check_opcodes(h->mmco, mmco_temp, mmco_index)))) { + av_log(h->s.avctx, AV_LOG_ERROR, + "Inconsistent MMCO state between slices [%d, %d, %d]\n", + mmco_index, h->mmco_index, i); + return AVERROR_INVALIDDATA; + } } int ff_h264_execute_ref_pic_marking(H264Context *h, MMCO *mmco, int mmco_count){ @@ -654,52 +682,83 @@ int ff_h264_execute_ref_pic_marking(H264Context *h, MMCO *mmco, int mmco_count){ return (h->s.avctx->err_recognition & AV_EF_EXPLODE) ? err : 0; } -int ff_h264_decode_ref_pic_marking(H264Context *h, GetBitContext *gb){ +int ff_h264_decode_ref_pic_marking(H264Context *h, GetBitContext *gb, + int first_slice) +{ MpegEncContext * const s = &h->s; int i; + MMCO mmco_temp[MAX_MMCO_COUNT], *mmco = first_slice ? h->mmco : mmco_temp; + int mmco_index = 0; - h->mmco_index= 0; - if(h->nal_unit_type == NAL_IDR_SLICE){ //FIXME fields - s->broken_link= get_bits1(gb) -1; - if(get_bits1(gb)){ - h->mmco[0].opcode= MMCO_LONG; - h->mmco[0].long_arg= 0; - h->mmco_index= 1; + if (h->nal_unit_type == NAL_IDR_SLICE){ // FIXME fields + s->broken_link = get_bits1(gb) - 1; + if (get_bits1(gb)){ + mmco[0].opcode = MMCO_LONG; + mmco[0].long_arg = 0; + mmco_index = 1; } - }else{ - if(get_bits1(gb)){ // adaptive_ref_pic_marking_mode_flag - for(i= 0; immco[i].opcode= opcode; - if(opcode==MMCO_SHORT2UNUSED || opcode==MMCO_SHORT2LONG){ - h->mmco[i].short_pic_num= (h->curr_pic_num - get_ue_golomb(gb) - 1) & (h->max_pic_num - 1); -/* if(h->mmco[i].short_pic_num >= h->short_ref_count || h->short_ref[ h->mmco[i].short_pic_num ] == NULL){ - av_log(s->avctx, AV_LOG_ERROR, "illegal short ref in memory management control operation %d\n", mmco); - return -1; - }*/ - } - if(opcode==MMCO_SHORT2LONG || opcode==MMCO_LONG2UNUSED || opcode==MMCO_LONG || opcode==MMCO_SET_MAX_LONG){ - unsigned int long_arg= get_ue_golomb_31(gb); - if(long_arg >= 32 || (long_arg >= 16 && !(opcode == MMCO_SET_MAX_LONG && long_arg == 16) && !(opcode == MMCO_LONG2UNUSED && FIELD_PICTURE))){ - av_log(h->s.avctx, AV_LOG_ERROR, "illegal long ref in memory management control operation %d\n", opcode); + mmco[i].opcode = opcode; + if (opcode == MMCO_SHORT2UNUSED || opcode == MMCO_SHORT2LONG){ + mmco[i].short_pic_num = + (h->curr_pic_num - get_ue_golomb(gb) - 1) & + (h->max_pic_num - 1); +#if 0 + if (mmco[i].short_pic_num >= h->short_ref_count || + h->short_ref[ mmco[i].short_pic_num ] == NULL){ + av_log(s->avctx, AV_LOG_ERROR, + "illegal short ref in memory management control " + "operation %d\n", mmco); return -1; } - h->mmco[i].long_arg= long_arg; +#endif + } + if (opcode == MMCO_SHORT2LONG || opcode == MMCO_LONG2UNUSED || + opcode == MMCO_LONG || opcode == MMCO_SET_MAX_LONG) { + unsigned int long_arg = get_ue_golomb_31(gb); + if (long_arg >= 32 || + (long_arg >= 16 && !(opcode == MMCO_SET_MAX_LONG && + long_arg == 16) && + !(opcode == MMCO_LONG2UNUSED && FIELD_PICTURE))){ + av_log(h->s.avctx, AV_LOG_ERROR, + "illegal long ref in memory management control " + "operation %d\n", opcode); + return -1; + } + mmco[i].long_arg = long_arg; } - if(opcode > (unsigned)MMCO_LONG){ - av_log(h->s.avctx, AV_LOG_ERROR, "illegal memory management control operation %d\n", opcode); + if (opcode > (unsigned) MMCO_LONG){ + av_log(h->s.avctx, AV_LOG_ERROR, + "illegal memory management control operation %d\n", + opcode); return -1; } - if(opcode == MMCO_END) + if (opcode == MMCO_END) break; } - h->mmco_index= i; - }else{ - ff_generate_sliding_window_mmcos(h); + mmco_index = i; + } else { + if (first_slice) + ff_generate_sliding_window_mmcos(h, first_slice); + mmco_index = -1; } } + if (first_slice && mmco_index != -1) { + h->mmco_index = mmco_index; + } else if (!first_slice && mmco_index >= 0 && + (mmco_index != h->mmco_index || + (i = check_opcodes(h->mmco, mmco_temp, mmco_index)))) { + av_log(h->s.avctx, AV_LOG_ERROR, + "Inconsistent MMCO state between slices [%d, %d, %d]\n", + mmco_index, h->mmco_index, i); + return AVERROR_INVALIDDATA; + } + return 0; }