Blender V5.0
strip_edit.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 * SPDX-FileCopyrightText: 2003-2009 Blender Authors
3 * SPDX-FileCopyrightText: 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later */
6
10
11#include "DNA_scene_types.h"
12#include "DNA_sequence_types.h"
13
14#include "BLI_listbase.h"
15#include "BLI_math_base.h"
16#include "BLI_string.h"
17#include "BLI_string_utf8.h"
18
19#include "BLT_translation.hh"
20
21#include "BKE_sound.h"
22
23#include "strip_time.hh"
24
25#include "SEQ_add.hh"
26#include "SEQ_animation.hh"
27#include "SEQ_channels.hh"
28#include "SEQ_connect.hh"
29
30#include "SEQ_edit.hh"
31#include "SEQ_effects.hh"
32#include "SEQ_iterator.hh"
33#include "SEQ_relations.hh"
34#include "SEQ_render.hh"
35#include "SEQ_sequencer.hh"
36#include "SEQ_time.hh"
37#include "SEQ_transform.hh"
38#include "SEQ_utils.hh"
39
40#include <cstring>
41
42namespace blender::seq {
43
44bool edit_strip_swap(Scene *scene, Strip *strip_a, Strip *strip_b, const char **r_error_str)
45{
46 char name[sizeof(strip_a->name)];
47
48 if (time_strip_length_get(scene, strip_a) != time_strip_length_get(scene, strip_b)) {
49 *r_error_str = N_("Strips must be the same length");
50 return false;
51 }
52
53 /* type checking, could be more advanced but disallow sound vs non-sound copy */
54 if (strip_a->type != strip_b->type) {
55 if (strip_a->type == STRIP_TYPE_SOUND_RAM || strip_b->type == STRIP_TYPE_SOUND_RAM) {
56 *r_error_str = N_("Strips were not compatible");
57 return false;
58 }
59
60 /* disallow effects to swap with non-effects strips */
61 if (strip_a->is_effect() != strip_b->is_effect()) {
62 *r_error_str = N_("Strips were not compatible");
63 return false;
64 }
65
66 if (strip_a->is_effect() && strip_b->is_effect()) {
67 if (effect_get_num_inputs(strip_a->type) != effect_get_num_inputs(strip_b->type)) {
68 *r_error_str = N_("Strips must have the same number of inputs");
69 return false;
70 }
71 }
72 }
73
74 blender::dna::shallow_swap(*strip_a, *strip_b);
75
76 /* swap back names so animation fcurves don't get swapped */
77 STRNCPY(name, strip_a->name + 2);
78 BLI_strncpy(strip_a->name + 2, strip_b->name + 2, sizeof(strip_b->name) - 2);
79 BLI_strncpy(strip_b->name + 2, name, sizeof(strip_b->name) - 2);
80
81 /* swap back opacity, and overlay mode */
82 std::swap(strip_a->blend_mode, strip_b->blend_mode);
83 std::swap(strip_a->blend_opacity, strip_b->blend_opacity);
84
85 std::swap(strip_a->prev, strip_b->prev);
86 std::swap(strip_a->next, strip_b->next);
87 std::swap(strip_a->start, strip_b->start);
88 std::swap(strip_a->startofs, strip_b->startofs);
89 std::swap(strip_a->endofs, strip_b->endofs);
90 std::swap(strip_a->channel, strip_b->channel);
91 strip_time_effect_range_set(scene, strip_a);
92 strip_time_effect_range_set(scene, strip_b);
93
95
96 return true;
97}
98
100 ListBase *seqbasep,
101 Strip *strip_meta,
102 const bool mute)
103{
104 /* For sound we go over full meta tree to update muted state,
105 * since sound is played outside of evaluating the imbufs. */
106 LISTBASE_FOREACH (Strip *, strip, seqbasep) {
107 bool strip_mute = (mute || render_is_muted(channels, strip));
108
109 if (strip->type == STRIP_TYPE_META) {
110 /* if this is the current meta-strip, unmute because
111 * all strips above this were set to mute */
112 if (strip == strip_meta) {
113 strip_mute = false;
114 }
115
116 strip_update_muting_recursive(&strip->channels, &strip->seqbase, strip_meta, strip_mute);
117 }
118 else if (ELEM(strip->type, STRIP_TYPE_SOUND_RAM, STRIP_TYPE_SCENE)) {
119 if (strip->scene_sound) {
120 BKE_sound_mute_scene_sound(strip->scene_sound, strip_mute);
121 }
122 }
123 }
124}
125
127{
128 if (ed) {
129 /* mute all sounds up to current metastack list */
130 MetaStack *ms = static_cast<MetaStack *>(ed->metastack.last);
131
132 if (ms) {
133 strip_update_muting_recursive(&ed->channels, &ed->seqbase, ms->parent_strip, true);
134 }
135 else {
136 strip_update_muting_recursive(&ed->channels, &ed->seqbase, nullptr, false);
137 }
138 }
139}
140
141static void sequencer_flag_users_for_removal(Scene *scene, ListBase *seqbase, Strip *strip)
142{
143 LISTBASE_FOREACH (Strip *, user_strip, seqbase) {
144 /* Look in meta-strips for usage of strip. */
145 if (user_strip->type == STRIP_TYPE_META) {
146 sequencer_flag_users_for_removal(scene, &user_strip->seqbase, strip);
147 }
148
149 /* Clear strip from modifiers. */
150 LISTBASE_FOREACH (StripModifierData *, smd, &user_strip->modifiers) {
151 if (smd->mask_strip == strip) {
152 smd->mask_strip = nullptr;
153 }
154 }
155
156 /* Mark effects for removal that use the strip. */
157 if (relation_is_effect_of_strip(user_strip, strip)) {
158 user_strip->runtime.flag |= STRIP_MARK_FOR_DELETE;
159 /* Strips can be used as mask even if not in same seqbase. */
160 sequencer_flag_users_for_removal(scene, &scene->ed->seqbase, user_strip);
161 }
162 }
163}
164
165void edit_flag_for_removal(Scene *scene, ListBase *seqbase, Strip *strip)
166{
167 if (strip == nullptr || (strip->runtime.flag & STRIP_MARK_FOR_DELETE) != 0) {
168 return;
169 }
170
171 /* Flag and remove meta children. */
172 if (strip->type == STRIP_TYPE_META) {
173 LISTBASE_FOREACH (Strip *, meta_child, &strip->seqbase) {
174 edit_flag_for_removal(scene, &strip->seqbase, meta_child);
175 }
176 }
177
179 sequencer_flag_users_for_removal(scene, seqbase, strip);
180}
181
183{
184 LISTBASE_FOREACH_MUTABLE (Strip *, strip, seqbase) {
185 if (strip->runtime.flag & STRIP_MARK_FOR_DELETE) {
186 if (strip->type == STRIP_TYPE_META) {
187 edit_remove_flagged_strips(scene, &strip->seqbase);
188 }
189 free_animdata(scene, strip);
190 BLI_remlink(seqbase, strip);
191 strip_free(scene, strip);
193 }
194 }
195}
196
198 ListBase *seqbase,
199 Strip *strip,
200 ListBase *dst_seqbase)
201{
202 /* Move to meta. */
203 BLI_remlink(seqbase, strip);
204 BLI_addtail(dst_seqbase, strip);
205 relations_invalidate_cache(scene, strip);
206
207 /* Update meta. */
208 if (transform_test_overlap(scene, dst_seqbase, strip)) {
209 transform_seqbase_shuffle(dst_seqbase, strip, scene);
210 }
211
212 return true;
213}
214
216 Strip *src_strip,
217 Strip *dst_stripm,
218 const char **r_error_str)
219{
220 /* Find the appropriate seqbase */
221 Editing *ed = editing_get(scene);
222 ListBase *seqbase = get_seqbase_by_strip(scene, src_strip);
223
224 if (dst_stripm->type != STRIP_TYPE_META) {
225 *r_error_str = N_("Cannot move strip to non-meta strip");
226 return false;
227 }
228
229 if (src_strip == dst_stripm) {
230 *r_error_str = N_("Strip cannot be moved into itself");
231 return false;
232 }
233
234 if (seqbase == &dst_stripm->seqbase) {
235 *r_error_str = N_("Moved strip is already inside provided meta strip");
236 return false;
237 }
238
239 if (src_strip->type == STRIP_TYPE_META && exists_in_seqbase(dst_stripm, &src_strip->seqbase)) {
240 *r_error_str = N_("Moved strip is parent of provided meta strip");
241 return false;
242 }
243
244 if (!exists_in_seqbase(dst_stripm, &ed->seqbase)) {
245 *r_error_str = N_("Cannot move strip to different scene");
246 return false;
247 }
248
250 strips.add(src_strip);
251 iterator_set_expand(scene, seqbase, strips, query_strip_effect_chain);
252
253 for (Strip *strip : strips) {
254 /* Move to meta. */
255 edit_move_strip_to_seqbase(scene, seqbase, strip, &dst_stripm->seqbase);
256 }
257
258 time_update_meta_strip_range(scene, dst_stripm);
259
260 return true;
261}
262
264 Scene *scene,
265 Strip *strip,
266 int timeline_frame)
267{
268 const float content_start = time_start_frame_get(strip);
269 const float content_end = time_content_end_frame_get(scene, strip);
270
271 /* Adjust within range of extended still-frames before strip. */
272 if (timeline_frame < content_start) {
273 const float offset = content_start + 1 - timeline_frame;
274 strip->start -= offset;
275 strip->startofs += offset;
276 }
277 /* Adjust within range of strip contents. */
278 else if ((timeline_frame >= content_start) && (timeline_frame <= content_end)) {
279 strip->endofs = 0;
280 const float scene_fps = float(scene->r.frs_sec) / float(scene->r.frs_sec_base);
281 const float speed_factor = time_media_playback_rate_factor_get(strip, scene_fps);
282 strip->anim_endofs += round_fl_to_int((content_end - timeline_frame) * speed_factor);
283 }
284
285 /* Needed only to set `strip->len`. */
286 add_reload_new_file(bmain, scene, strip, false);
287 time_right_handle_frame_set(scene, strip, timeline_frame);
288}
289
291 Scene *scene,
292 Strip *strip,
293 int timeline_frame)
294{
295 const float content_start = time_start_frame_get(strip);
296 const float content_end = time_content_end_frame_get(scene, strip);
297
298 /* Adjust within range of strip contents. */
299 if ((timeline_frame >= content_start) && (timeline_frame <= content_end)) {
300 const float scene_fps = float(scene->r.frs_sec) / float(scene->r.frs_sec_base);
301 const float speed_factor = time_media_playback_rate_factor_get(strip, scene_fps);
302 strip->anim_startofs += round_fl_to_int((timeline_frame - content_start) * speed_factor);
303 strip->start = timeline_frame;
304 strip->startofs = 0;
305 }
306 /* Adjust within range of extended still-frames after strip. */
307 else if (timeline_frame > content_end) {
308 const float offset = timeline_frame - content_end + 1;
309 strip->start += offset;
310 strip->endofs += offset;
311 }
312
313 /* Needed only to set `strip->len`. */
314 add_reload_new_file(bmain, scene, strip, false);
315 time_left_handle_frame_set(scene, strip, timeline_frame);
316}
317
318static bool seq_edit_split_intersect_check(const Scene *scene,
319 const Strip *strip,
320 const int timeline_frame)
321{
322 return timeline_frame > time_left_handle_frame_get(scene, strip) &&
323 timeline_frame < time_right_handle_frame_get(scene, strip);
324}
325
327 Scene *scene,
328 Strip *left_strip,
329 Strip *right_strip,
330 const int timeline_frame,
331 const eSplitMethod method)
332{
333 if (seq_edit_split_intersect_check(scene, right_strip, timeline_frame)) {
334 switch (method) {
335 case SPLIT_SOFT:
336 time_left_handle_frame_set(scene, right_strip, timeline_frame);
337 break;
338 case SPLIT_HARD:
339 seq_split_set_left_hold_offset(bmain, scene, right_strip, timeline_frame);
340 break;
341 }
342 }
343
344 if (seq_edit_split_intersect_check(scene, left_strip, timeline_frame)) {
345 switch (method) {
346 case SPLIT_SOFT:
347 time_right_handle_frame_set(scene, left_strip, timeline_frame);
348 break;
349 case SPLIT_HARD:
350 seq_split_set_right_hold_offset(bmain, scene, left_strip, timeline_frame);
351 break;
352 }
353 }
354}
355
357 const Strip *strip,
358 const int timeline_frame)
359{
360 bool input_does_intersect = false;
361 if (strip->input1) {
362 input_does_intersect |= seq_edit_split_intersect_check(scene, strip->input1, timeline_frame);
363 if (strip->input1->is_effect()) {
364 input_does_intersect |= seq_edit_split_effect_inputs_intersect(
365 scene, strip->input1, timeline_frame);
366 }
367 }
368 if (strip->input2) {
369 input_does_intersect |= seq_edit_split_intersect_check(scene, strip->input2, timeline_frame);
370 if (strip->input2->is_effect()) {
371 input_does_intersect |= seq_edit_split_effect_inputs_intersect(
372 scene, strip->input2, timeline_frame);
373 }
374 }
375 return input_does_intersect;
376}
377
380 const int timeline_frame,
381 const char **r_error)
382{
383 for (Strip *strip : strips) {
384 ListBase *channels = channels_displayed_get(editing_get(scene));
385 if (transform_is_locked(channels, strip)) {
386 *r_error = "Strip is locked.";
387 return false;
388 }
389 if (!strip->is_effect()) {
390 continue;
391 }
392 if (!seq_edit_split_intersect_check(scene, strip, timeline_frame)) {
393 continue;
394 }
395 if (effect_get_num_inputs(strip->type) <= 1) {
396 continue;
397 }
399 *r_error = "Splitting transition effect is not permitted.";
400 return false;
401 }
402 if (!seq_edit_split_effect_inputs_intersect(scene, strip, timeline_frame)) {
403 *r_error = "Effect inputs don't overlap. Can not split such effect.";
404 return false;
405 }
406 }
407 return true;
408}
409
411 Scene *scene,
412 ListBase *seqbase,
413 Strip *strip,
414 const int timeline_frame,
415 const eSplitMethod method,
416 const bool ignore_connections,
417 const char **r_error)
418{
419 if (!seq_edit_split_intersect_check(scene, strip, timeline_frame)) {
420 return nullptr;
421 }
422
423 /* Whole strip effect chain must be duplicated in order to preserve relationships. */
425 strips.add(strip);
427 seqbase,
428 strips,
429 ignore_connections ? query_strip_effect_chain :
431
432 if (!seq_edit_split_operation_permitted_check(scene, strips, timeline_frame, r_error)) {
433 return nullptr;
434 }
435
436 /* Store `F-Curves`, so original ones aren't renamed. */
437 AnimationBackup animation_backup{};
438 animation_backup_original(scene, &animation_backup);
439
440 ListBase left_strips = {nullptr, nullptr};
441 for (Strip *strip_iter : strips) {
442 /* Move strips in collection from seqbase to new ListBase. */
443 BLI_remlink(seqbase, strip_iter);
444 BLI_addtail(&left_strips, strip_iter);
445
446 if (ignore_connections) {
447 seq::disconnect(strip_iter);
448 }
449
450 /* Duplicate curves from backup, so they can be renamed along with split strips. */
451 animation_duplicate_backup_to_scene(scene, strip_iter, &animation_backup);
452 }
453
454 /* Duplicate ListBase. */
455 ListBase right_strips = {nullptr, nullptr};
457 bmain, scene, scene, &right_strips, &left_strips, StripDuplicate::All, 0);
458
459 Strip *left_strip = static_cast<Strip *>(left_strips.first);
460 Strip *right_strip = static_cast<Strip *>(right_strips.first);
461 Strip *return_strip = nullptr;
462
463 /* Move strips from detached `ListBase`, otherwise they can't be flagged for removal. */
464 BLI_movelisttolist(seqbase, &left_strips);
465 BLI_movelisttolist(seqbase, &right_strips);
466
467 /* Rename duplicated strips. This has to be done immediately after adding
468 * strips to seqbase, for lookup cache to work correctly. */
469 Strip *strip_rename = right_strip;
470 for (; strip_rename; strip_rename = strip_rename->next) {
471 ensure_unique_name(strip_rename, scene);
472 }
473
474 /* Split strips. */
475 while (left_strip && right_strip) {
476 if (time_left_handle_frame_get(scene, left_strip) >= timeline_frame) {
477 edit_flag_for_removal(scene, seqbase, left_strip);
478 }
479 else if (time_right_handle_frame_get(scene, right_strip) <= timeline_frame) {
480 edit_flag_for_removal(scene, seqbase, right_strip);
481 }
482 else if (return_strip == nullptr) {
483 /* Store return value - pointer to strip that will not be removed. */
484 return_strip = right_strip;
485 }
486
488 bmain, scene, left_strip, right_strip, timeline_frame, method);
489 left_strip = left_strip->next;
490 right_strip = right_strip->next;
491 }
492
493 edit_remove_flagged_strips(scene, seqbase);
494 animation_restore_original(scene, &animation_backup);
495
496 return return_strip;
497}
498
500 ListBase *seqbase,
501 const int initial_frame,
502 const bool remove_all_gaps)
503{
504 GapInfo gap_info = {0};
505 seq_time_gap_info_get(scene, seqbase, initial_frame, &gap_info);
506
507 if (!gap_info.gap_exists) {
508 return false;
509 }
510
511 if (remove_all_gaps) {
512 while (gap_info.gap_exists) {
513 transform_offset_after_frame(scene, seqbase, -gap_info.gap_length, gap_info.gap_start_frame);
514 seq_time_gap_info_get(scene, seqbase, initial_frame, &gap_info);
515 }
516 }
517 else {
518 transform_offset_after_frame(scene, seqbase, -gap_info.gap_length, gap_info.gap_start_frame);
519 }
520 return true;
521}
522
523void edit_strip_name_set(Scene *scene, Strip *strip, const char *new_name)
524{
525 BLI_strncpy_utf8(strip->name + 2, new_name, MAX_NAME - 2);
526 BLI_str_utf8_invalid_strip(strip->name + 2, strlen(strip->name + 2));
528}
529
530} // namespace blender::seq
void BKE_sound_mute_scene_sound(void *handle, bool mute)
void void void BLI_movelisttolist(ListBase *dst, ListBase *src) ATTR_NONNULL(1
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
MINLINE int round_fl_to_int(float a)
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
char * BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
int BLI_str_utf8_invalid_strip(char *str, size_t str_len) ATTR_NONNULL(1)
#define ELEM(...)
#define MAX_NAME
Definition DNA_defs.h:50
@ STRIP_MARK_FOR_DELETE
@ STRIP_TYPE_GAMCROSS
@ STRIP_TYPE_SCENE
@ STRIP_TYPE_WIPE
@ STRIP_TYPE_SOUND_RAM
@ STRIP_TYPE_META
@ STRIP_TYPE_CROSS
bool add(const Key &key)
nullptr float
bool render_is_muted(const ListBase *channels, const Strip *strip)
Definition render.cc:2110
int time_right_handle_frame_get(const Scene *scene, const Strip *strip)
bool disconnect(Strip *strip)
void relations_invalidate_cache(Scene *scene, Strip *strip)
static void seq_split_set_left_hold_offset(Main *bmain, Scene *scene, Strip *strip, int timeline_frame)
ListBase * channels_displayed_get(const Editing *ed)
Definition channels.cc:28
void edit_remove_flagged_strips(Scene *scene, ListBase *seqbase)
float time_content_end_frame_get(const Scene *scene, const Strip *strip)
void edit_flag_for_removal(Scene *scene, ListBase *seqbase, Strip *strip)
float time_media_playback_rate_factor_get(const Strip *strip, const float scene_fps)
Definition strip_time.cc:41
bool transform_test_overlap(const Scene *scene, Strip *strip1, Strip *strip2)
Editing * editing_get(const Scene *scene)
Definition sequencer.cc:286
ListBase * get_seqbase_by_strip(const Scene *scene, Strip *strip)
void edit_strip_name_set(Scene *scene, Strip *strip, const char *new_name)
int time_left_handle_frame_get(const Scene *, const Strip *strip)
void animation_duplicate_backup_to_scene(Scene *scene, Strip *strip, AnimationBackup *backup)
Definition animation.cc:205
void seq_time_gap_info_get(const Scene *scene, ListBase *seqbase, const int initial_frame, GapInfo *r_gap_info)
void strip_free(Scene *scene, Strip *strip)
Definition sequencer.cc:269
float time_start_frame_get(const Strip *strip)
void query_strip_effect_chain(const Scene *scene, Strip *reference_strip, ListBase *seqbase, VectorSet< Strip * > &r_strips)
Definition iterator.cc:254
int time_strip_length_get(const Scene *scene, const Strip *strip)
static void seq_split_set_right_hold_offset(Main *bmain, Scene *scene, Strip *strip, int timeline_frame)
void add_reload_new_file(Main *bmain, Scene *scene, Strip *strip, const bool lock_range)
Definition strip_add.cc:538
void time_update_meta_strip_range(const Scene *scene, Strip *strip_meta)
void free_animdata(Scene *scene, Strip *strip)
Definition animation.cc:74
bool edit_move_strip_to_meta(Scene *scene, Strip *src_strip, Strip *dst_stripm, const char **r_error_str)
void iterator_set_expand(const Scene *scene, ListBase *seqbase, VectorSet< Strip * > &strips, void strip_query_func(const Scene *scene, Strip *strip_reference, ListBase *seqbase, VectorSet< Strip * > &strips))
Definition iterator.cc:82
void transform_offset_after_frame(Scene *scene, ListBase *seqbase, const int delta, const int timeline_frame)
void edit_update_muting(Editing *ed)
static bool seq_edit_split_intersect_check(const Scene *scene, const Strip *strip, const int timeline_frame)
static void strip_update_muting_recursive(ListBase *channels, ListBase *seqbasep, Strip *strip_meta, const bool mute)
Definition strip_edit.cc:99
static bool seq_edit_split_operation_permitted_check(const Scene *scene, blender::Span< Strip * > strips, const int timeline_frame, const char **r_error)
void animation_backup_original(Scene *scene, AnimationBackup *backup)
Definition animation.cc:91
static void seq_edit_split_handle_strip_offsets(Main *bmain, Scene *scene, Strip *left_strip, Strip *right_strip, const int timeline_frame, const eSplitMethod method)
static void sequencer_flag_users_for_removal(Scene *scene, ListBase *seqbase, Strip *strip)
bool edit_strip_swap(Scene *scene, Strip *strip_a, Strip *strip_b, const char **r_error_str)
Definition strip_edit.cc:44
bool transform_is_locked(ListBase *channels, const Strip *strip)
bool relation_is_effect_of_strip(const Strip *effect, const Strip *input)
void animation_restore_original(Scene *scene, AnimationBackup *backup)
Definition animation.cc:113
static bool seq_edit_split_effect_inputs_intersect(const Scene *scene, const Strip *strip, const int timeline_frame)
void time_left_handle_frame_set(const Scene *scene, Strip *strip, int timeline_frame)
void query_strip_connected_and_effect_chain(const Scene *scene, Strip *reference_strip, ListBase *seqbase, VectorSet< Strip * > &r_strips)
Definition iterator.cc:283
void time_right_handle_frame_set(const Scene *scene, Strip *strip, int timeline_frame)
Strip * edit_strip_split(Main *bmain, Scene *scene, ListBase *seqbase, Strip *strip, const int timeline_frame, const eSplitMethod method, const bool ignore_connections, const char **r_error)
void strip_lookup_invalidate(const Editing *ed)
void ensure_unique_name(Strip *strip, Scene *scene)
bool edit_remove_gaps(Scene *scene, ListBase *seqbase, const int initial_frame, const bool remove_all_gaps)
bool transform_seqbase_shuffle(ListBase *seqbasep, Strip *test, Scene *evil_scene)
bool exists_in_seqbase(const Strip *strip, const ListBase *seqbase)
void seqbase_duplicate_recursive(Main *bmain, const Scene *scene_src, Scene *scene_dst, ListBase *nseqbase, const ListBase *seqbase, const StripDuplicate dupe_flag, const int flag)
Definition sequencer.cc:819
void strip_time_effect_range_set(const Scene *scene, Strip *strip)
bool edit_move_strip_to_seqbase(Scene *scene, ListBase *seqbase, Strip *strip, ListBase *dst_seqbase)
int effect_get_num_inputs(int strip_type)
Definition effects.cc:327
const char * name
ListBase seqbase
void * first
struct Editing * ed
struct RenderData r
struct Strip * input1
struct Strip * prev
ListBase seqbase
StripRuntime runtime
struct Strip * next
float blend_opacity
char name[64]
struct Strip * input2
#define N_(msgid)