[FFmpeg-cvslog] h264: don't clobber mmco opcode tables for non-first slice headers.
Ronald S. Bultje
git at videolan.org
Sat Jan 19 16:16:10 CET 2013
ffmpeg | branch: release/1.1 | Ronald S. Bultje <rsbultje at gmail.com> | Sun Jan 13 21:46:44 2013 -0800| [c3b67720f913d0d6be8369777b4cae86a6417bd1] | committer: Luca Barbato
h264: don't clobber mmco opcode tables for non-first slice headers.
Clobbering these tables will temporarily clobber the template used
as a basis for other threads to start decoding from. If the other
decoding thread updates from the template right at that moment,
subsequent threads will get invalid (or, usually, none at all) mmco
tables. This leads to invalid reference lists and subsequent decode
failures.
Therefore, instead, decode the mmco tables only for the first slice in
a field or frame. For other slices, decode the bits and ensure they
are identical to the mmco tables in the first slice, but don't ever
clobber the context state. This prevents other threads from using a
clobbered/invalid template as starting point for decoding, and thus
fixes decoding in these cases.
This fixes occasional (~1%) failures of h264-conformance-mr1_bt_a with
frame-multithreading enabled.
(cherry picked from commit bad446e251405dc250c3cbee199072e083a1e4b9)
Signed-off-by: Luca Barbato <lu_zero at gentoo.org>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=c3b67720f913d0d6be8369777b4cae86a6417bd1
---
libavcodec/h264.c | 12 +++-
libavcodec/h264.h | 5 +-
libavcodec/h264_refs.c | 143 ++++++++++++++++++++++++++++++++++--------------
3 files changed, 114 insertions(+), 46 deletions(-)
diff --git a/libavcodec/h264.c b/libavcodec/h264.c
index 3660597..e8b3e72 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 8596121..ad4732e 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 2a71ac1..62c2a57 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;
-
- 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;
+ MMCO mmco_temp[MAX_MMCO_COUNT], *mmco = first_slice ? h->mmco : mmco_temp;
+ int mmco_index = 0;
+
+ 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; i<MAX_MMCO_COUNT; i++) {
- MMCOOpcode opcode= get_ue_golomb_31(gb);
-
- h->mmco[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);
+ } else {
+ if (get_bits1(gb)) { // adaptive_ref_pic_marking_mode_flag
+ for (i = 0; i < MAX_MMCO_COUNT; i++) {
+ MMCOOpcode opcode = get_ue_golomb_31(gb);
+
+ 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;
- }*/
+ }
+#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);
+ 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;
}
- h->mmco[i].long_arg= long_arg;
+ 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;
}
More information about the ffmpeg-cvslog
mailing list