in Extended/libwebp/src/mux/anim_encode.c [1196:1295]
static int CacheFrame(WebPAnimEncoder* const enc,
const WebPConfig* const config) {
int ok = 0;
int frame_skipped = 0;
WebPEncodingError error_code = VP8_ENC_OK;
const size_t position = enc->count_;
EncodedFrame* const encoded_frame = GetFrame(enc, position);
++enc->count_;
if (enc->is_first_frame_) { // Add this as a key-frame.
error_code = SetFrame(enc, config, 1, encoded_frame, &frame_skipped);
if (error_code != VP8_ENC_OK) goto End;
assert(frame_skipped == 0); // First frame can't be skipped, even if empty.
assert(position == 0 && enc->count_ == 1);
encoded_frame->is_key_frame_ = 1;
enc->flush_count_ = 0;
enc->count_since_key_frame_ = 0;
enc->prev_candidate_undecided_ = 0;
} else {
++enc->count_since_key_frame_;
if (enc->count_since_key_frame_ <= enc->options_.kmin) {
// Add this as a frame rectangle.
error_code = SetFrame(enc, config, 0, encoded_frame, &frame_skipped);
if (error_code != VP8_ENC_OK) goto End;
if (frame_skipped) goto Skip;
encoded_frame->is_key_frame_ = 0;
enc->flush_count_ = enc->count_ - 1;
enc->prev_candidate_undecided_ = 0;
} else {
int64_t curr_delta;
FrameRectangle prev_rect_key, prev_rect_sub;
// Add this as a frame rectangle to enc.
error_code = SetFrame(enc, config, 0, encoded_frame, &frame_skipped);
if (error_code != VP8_ENC_OK) goto End;
if (frame_skipped) goto Skip;
prev_rect_sub = enc->prev_rect_;
// Add this as a key-frame to enc, too.
error_code = SetFrame(enc, config, 1, encoded_frame, &frame_skipped);
if (error_code != VP8_ENC_OK) goto End;
assert(frame_skipped == 0); // Key-frame cannot be an empty rectangle.
prev_rect_key = enc->prev_rect_;
// Analyze size difference of the two variants.
curr_delta = KeyFramePenalty(encoded_frame);
if (curr_delta <= enc->best_delta_) { // Pick this as the key-frame.
if (enc->keyframe_ != KEYFRAME_NONE) {
EncodedFrame* const old_keyframe = GetFrame(enc, enc->keyframe_);
assert(old_keyframe->is_key_frame_);
old_keyframe->is_key_frame_ = 0;
}
encoded_frame->is_key_frame_ = 1;
enc->prev_candidate_undecided_ = 1;
enc->keyframe_ = (int)position;
enc->best_delta_ = curr_delta;
enc->flush_count_ = enc->count_ - 1; // We can flush previous frames.
} else {
encoded_frame->is_key_frame_ = 0;
enc->prev_candidate_undecided_ = 0;
}
// Note: We need '>=' below because when kmin and kmax are both zero,
// count_since_key_frame will always be > kmax.
if (enc->count_since_key_frame_ >= enc->options_.kmax) {
enc->flush_count_ = enc->count_ - 1;
enc->count_since_key_frame_ = 0;
enc->keyframe_ = KEYFRAME_NONE;
enc->best_delta_ = DELTA_INFINITY;
}
if (!enc->prev_candidate_undecided_) {
enc->prev_rect_ =
encoded_frame->is_key_frame_ ? prev_rect_key : prev_rect_sub;
}
}
}
// Update previous to previous and previous canvases for next call.
WebPCopyPixels(enc->curr_canvas_, &enc->prev_canvas_);
enc->is_first_frame_ = 0;
Skip:
ok = 1;
++enc->in_frame_count_;
End:
if (!ok || frame_skipped) {
FrameRelease(encoded_frame);
// We reset some counters, as the frame addition failed/was skipped.
--enc->count_;
if (!enc->is_first_frame_) --enc->count_since_key_frame_;
if (!ok) {
MarkError2(enc, "ERROR adding frame. WebPEncodingError", error_code);
}
}
enc->curr_canvas_->error_code = error_code; // report error_code
assert(ok || error_code != VP8_ENC_OK);
return ok;
}