X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Fh264.c;h=963399523993c49a055a2fd15b1fd071296fd4e2;hb=0d43dd8c2d1e8dc7cf0bc65b95b7612cbe01c060;hp=a1ac4aca29c42a148c20c99b7798412624385eff;hpb=12a09bfa3952e6a7b53cd352920dd0d03cbc6f3e;p=ffmpeg diff --git a/libavcodec/h264.c b/libavcodec/h264.c index a1ac4aca29c..96339952399 100644 --- a/libavcodec/h264.c +++ b/libavcodec/h264.c @@ -1703,7 +1703,7 @@ static inline void mc_dir_part(H264Context *h, Picture *pic, int n, int square, const int full_mx= mx>>2; const int full_my= my>>2; const int pic_width = 16*s->mb_width; - const int pic_height = 16*s->mb_height >> (MB_MBAFF || FIELD_PICTURE); + const int pic_height = 16*s->mb_height >> MB_FIELD; if(!pic->data[0]) //FIXME this is unacceptable, some senseable error concealment must be done for missing reference frames return; @@ -1727,7 +1727,7 @@ static inline void mc_dir_part(H264Context *h, Picture *pic, int n, int square, if(ENABLE_GRAY && s->flags&CODEC_FLAG_GRAY) return; - if(MB_MBAFF || FIELD_PICTURE){ + if(MB_FIELD){ // chroma offset when predicting from a field of opposite parity my += 2 * ((s->mb_y & 1) - (h->ref_cache[list][scan8[n]] & 1)); emu |= (my>>3) < 0 || (my>>3) + 8 >= (pic_height>>1); @@ -1762,7 +1762,7 @@ static inline void mc_part_std(H264Context *h, int n, int square, int chroma_hei dest_cb += x_offset + y_offset*h->mb_uvlinesize; dest_cr += x_offset + y_offset*h->mb_uvlinesize; x_offset += 8*s->mb_x; - y_offset += 8*(s->mb_y >> (MB_MBAFF || FIELD_PICTURE)); + y_offset += 8*(s->mb_y >> MB_FIELD); if(list0){ Picture *ref= &h->ref_list[0][ h->ref_cache[0][ scan8[n] ] ]; @@ -1795,7 +1795,7 @@ static inline void mc_part_weighted(H264Context *h, int n, int square, int chrom dest_cb += x_offset + y_offset*h->mb_uvlinesize; dest_cr += x_offset + y_offset*h->mb_uvlinesize; x_offset += 8*s->mb_x; - y_offset += 8*(s->mb_y >> (MB_MBAFF || FIELD_PICTURE)); + y_offset += 8*(s->mb_y >> MB_FIELD); if(list0 && list1){ /* don't optimize for luma-only case, since B-frames usually @@ -3076,7 +3076,7 @@ static int decode_ref_pic_list_reordering(H264Context *h){ const unsigned int abs_diff_pic_num= get_ue_golomb(&s->gb) + 1; int frame_num; - if(abs_diff_pic_num >= h->max_pic_num){ + if(abs_diff_pic_num > h->max_pic_num){ av_log(h->s.avctx, AV_LOG_ERROR, "abs_diff_pic_num overflow\n"); return -1; } @@ -3339,6 +3339,7 @@ static void flush_dpb(AVCodecContext *avctx){ idr(h); if(h->s.current_picture_ptr) h->s.current_picture_ptr->reference= 0; + h->s.first_field= 0; } /** @@ -3458,50 +3459,88 @@ static void print_long_term(H264Context *h) { static int execute_ref_pic_marking(H264Context *h, MMCO *mmco, int mmco_count){ MpegEncContext * const s = &h->s; int i, j; - int current_is_long=0; + int current_ref_assigned=0; Picture *pic; if((s->avctx->debug&FF_DEBUG_MMCO) && mmco_count==0) av_log(h->s.avctx, AV_LOG_DEBUG, "no mmco here\n"); for(i=0; iavctx->debug&FF_DEBUG_MMCO) av_log(h->s.avctx, AV_LOG_DEBUG, "mmco:%d %d %d\n", h->mmco[i].opcode, h->mmco[i].short_pic_num, h->mmco[i].long_arg); switch(mmco[i].opcode){ case MMCO_SHORT2UNUSED: - pic= remove_short(h, mmco[i].short_pic_num); - if(pic) - unreference_pic(h, pic, 0); - else if(s->avctx->debug&FF_DEBUG_MMCO) - av_log(h->s.avctx, AV_LOG_DEBUG, "mmco: remove_short() failure\n"); + if(s->avctx->debug&FF_DEBUG_MMCO) + av_log(h->s.avctx, AV_LOG_DEBUG, "mmco: unref short %d count %d\n", h->mmco[i].short_pic_num, h->short_ref_count); + frame_num = pic_num_extract(h, mmco[i].short_pic_num, &structure); + pic = find_short(h, frame_num, &j); + if (pic) { + if (unreference_pic(h, pic, structure ^ PICT_FRAME)) + remove_short_at_index(h, j); + } else if(s->avctx->debug&FF_DEBUG_MMCO) + av_log(h->s.avctx, AV_LOG_DEBUG, "mmco: unref short failure\n"); break; case MMCO_SHORT2LONG: - pic= remove_long(h, mmco[i].long_arg); - if(pic) unreference_pic(h, pic, 0); + if (FIELD_PICTURE && mmco[i].long_arg < h->long_ref_count && + h->long_ref[mmco[i].long_arg]->frame_num == + mmco[i].short_pic_num / 2) { + /* do nothing, we've already moved this field pair. */ + } else { + int frame_num = mmco[i].short_pic_num >> FIELD_PICTURE; - h->long_ref[ mmco[i].long_arg ]= remove_short(h, mmco[i].short_pic_num); - if (h->long_ref[ mmco[i].long_arg ]){ - h->long_ref[ mmco[i].long_arg ]->long_ref=1; - h->long_ref_count++; + pic= remove_long(h, mmco[i].long_arg); + if(pic) unreference_pic(h, pic, 0); + + h->long_ref[ mmco[i].long_arg ]= remove_short(h, frame_num); + if (h->long_ref[ mmco[i].long_arg ]){ + h->long_ref[ mmco[i].long_arg ]->long_ref=1; + h->long_ref_count++; + } } break; case MMCO_LONG2UNUSED: - pic= remove_long(h, mmco[i].long_arg); - if(pic) - unreference_pic(h, pic, 0); - else if(s->avctx->debug&FF_DEBUG_MMCO) - av_log(h->s.avctx, AV_LOG_DEBUG, "mmco: remove_long() failure\n"); + j = pic_num_extract(h, mmco[i].long_arg, &structure); + pic = h->long_ref[j]; + if (pic) { + if (unreference_pic(h, pic, structure ^ PICT_FRAME)) + remove_long_at_index(h, j); + } else if(s->avctx->debug&FF_DEBUG_MMCO) + av_log(h->s.avctx, AV_LOG_DEBUG, "mmco: unref long failure\n"); break; case MMCO_LONG: - pic= remove_long(h, mmco[i].long_arg); - if(pic) unreference_pic(h, pic, 0); + unref_pic = 1; + if (FIELD_PICTURE && !s->first_field) { + if (h->long_ref[mmco[i].long_arg] == s->current_picture_ptr) { + /* Just mark second field as referenced */ + unref_pic = 0; + } else if (s->current_picture_ptr->reference) { + /* First field in pair is in short term list or + * at a different long term index. + * This is not allowed; see 7.4.3, notes 2 and 3. + * Report the problem and keep the pair where it is, + * and mark this field valid. + */ + av_log(h->s.avctx, AV_LOG_ERROR, + "illegal long term reference assignment for second " + "field in complementary field pair (first field is " + "short term or has non-matching long index)\n"); + unref_pic = 0; + } + } + + if (unref_pic) { + pic= remove_long(h, mmco[i].long_arg); + if(pic) unreference_pic(h, pic, 0); - h->long_ref[ mmco[i].long_arg ]= s->current_picture_ptr; - h->long_ref[ mmco[i].long_arg ]->long_ref=1; - h->long_ref_count++; + h->long_ref[ mmco[i].long_arg ]= s->current_picture_ptr; + h->long_ref[ mmco[i].long_arg ]->long_ref=1; + h->long_ref_count++; + } - current_is_long=1; + s->current_picture_ptr->reference |= s->picture_structure; + current_ref_assigned=1; break; case MMCO_SET_MAX_LONG: assert(mmco[i].long_arg <= 16); @@ -3525,7 +3564,35 @@ static int execute_ref_pic_marking(H264Context *h, MMCO *mmco, int mmco_count){ } } - if(!current_is_long){ + if (!current_ref_assigned && FIELD_PICTURE && + !s->first_field && s->current_picture_ptr->reference) { + + /* Second field of complementary field pair; the first field of + * which is already referenced. If short referenced, it + * should be first entry in short_ref. If not, it must exist + * in long_ref; trying to put it on the short list here is an + * error in the encoded bit stream (ref: 7.4.3, NOTE 2 and 3). + */ + if (h->short_ref_count && h->short_ref[0] == s->current_picture_ptr) { + /* Just mark the second field valid */ + s->current_picture_ptr->reference = PICT_FRAME; + } else if (s->current_picture_ptr->long_ref) { + av_log(h->s.avctx, AV_LOG_ERROR, "illegal short term reference " + "assignment for second field " + "in complementary field pair " + "(first field is long term)\n"); + } else { + /* + * First field in reference, but not in any sensible place on our + * reference lists. This shouldn't happen unless reference + * handling somewhere else is wrong. + */ + assert(0); + } + current_ref_assigned = 1; + } + + if(!current_ref_assigned){ pic= remove_short(h, s->current_picture_ptr->frame_num); if(pic){ unreference_pic(h, pic, 0); @@ -3538,6 +3605,7 @@ static int execute_ref_pic_marking(H264Context *h, MMCO *mmco, int mmco_count){ h->short_ref[0]= s->current_picture_ptr; h->short_ref[0]->long_ref=0; h->short_ref_count++; + s->current_picture_ptr->reference |= s->picture_structure; } print_short_term(h); @@ -3763,6 +3831,7 @@ static void clone_slice(H264Context *dst, H264Context *src) dst->s.current_picture = src->s.current_picture; dst->s.linesize = src->s.linesize; dst->s.uvlinesize = src->s.uvlinesize; + dst->s.first_field = src->s.first_field; dst->prev_poc_msb = src->prev_poc_msb; dst->prev_poc_lsb = src->prev_poc_lsb; @@ -3790,12 +3859,14 @@ static void clone_slice(H264Context *dst, H264Context *src) */ static int decode_slice_header(H264Context *h, H264Context *h0){ MpegEncContext * const s = &h->s; + MpegEncContext * const s0 = &h0->s; unsigned int first_mb_in_slice; unsigned int pps_id; int num_ref_idx_active_override_flag; static const uint8_t slice_type_map[5]= {P_TYPE, B_TYPE, I_TYPE, SP_TYPE, SI_TYPE}; unsigned int slice_type, tmp, i; int default_ref_list_done = 0; + int last_pic_structure; s->dropable= h->nal_ref_idc == 0; @@ -3803,7 +3874,8 @@ static int decode_slice_header(H264Context *h, H264Context *h0){ if((s->flags2 & CODEC_FLAG2_CHUNKS) && first_mb_in_slice == 0){ h0->current_slice = 0; - s->current_picture_ptr= NULL; + if (!s0->first_field) + s->current_picture_ptr= NULL; } slice_type= get_ue_golomb(&s->gb); @@ -3872,6 +3944,7 @@ static int decode_slice_header(H264Context *h, H264Context *h0){ return -1; // we cant (re-)initialize context during parallel decoding if (MPV_common_init(s) < 0) return -1; + s->first_field = 0; init_scan_tables(h); alloc_tables(h); @@ -3910,12 +3983,12 @@ static int decode_slice_header(H264Context *h, H264Context *h0){ h->mb_mbaff = 0; h->mb_aff_frame = 0; + last_pic_structure = s0->picture_structure; if(h->sps.frame_mbs_only_flag){ s->picture_structure= PICT_FRAME; }else{ if(get_bits1(&s->gb)) { //field_pic_flag s->picture_structure= PICT_TOP_FIELD + get_bits1(&s->gb); //bottom_field_flag - av_log(h->s.avctx, AV_LOG_ERROR, "PAFF interlacing is not implemented\n"); } else { s->picture_structure= PICT_FRAME; h->mb_aff_frame = h->sps.mb_aff; @@ -3923,8 +3996,50 @@ static int decode_slice_header(H264Context *h, H264Context *h0){ } if(h0->current_slice == 0){ - if(frame_start(h) < 0) + /* See if we have a decoded first field looking for a pair... */ + if (s0->first_field) { + assert(s0->current_picture_ptr); + assert(s0->current_picture_ptr->data[0]); + assert(s0->current_picture_ptr->reference != DELAYED_PIC_REF); + + /* figure out if we have a complementary field pair */ + if (!FIELD_PICTURE || s->picture_structure == last_pic_structure) { + /* + * Previous field is unmatched. Don't display it, but let it + * remain for reference if marked as such. + */ + s0->current_picture_ptr = NULL; + s0->first_field = FIELD_PICTURE; + + } else { + if (h->nal_ref_idc && + s0->current_picture_ptr->reference && + s0->current_picture_ptr->frame_num != h->frame_num) { + /* + * This and previous field were reference, but had + * different frame_nums. Consider this field first in + * pair. Throw away previous field except for reference + * purposes. + */ + s0->first_field = 1; + s0->current_picture_ptr = NULL; + + } else { + /* Second field in complementary pair */ + s0->first_field = 0; + } + } + + } else { + /* Frame or first field in a potentially complementary pair */ + assert(!s0->current_picture_ptr); + s0->first_field = FIELD_PICTURE; + } + + if((!FIELD_PICTURE || s0->first_field) && frame_start(h) < 0) { + s0->first_field = 0; return -1; + } } if(h != h0) clone_slice(h, h0); @@ -3983,8 +4098,8 @@ static int decode_slice_header(H264Context *h, H264Context *h0){ if(h->slice_type == P_TYPE || h->slice_type == SP_TYPE || h->slice_type == B_TYPE){ if(h->slice_type == B_TYPE){ h->direct_spatial_mv_pred= get_bits1(&s->gb); - if(h->sps.mb_aff && h->direct_spatial_mv_pred) - av_log(h->s.avctx, AV_LOG_ERROR, "MBAFF + spatial direct mode is not implemented\n"); + if(FIELD_OR_MBAFF_PICTURE && h->direct_spatial_mv_pred) + av_log(h->s.avctx, AV_LOG_ERROR, "Interlaced pictures + spatial direct mode is not implemented\n"); } num_ref_idx_active_override_flag= get_bits1(&s->gb); @@ -6284,17 +6399,18 @@ static void filter_mb_fast( H264Context *h, int mb_x, int mb_y, uint8_t *img_y, if( IS_INTRA(mb_type) ) { int16_t bS4[4] = {4,4,4,4}; int16_t bS3[4] = {3,3,3,3}; + int16_t *bSH = FIELD_PICTURE ? bS3 : bS4; if( IS_8x8DCT(mb_type) ) { filter_mb_edgev( h, &img_y[4*0], linesize, bS4, qp0 ); filter_mb_edgev( h, &img_y[4*2], linesize, bS3, qp ); - filter_mb_edgeh( h, &img_y[4*0*linesize], linesize, bS4, qp1 ); + filter_mb_edgeh( h, &img_y[4*0*linesize], linesize, bSH, qp1 ); filter_mb_edgeh( h, &img_y[4*2*linesize], linesize, bS3, qp ); } else { filter_mb_edgev( h, &img_y[4*0], linesize, bS4, qp0 ); filter_mb_edgev( h, &img_y[4*1], linesize, bS3, qp ); filter_mb_edgev( h, &img_y[4*2], linesize, bS3, qp ); filter_mb_edgev( h, &img_y[4*3], linesize, bS3, qp ); - filter_mb_edgeh( h, &img_y[4*0*linesize], linesize, bS4, qp1 ); + filter_mb_edgeh( h, &img_y[4*0*linesize], linesize, bSH, qp1 ); filter_mb_edgeh( h, &img_y[4*1*linesize], linesize, bS3, qp ); filter_mb_edgeh( h, &img_y[4*2*linesize], linesize, bS3, qp ); filter_mb_edgeh( h, &img_y[4*3*linesize], linesize, bS3, qp ); @@ -6303,9 +6419,9 @@ static void filter_mb_fast( H264Context *h, int mb_x, int mb_y, uint8_t *img_y, filter_mb_edgecv( h, &img_cb[2*2], uvlinesize, bS3, qpc ); filter_mb_edgecv( h, &img_cr[2*0], uvlinesize, bS4, qpc0 ); filter_mb_edgecv( h, &img_cr[2*2], uvlinesize, bS3, qpc ); - filter_mb_edgech( h, &img_cb[2*0*uvlinesize], uvlinesize, bS4, qpc1 ); + filter_mb_edgech( h, &img_cb[2*0*uvlinesize], uvlinesize, bSH, qpc1 ); filter_mb_edgech( h, &img_cb[2*2*uvlinesize], uvlinesize, bS3, qpc ); - filter_mb_edgech( h, &img_cr[2*0*uvlinesize], uvlinesize, bS4, qpc1 ); + filter_mb_edgech( h, &img_cr[2*0*uvlinesize], uvlinesize, bSH, qpc1 ); filter_mb_edgech( h, &img_cr[2*2*uvlinesize], uvlinesize, bS3, qpc ); return; } else { @@ -6327,7 +6443,7 @@ static void filter_mb_fast( H264Context *h, int mb_x, int mb_y, uint8_t *img_y, (h->slice_type == B_TYPE), edges, step, mask_edge0, mask_edge1 ); } if( IS_INTRA(s->current_picture.mb_type[mb_xy-1]) ) - bSv[0][0] = 0x0004000400040004ULL; + bSv[0][0] = FIELD_PICTURE ? 0x0003000300030003ULL : 0x0004000400040004ULL; if( IS_INTRA(s->current_picture.mb_type[h->top_mb_xy]) ) bSv[1][0] = 0x0004000400040004ULL; @@ -7295,6 +7411,8 @@ static void execute_decode_slices(H264Context *h, int context_count){ hx = h->thread_context[context_count - 1]; s->mb_x = hx->s.mb_x; s->mb_y = hx->s.mb_y; + s->dropable = hx->s.dropable; + s->picture_structure = hx->s.picture_structure; for(i = 1; i < context_count; i++) h->s.error_count += h->thread_context[i]->s.error_count; } @@ -7317,7 +7435,8 @@ static int decode_nal_units(H264Context *h, uint8_t *buf, int buf_size){ #endif if(!(s->flags2 & CODEC_FLAG2_CHUNKS)){ h->current_slice = 0; - s->current_picture_ptr= NULL; + if (!s->first_field) + s->current_picture_ptr= NULL; } for(;;){ @@ -7614,86 +7733,105 @@ static int decode_frame(AVCodecContext *avctx, h->prev_frame_num_offset= h->frame_num_offset; h->prev_frame_num= h->frame_num; - if(s->current_picture_ptr->reference & s->picture_structure){ + if(!s->dropable) { h->prev_poc_msb= h->poc_msb; h->prev_poc_lsb= h->poc_lsb; execute_ref_pic_marking(h, h->mmco, h->mmco_index); } - ff_er_frame_end(s); + /* + * FIXME: Error handling code does not seem to support interlaced + * when slices span multiple rows + * The ff_er_add_slice calls don't work right for bottom + * fields; they cause massive erroneous error concealing + * Error marking covers both fields (top and bottom). + * This causes a mismatched s->error_count + * and a bad error table. Further, the error count goes to + * INT_MAX when called for bottom field, because mb_y is + * past end by one (callers fault) and resync_mb_y != 0 + * causes problems for the first MB line, too. + */ + if (!FIELD_PICTURE) + ff_er_frame_end(s); MPV_frame_end(s); - //FIXME do something with unavailable reference frames + if (s->first_field) { + /* Wait for second field. */ + *data_size = 0; + + } else { + //FIXME do something with unavailable reference frames #if 0 //decode order - *data_size = sizeof(AVFrame); + *data_size = sizeof(AVFrame); #else - /* Sort B-frames into display order */ + /* Sort B-frames into display order */ - if(h->sps.bitstream_restriction_flag - && s->avctx->has_b_frames < h->sps.num_reorder_frames){ - s->avctx->has_b_frames = h->sps.num_reorder_frames; - s->low_delay = 0; - } - - pics = 0; - while(h->delayed_pic[pics]) pics++; - - assert(pics+1 < sizeof(h->delayed_pic) / sizeof(h->delayed_pic[0])); + if(h->sps.bitstream_restriction_flag + && s->avctx->has_b_frames < h->sps.num_reorder_frames){ + s->avctx->has_b_frames = h->sps.num_reorder_frames; + s->low_delay = 0; + } - h->delayed_pic[pics++] = cur; - if(cur->reference == 0) - cur->reference = DELAYED_PIC_REF; + pics = 0; + while(h->delayed_pic[pics]) pics++; - cross_idr = 0; - for(i=0; h->delayed_pic[i]; i++) - if(h->delayed_pic[i]->key_frame || h->delayed_pic[i]->poc==0) - cross_idr = 1; + assert(pics+1 < sizeof(h->delayed_pic) / sizeof(h->delayed_pic[0])); - out = h->delayed_pic[0]; - out_idx = 0; - for(i=1; h->delayed_pic[i] && !h->delayed_pic[i]->key_frame; i++) - if(h->delayed_pic[i]->poc < out->poc){ - out = h->delayed_pic[i]; - out_idx = i; - } + h->delayed_pic[pics++] = cur; + if(cur->reference == 0) + cur->reference = DELAYED_PIC_REF; - out_of_order = !cross_idr && prev && out->poc < prev->poc; - if(h->sps.bitstream_restriction_flag && s->avctx->has_b_frames >= h->sps.num_reorder_frames) - { } - else if(prev && pics <= s->avctx->has_b_frames) - out = prev; - else if((out_of_order && pics-1 == s->avctx->has_b_frames && pics < 15) - || (s->low_delay && - ((!cross_idr && prev && out->poc > prev->poc + 2) - || cur->pict_type == B_TYPE))) - { - s->low_delay = 0; - s->avctx->has_b_frames++; - out = prev; - } - else if(out_of_order) - out = prev; + cross_idr = 0; + for(i=0; h->delayed_pic[i]; i++) + if(h->delayed_pic[i]->key_frame || h->delayed_pic[i]->poc==0) + cross_idr = 1; - if(out_of_order || pics > s->avctx->has_b_frames){ - for(i=out_idx; h->delayed_pic[i]; i++) - h->delayed_pic[i] = h->delayed_pic[i+1]; - } + out = h->delayed_pic[0]; + out_idx = 0; + for(i=1; h->delayed_pic[i] && !h->delayed_pic[i]->key_frame; i++) + if(h->delayed_pic[i]->poc < out->poc){ + out = h->delayed_pic[i]; + out_idx = i; + } - if(prev == out) - *data_size = 0; - else - *data_size = sizeof(AVFrame); - if(prev && prev != out && prev->reference == DELAYED_PIC_REF) - prev->reference = 0; - h->delayed_output_pic = out; + out_of_order = !cross_idr && prev && out->poc < prev->poc; + if(h->sps.bitstream_restriction_flag && s->avctx->has_b_frames >= h->sps.num_reorder_frames) + { } + else if(prev && pics <= s->avctx->has_b_frames) + out = prev; + else if((out_of_order && pics-1 == s->avctx->has_b_frames && pics < 15) + || (s->low_delay && + ((!cross_idr && prev && out->poc > prev->poc + 2) + || cur->pict_type == B_TYPE))) + { + s->low_delay = 0; + s->avctx->has_b_frames++; + out = prev; + } + else if(out_of_order) + out = prev; + + if(out_of_order || pics > s->avctx->has_b_frames){ + for(i=out_idx; h->delayed_pic[i]; i++) + h->delayed_pic[i] = h->delayed_pic[i+1]; + } + + if(prev == out) + *data_size = 0; + else + *data_size = sizeof(AVFrame); + if(prev && prev != out && prev->reference == DELAYED_PIC_REF) + prev->reference = 0; + h->delayed_output_pic = out; #endif - if(out) - *pict= *(AVFrame*)out; - else - av_log(avctx, AV_LOG_DEBUG, "no picture\n"); + if(out) + *pict= *(AVFrame*)out; + else + av_log(avctx, AV_LOG_DEBUG, "no picture\n"); + } } assert(pict->data[0] || !*data_size); @@ -7705,6 +7843,7 @@ static int decode_frame(AVCodecContext *avctx, /* we substract 1 because it is added on utils.c */ avctx->frame_number = s->picture_number - 1; #endif + pict->interlaced_frame = FIELD_OR_MBAFF_PICTURE; return get_consumed_bytes(s, buf_index, buf_size); } #if 0