static int rd_frame(CinepakEncContext *s, const AVFrame *frame, int isakeyframe, unsigned char *buf, int buf_size) { int num_strips, strip, i, y, nexty, size, temp_size, best_size; AVPicture last_pict, pict, scratch_pict; int64_t best_score = 0, score, score_temp; #ifdef CINEPAK_REPORT_SERR int64_t best_serr = 0, serr, serr_temp; #endif int best_nstrips; if(s->pix_fmt == AV_PIX_FMT_RGB24) { int x; // build a copy of the given frame in the correct colorspace for(y = 0; y < s->h; y += 2) { for(x = 0; x < s->w; x += 2) { uint8_t *ir[2]; int32_t r, g, b, rr, gg, bb; ir[0] = ((AVPicture*)frame)->data[0] + x*3 + y*((AVPicture*)frame)->linesize[0]; ir[1] = ir[0] + ((AVPicture*)frame)->linesize[0]; get_sub_picture(s, x, y, (AVPicture*)s->input_frame, &scratch_pict); r = g = b = 0; for(i=0; i<4; ++i) { int i1, i2; i1 = (i&1); i2 = (i>=2); rr = ir[i2][i1*3+0]; gg = ir[i2][i1*3+1]; bb = ir[i2][i1*3+2]; r += rr; g += gg; b += bb; // using fixed point arithmetic for portable repeatability, scaling by 2^23 // "Y" // rr = 0.2857*rr + 0.5714*gg + 0.1429*bb; rr = (2396625*rr + 4793251*gg + 1198732*bb) >> 23; if( rr < 0) rr = 0; else if (rr > 255) rr = 255; scratch_pict.data[0][i1 + i2*scratch_pict.linesize[0]] = rr; } // let us scale down as late as possible // r /= 4; g /= 4; b /= 4; // "U" // rr = -0.1429*r - 0.2857*g + 0.4286*b; rr = (-299683*r - 599156*g + 898839*b) >> 23; if( rr < -128) rr = -128; else if (rr > 127) rr = 127; scratch_pict.data[1][0] = rr + 128; // quantize needs unsigned // "V" // rr = 0.3571*r - 0.2857*g - 0.0714*b; rr = (748893*r - 599156*g - 149737*b) >> 23; if( rr < -128) rr = -128; else if (rr > 127) rr = 127; scratch_pict.data[2][0] = rr + 128; // quantize needs unsigned } } } //would be nice but quite certainly incompatible with vintage players: // support encoding zero strips (meaning skip the whole frame) for(num_strips = s->min_strips; num_strips <= s->max_strips && num_strips <= s->h / MB_SIZE; num_strips++) { score = 0; size = 0; #ifdef CINEPAK_REPORT_SERR serr = 0; #endif for(y = 0, strip = 1; y < s->h; strip++, y = nexty) { int strip_height; nexty = strip * s->h / num_strips; // <= s->h //make nexty the next multiple of 4 if not already there if(nexty & 3) nexty += 4 - (nexty & 3); strip_height = nexty - y; if(strip_height <= 0) { // can this ever happen? av_log(s->avctx, AV_LOG_INFO, "skipping zero height strip %i of %i\n", strip, num_strips); continue; } if(s->pix_fmt == AV_PIX_FMT_RGB24) get_sub_picture(s, 0, y, (AVPicture*)s->input_frame, &pict); else get_sub_picture(s, 0, y, (AVPicture*)frame, &pict); get_sub_picture(s, 0, y, (AVPicture*)s->last_frame, &last_pict); get_sub_picture(s, 0, y, (AVPicture*)s->scratch_frame, &scratch_pict); if((temp_size = rd_strip(s, y, strip_height, isakeyframe, &last_pict, &pict, &scratch_pict, s->frame_buf + size + CVID_HEADER_SIZE, &score_temp #ifdef CINEPAK_REPORT_SERR , &serr_temp #endif )) < 0) return temp_size; score += score_temp; #ifdef CINEPAK_REPORT_SERR serr += serr_temp; #endif size += temp_size; //av_log(s->avctx, AV_LOG_INFO, "strip %d, isakeyframe=%d", strip, isakeyframe); //av_log(s->avctx, AV_LOG_INFO, "\n"); } if(best_score == 0 || score < best_score) { best_score = score; #ifdef CINEPAK_REPORT_SERR best_serr = serr; #endif best_size = size + write_cvid_header(s, s->frame_buf, num_strips, size, isakeyframe); //av_log(s->avctx, AV_LOG_INFO, "best number of strips so far: %2i, %12lli, %i B\n", num_strips, (long long int)score, best_size); #ifdef CINEPAK_REPORT_SERR av_log(s->avctx, AV_LOG_INFO, "best number of strips so far: %2i, %12lli, %i B\n", num_strips, (long long int)serr, best_size); #endif FFSWAP(AVFrame *, s->best_frame, s->scratch_frame); memcpy(buf, s->frame_buf, best_size); best_nstrips = num_strips; } // avoid trying too many strip numbers without a real reason // (this makes the processing of the very first frame faster) if(num_strips - best_nstrips > 4) break; } // let the number of strips slowly adapt to the changes in the contents, // compared to full bruteforcing every time this will occasionally lead // to some r/d performance loss but makes encoding up to several times faster if(!s->strip_number_delta_range) { if(best_nstrips == s->max_strips) { // let us try to step up s->max_strips = best_nstrips + 1; if(s->max_strips >= s->max_max_strips) s->max_strips = s->max_max_strips; } else { // try to step down s->max_strips = best_nstrips; } s->min_strips = s->max_strips - 1; if(s->min_strips < s->min_min_strips) s->min_strips = s->min_min_strips; } else { s->max_strips = best_nstrips + s->strip_number_delta_range; if(s->max_strips >= s->max_max_strips) s->max_strips = s->max_max_strips; s->min_strips = best_nstrips - s->strip_number_delta_range; if(s->min_strips < s->min_min_strips) s->min_strips = s->min_min_strips; } return best_size; }