VSE: Cleanup speed effect math

Simplify logic of speed effect frame calculation by using discrete math
where possible. Only `SEQ_SPEED_MULTIPLY` mode with animation requires
frame map to be built. Frame map building was simplified by removing
unused branches.

Functional change: Animating strip in negative range will reverse playback.
I assume this was limitation of previous system, where each frame map item
was limited to be within correct frame range. Now frame map can contain
values that point beyond usable range and they are limited by
`seq_speed_effect_target_frame_get`. This way it is possible to control
playback rate in both directions.

Mostly fixes T89120 apart from offset handling.

Reviewed By: mano-wii

Differential Revision: https://developer.blender.org/D11939
This commit is contained in:
Richard Antalik 2021-08-24 01:01:48 +02:00
parent a57ba4147f
commit 929d7597b3
Notes: blender-bot 2023-02-13 18:25:07 +01:00
Referenced by commit b08ab49cce, Fix T90988: Incorrect speed effect strip math
Referenced by issue #89120, VSE: Speed Strip bugs
6 changed files with 86 additions and 151 deletions

View File

@ -1176,11 +1176,6 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
seq->flag |= SEQ_EFFECT_NOT_LOADED;
}
if (seq->type == SEQ_TYPE_SPEED) {
SpeedControlVars *s = seq->effectdata;
s->frameMap = NULL;
}
if (seq->type == SEQ_TYPE_TEXT) {
TextVars *t = seq->effectdata;
t->text_blf_id = SEQ_FONT_NOT_LOADED;

View File

@ -339,11 +339,8 @@ typedef struct SpeedControlVars {
float *frameMap;
/* DEPRECATED, only used for versioning. */
float globalSpeed;
/* DEPRECATED, only used for versioning. */
int flags;
int length;
int lastValidFrame;
int speed_control_type;
float speed_fader;

View File

@ -3069,8 +3069,6 @@ static void init_speed_effect(Sequence *seq)
seq->effectdata = MEM_callocN(sizeof(SpeedControlVars), "speedcontrolvars");
v = (SpeedControlVars *)seq->effectdata;
v->frameMap = NULL;
v->length = 0;
v->speed_control_type = SEQ_SPEED_STRETCH;
v->speed_fader = 1.0f;
v->speed_fader_length = 0.0f;
@ -3080,9 +3078,7 @@ static void init_speed_effect(Sequence *seq)
static void load_speed_effect(Sequence *seq)
{
SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
v->frameMap = NULL;
v->length = 0;
}
static int num_inputs_speed(void)
@ -3105,7 +3101,6 @@ static void copy_speed_effect(Sequence *dst, Sequence *src, const int UNUSED(fla
dst->effectdata = MEM_dupallocN(src->effectdata);
v = (SpeedControlVars *)dst->effectdata;
v->frameMap = NULL;
v->length = 0;
}
static int early_out_speed(Sequence *UNUSED(seq), float UNUSED(facf0), float UNUSED(facf1))
@ -3127,164 +3122,112 @@ static int seq_effect_speed_get_strip_content_length(const Sequence *seq)
return seq->len;
}
void seq_effect_speed_rebuild_map(Scene *scene, Sequence *seq, bool force)
static FCurve *seq_effect_speed_speed_factor_curve_get(Scene *scene, Sequence *seq)
{
int timeline_frame;
float fallback_fac = 1.0f;
SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
FCurve *fcu = NULL;
return id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_factor", 0, NULL);
}
/* if not already done, load / initialize data */
SEQ_effect_handle_get(seq);
if ((force == false) && (seq->len == v->length) && (v->frameMap != NULL)) {
return;
}
/* Build frame map when speed in mode #SEQ_SPEED_MULTIPLY is animated.
* This is, because `target_frame` value is integrated over time. */
void seq_effect_speed_rebuild_map(Scene *scene, Sequence *seq)
{
if ((seq->seq1 == NULL) || (seq->len < 1)) {
/* make coverity happy and check for (CID 598) input strip ... */
return; /* Make coverity happy and check for (CID 598) input strip... */
}
FCurve *fcu = seq_effect_speed_speed_factor_curve_get(scene, seq);
if (fcu == NULL) {
return;
}
/* XXX(campbell): new in 2.5x. should we use the animation system this way?
* The fcurve is needed because many frames need evaluating at once. */
switch (v->speed_control_type) {
case SEQ_SPEED_MULTIPLY: {
fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_factor", 0, NULL);
break;
}
case SEQ_SPEED_FRAME_NUMBER: {
fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_frame_number", 0, NULL);
break;
}
case SEQ_SPEED_LENGTH: {
fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_length", 0, NULL);
break;
}
}
if (!v->frameMap || v->length != seq->len) {
if (v->frameMap) {
MEM_freeN(v->frameMap);
}
v->length = seq->len;
v->frameMap = MEM_callocN(sizeof(float) * v->length, "speedcontrol frameMap");
SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
if (v->frameMap) {
MEM_freeN(v->frameMap);
}
fallback_fac = 1.0;
const int effect_strip_length = seq->enddisp - seq->startdisp;
v->frameMap = MEM_mallocN(sizeof(float) * effect_strip_length, __func__);
v->frameMap[0] = 0.0f;
const int target_strip_length = seq_effect_speed_get_strip_content_length(seq->seq1);
if (v->speed_control_type == SEQ_SPEED_STRETCH) {
if ((seq->seq1->enddisp != seq->seq1->start) && (target_strip_length != 0)) {
fallback_fac = (float)target_strip_length / (float)(seq->seq1->enddisp - seq->seq1->start);
fcu = NULL;
}
float target_frame = 0;
for (int frame_index = 1; frame_index < effect_strip_length; frame_index++) {
target_frame += evaluate_fcurve(fcu, seq->startdisp + frame_index);
v->frameMap[frame_index] = target_frame;
}
else {
/* if there is no fcurve, use value as simple multiplier */
if (!fcu) {
switch (v->speed_control_type) {
case SEQ_SPEED_MULTIPLY: {
fallback_fac = v->speed_fader;
break;
}
case SEQ_SPEED_FRAME_NUMBER: {
fallback_fac = v->speed_fader_frame_number;
break;
}
case SEQ_SPEED_LENGTH: {
fallback_fac = v->speed_fader_length;
break;
}
}
}
}
static void seq_effect_speed_frame_map_ensure(Scene *scene, Sequence *seq, FCurve *fcu)
{
SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
if (v->frameMap != NULL) {
return;
}
if (ELEM(v->speed_control_type, SEQ_SPEED_MULTIPLY, SEQ_SPEED_STRETCH)) {
float cursor = 0;
float facf;
v->frameMap[0] = 0;
v->lastValidFrame = 0;
for (timeline_frame = 1; timeline_frame < v->length; timeline_frame++) {
if (fcu) {
facf = evaluate_fcurve(fcu, seq->startdisp + timeline_frame);
}
else {
facf = fallback_fac;
}
cursor += facf;
if (cursor >= target_strip_length) {
v->frameMap[timeline_frame] = target_strip_length - 1;
}
else {
v->frameMap[timeline_frame] = cursor;
v->lastValidFrame = timeline_frame;
}
}
}
else {
float facf;
v->lastValidFrame = 0;
for (timeline_frame = 0; timeline_frame < v->length; timeline_frame++) {
if (fcu) {
facf = evaluate_fcurve(fcu, seq->startdisp + timeline_frame);
}
else {
facf = fallback_fac;
}
if (v->speed_control_type == SEQ_SPEED_LENGTH) {
facf *= target_strip_length;
facf /= 100.0f;
}
if (facf >= target_strip_length) {
facf = target_strip_length - 1;
}
else {
v->lastValidFrame = timeline_frame;
}
v->frameMap[timeline_frame] = facf;
}
}
seq_effect_speed_rebuild_map(scene, seq);
}
/* Override timeline_frame when rendering speed effect input. */
float seq_speed_effect_target_frame_get(const SeqRenderData *context,
Sequence *seq,
float seq_speed_effect_target_frame_get(Scene *scene,
Sequence *seq_speed,
float timeline_frame,
int input)
{
int frame_index = seq_give_frame_index(seq, timeline_frame);
SpeedControlVars *s = (SpeedControlVars *)seq->effectdata;
seq_effect_speed_rebuild_map(context->scene, seq, false);
if (seq_speed->seq1 == NULL) {
return 0.0f;
}
SEQ_effect_handle_get(seq_speed); /* Ensure, that data are initialized. */
int frame_index = seq_give_frame_index(seq_speed, timeline_frame);
SpeedControlVars *s = (SpeedControlVars *)seq_speed->effectdata;
const Sequence *source = seq_speed->seq1;
float target_frame = 0.0f;
switch (s->speed_control_type) {
case SEQ_SPEED_STRETCH:
const float target_content_length = seq_effect_speed_get_strip_content_length(source);
const float target_strip_length = source->enddisp - source->startdisp;
const float ratio = target_content_length / target_strip_length;
target_frame = frame_index * ratio;
break;
case SEQ_SPEED_MULTIPLY:
FCurve *fcu = seq_effect_speed_speed_factor_curve_get(scene, seq_speed);
if (fcu != NULL) {
seq_effect_speed_frame_map_ensure(scene, seq_speed, fcu);
target_frame = s->frameMap[frame_index];
}
else {
target_frame = frame_index * s->speed_fader;
}
break;
case SEQ_SPEED_LENGTH:
target_frame = seq_effect_speed_get_strip_content_length(source) *
(s->speed_fader_length / 100.0f);
break;
case SEQ_SPEED_FRAME_NUMBER:
target_frame = s->speed_fader_frame_number;
break;
}
CLAMP(target_frame, 0, seq_effect_speed_get_strip_content_length(source));
target_frame += seq_speed->start;
/* No interpolation. */
if ((s->flags & SEQ_SPEED_USE_INTERPOLATION) == 0) {
return seq->start + s->frameMap[frame_index];
return target_frame;
}
/* We need to provide current and next image for interpolation. */
if (input == 0) { /* Current frame. */
return floor(seq->start + s->frameMap[frame_index]);
}
/* Next frame. */
return ceil(seq->start + s->frameMap[frame_index]);
/* Interpolation is used, switch between current and next frame based on which input is
* requested. */
return input == 0 ? target_frame : ceil(target_frame);
}
static float speed_effect_interpolation_ratio_get(SpeedControlVars *s,
Sequence *seq,
static float speed_effect_interpolation_ratio_get(Scene *scene,
Sequence *seq_speed,
float timeline_frame)
{
int frame_index = seq_give_frame_index(seq, timeline_frame);
return s->frameMap[frame_index] - floor(s->frameMap[frame_index]);
const float target_frame = seq_speed_effect_target_frame_get(
scene, seq_speed, timeline_frame, 0);
return target_frame - floor(target_frame);
}
static ImBuf *do_speed_effect(const SeqRenderData *context,
@ -3302,7 +3245,7 @@ static ImBuf *do_speed_effect(const SeqRenderData *context,
if (s->flags & SEQ_SPEED_USE_INTERPOLATION) {
out = prepare_effect_imbufs(context, ibuf1, ibuf2, ibuf3);
facf0 = facf1 = speed_effect_interpolation_ratio_get(s, seq, timeline_frame);
facf0 = facf1 = speed_effect_interpolation_ratio_get(context->scene, seq, timeline_frame);
/* Current frame is ibuf1, next frame is ibuf2. */
out = seq_render_effect_execute_threaded(
&cross_effect, context, NULL, timeline_frame, facf0, facf1, ibuf1, ibuf2, ibuf3);

View File

@ -39,8 +39,8 @@ struct Sequence;
*/
struct SeqEffectHandle seq_effect_get_sequence_blend(struct Sequence *seq);
void seq_effect_speed_rebuild_map(struct Scene *scene, struct Sequence *seq, bool force);
float seq_speed_effect_target_frame_get(const struct SeqRenderData *context,
void seq_effect_speed_rebuild_map(struct Scene *scene, struct Sequence *seq);
float seq_speed_effect_target_frame_get(struct Scene *scene,
struct Sequence *seq,
float timeline_frame,
int input);

View File

@ -840,7 +840,7 @@ static ImBuf *seq_render_effect_strip_impl(const SeqRenderData *context,
for (i = 0; i < 3; i++) {
/* Speed effect requires time remapping of `timeline_frame` for input(s). */
if (input[0] && seq->type == SEQ_TYPE_SPEED) {
float target_frame = seq_speed_effect_target_frame_get(context, seq, timeline_frame, i);
float target_frame = seq_speed_effect_target_frame_get(scene, seq, timeline_frame, i);
ibuf[i] = seq_render_strip(context, state, input[0], target_frame);
}
else { /* Other effects. */

View File

@ -118,7 +118,7 @@ static void sequence_invalidate_cache(Scene *scene,
}
if (seq->effectdata && seq->type == SEQ_TYPE_SPEED) {
seq_effect_speed_rebuild_map(scene, seq, true);
seq_effect_speed_rebuild_map(scene, seq);
}
sequence_do_invalidate_dependent(scene, seq, &ed->seqbase);
@ -268,7 +268,7 @@ void SEQ_relations_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render)
SEQ_relations_sequence_free_anim(seq);
}
if (seq->type == SEQ_TYPE_SPEED) {
seq_effect_speed_rebuild_map(scene, seq, true);
seq_effect_speed_rebuild_map(scene, seq);
}
}
if (seq->type == SEQ_TYPE_META) {
@ -325,7 +325,7 @@ static bool update_changed_seq_recurs(
SEQ_relations_sequence_free_anim(seq);
}
else if (seq->type == SEQ_TYPE_SPEED) {
seq_effect_speed_rebuild_map(scene, seq, true);
seq_effect_speed_rebuild_map(scene, seq);
}
}