This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/sequencer/intern/sequencer.c
Richard Antalik e4289f2360 Fix errors in sequencer channel headers
Failing constraint test uncovered crashes on NULL dereference and
missing channels initialization in `SEQ_editing_ensure()`.
2022-04-04 16:30:16 +02:00

943 lines
26 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2001-2002 NaN Holding BV. All rights reserved.
* 2003-2009 Blender Foundation.
* 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de> */
/** \file
* \ingroup bke
*/
#define DNA_DEPRECATED_ALLOW
#include "MEM_guardedalloc.h"
#include "DNA_anim_types.h"
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
#include "DNA_sound_types.h"
#include "BLI_listbase.h"
#include "BKE_idprop.h"
#include "BKE_lib_id.h"
#include "BKE_sound.h"
#include "DEG_depsgraph.h"
#include "IMB_colormanagement.h"
#include "IMB_imbuf.h"
#include "SEQ_channels.h"
#include "SEQ_edit.h"
#include "SEQ_effects.h"
#include "SEQ_iterator.h"
#include "SEQ_modifier.h"
#include "SEQ_proxy.h"
#include "SEQ_relations.h"
#include "SEQ_select.h"
#include "SEQ_sequencer.h"
#include "SEQ_sound.h"
#include "SEQ_utils.h"
#include "BLO_read_write.h"
#include "image_cache.h"
#include "prefetch.h"
#include "sequencer.h"
#include "utils.h"
/* -------------------------------------------------------------------- */
/** \name Allocate / Free Functions
* \{ */
StripProxy *seq_strip_proxy_alloc(void)
{
StripProxy *strip_proxy = MEM_callocN(sizeof(struct StripProxy), "StripProxy");
strip_proxy->quality = 50;
strip_proxy->build_tc_flags = SEQ_PROXY_TC_ALL;
strip_proxy->tc = SEQ_PROXY_TC_RECORD_RUN;
return strip_proxy;
}
static Strip *seq_strip_alloc(int type)
{
Strip *strip = MEM_callocN(sizeof(Strip), "strip");
if (ELEM(type, SEQ_TYPE_SOUND_RAM, SEQ_TYPE_SOUND_HD) == 0) {
strip->transform = MEM_callocN(sizeof(struct StripTransform), "StripTransform");
strip->transform->scale_x = 1;
strip->transform->scale_y = 1;
strip->transform->origin[0] = 0.5f;
strip->transform->origin[1] = 0.5f;
strip->transform->filter = SEQ_TRANSFORM_FILTER_BILINEAR;
strip->crop = MEM_callocN(sizeof(struct StripCrop), "StripCrop");
}
strip->us = 1;
return strip;
}
static void seq_free_strip(Strip *strip)
{
strip->us--;
if (strip->us > 0) {
return;
}
if (strip->us < 0) {
printf("error: negative users in strip\n");
return;
}
if (strip->stripdata) {
MEM_freeN(strip->stripdata);
}
if (strip->proxy) {
if (strip->proxy->anim) {
IMB_free_anim(strip->proxy->anim);
}
MEM_freeN(strip->proxy);
}
if (strip->crop) {
MEM_freeN(strip->crop);
}
if (strip->transform) {
MEM_freeN(strip->transform);
}
MEM_freeN(strip);
}
Sequence *SEQ_sequence_alloc(ListBase *lb, int timeline_frame, int machine, int type)
{
Sequence *seq;
seq = MEM_callocN(sizeof(Sequence), "addseq");
BLI_addtail(lb, seq);
*((short *)seq->name) = ID_SEQ;
seq->name[2] = 0;
seq->flag = SELECT;
seq->start = timeline_frame;
seq->machine = machine;
seq->sat = 1.0;
seq->mul = 1.0;
seq->blend_opacity = 100.0;
seq->volume = 1.0f;
seq->pitch = 1.0f;
seq->scene_sound = NULL;
seq->type = type;
seq->blend_mode = SEQ_TYPE_ALPHAOVER;
seq->strip = seq_strip_alloc(type);
seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Sequence Stereo Format");
seq->color_tag = SEQUENCE_COLOR_NONE;
if (seq->type == SEQ_TYPE_META) {
SEQ_channels_ensure(&seq->channels);
}
SEQ_relations_session_uuid_generate(seq);
return seq;
}
/* only give option to skip cache locally (static func) */
static void seq_sequence_free_ex(Scene *scene,
Sequence *seq,
const bool do_cache,
const bool do_id_user)
{
if (seq->strip) {
seq_free_strip(seq->strip);
}
SEQ_relations_sequence_free_anim(seq);
if (seq->type & SEQ_TYPE_EFFECT) {
struct SeqEffectHandle sh = SEQ_effect_handle_get(seq);
sh.free(seq, do_id_user);
}
if (seq->sound && do_id_user) {
id_us_min(((ID *)seq->sound));
}
if (seq->stereo3d_format) {
MEM_freeN(seq->stereo3d_format);
}
/* clipboard has no scene and will never have a sound handle or be active
* same goes to sequences copy for proxy rebuild job
*/
if (scene) {
Editing *ed = scene->ed;
if (ed->act_seq == seq) {
ed->act_seq = NULL;
}
if (seq->scene_sound && ELEM(seq->type, SEQ_TYPE_SOUND_RAM, SEQ_TYPE_SCENE)) {
BKE_sound_remove_scene_sound(scene, seq->scene_sound);
}
}
if (seq->prop) {
IDP_FreePropertyContent_ex(seq->prop, do_id_user);
MEM_freeN(seq->prop);
}
/* free modifiers */
SEQ_modifier_clear(seq);
/* free cached data used by this strip,
* also invalidate cache for all dependent sequences
*
* be _very_ careful here, invalidating cache loops over the scene sequences and
* assumes the listbase is valid for all strips,
* this may not be the case if lists are being freed.
* this is optional SEQ_relations_invalidate_cache
*/
if (do_cache) {
if (scene) {
SEQ_relations_invalidate_cache_raw(scene, seq);
}
}
if (seq->type == SEQ_TYPE_META) {
SEQ_channels_free(&seq->channels);
}
MEM_freeN(seq);
}
void SEQ_sequence_free(Scene *scene, Sequence *seq)
{
seq_sequence_free_ex(scene, seq, true, true);
}
void seq_free_sequence_recurse(Scene *scene, Sequence *seq, const bool do_id_user)
{
Sequence *iseq, *iseq_next;
for (iseq = seq->seqbase.first; iseq; iseq = iseq_next) {
iseq_next = iseq->next;
seq_free_sequence_recurse(scene, iseq, do_id_user);
}
seq_sequence_free_ex(scene, seq, false, do_id_user);
}
Editing *SEQ_editing_get(const Scene *scene)
{
return scene->ed;
}
Editing *SEQ_editing_ensure(Scene *scene)
{
if (scene->ed == NULL) {
Editing *ed;
ed = scene->ed = MEM_callocN(sizeof(Editing), "addseq");
ed->seqbasep = &ed->seqbase;
ed->cache = NULL;
ed->cache_flag = SEQ_CACHE_STORE_FINAL_OUT;
ed->cache_flag |= SEQ_CACHE_STORE_RAW;
ed->displayed_channels = &ed->channels;
SEQ_channels_ensure(ed->displayed_channels);
}
return scene->ed;
}
void SEQ_editing_free(Scene *scene, const bool do_id_user)
{
Editing *ed = scene->ed;
if (ed == NULL) {
return;
}
seq_prefetch_free(scene);
seq_cache_destruct(scene);
/* handle cache freeing above */
LISTBASE_FOREACH_MUTABLE (Sequence *, seq, &ed->seqbase) {
seq_free_sequence_recurse(scene, seq, do_id_user);
}
BLI_freelistN(&ed->metastack);
SEQ_sequence_lookup_free(scene);
SEQ_channels_free(&ed->channels);
MEM_freeN(ed);
scene->ed = NULL;
}
static void seq_new_fix_links_recursive(Sequence *seq)
{
SequenceModifierData *smd;
if (seq->type & SEQ_TYPE_EFFECT) {
if (seq->seq1 && seq->seq1->tmp) {
seq->seq1 = seq->seq1->tmp;
}
if (seq->seq2 && seq->seq2->tmp) {
seq->seq2 = seq->seq2->tmp;
}
if (seq->seq3 && seq->seq3->tmp) {
seq->seq3 = seq->seq3->tmp;
}
}
else if (seq->type == SEQ_TYPE_META) {
Sequence *seqn;
for (seqn = seq->seqbase.first; seqn; seqn = seqn->next) {
seq_new_fix_links_recursive(seqn);
}
}
for (smd = seq->modifiers.first; smd; smd = smd->next) {
if (smd->mask_sequence && smd->mask_sequence->tmp) {
smd->mask_sequence = smd->mask_sequence->tmp;
}
}
}
SequencerToolSettings *SEQ_tool_settings_init(void)
{
SequencerToolSettings *tool_settings = MEM_callocN(sizeof(SequencerToolSettings),
"Sequencer tool settings");
tool_settings->fit_method = SEQ_SCALE_TO_FIT;
tool_settings->snap_mode = SEQ_SNAP_TO_STRIPS | SEQ_SNAP_TO_CURRENT_FRAME |
SEQ_SNAP_TO_STRIP_HOLD;
tool_settings->snap_distance = 15;
tool_settings->overlap_mode = SEQ_OVERLAP_SHUFFLE;
tool_settings->pivot_point = V3D_AROUND_LOCAL_ORIGINS;
return tool_settings;
}
SequencerToolSettings *SEQ_tool_settings_ensure(Scene *scene)
{
SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings;
if (tool_settings == NULL) {
scene->toolsettings->sequencer_tool_settings = SEQ_tool_settings_init();
tool_settings = scene->toolsettings->sequencer_tool_settings;
}
return tool_settings;
}
void SEQ_tool_settings_free(SequencerToolSettings *tool_settings)
{
MEM_freeN(tool_settings);
}
eSeqImageFitMethod SEQ_tool_settings_fit_method_get(Scene *scene)
{
const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene);
return tool_settings->fit_method;
}
short SEQ_tool_settings_snap_mode_get(Scene *scene)
{
const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene);
return tool_settings->snap_mode;
}
short SEQ_tool_settings_snap_flag_get(Scene *scene)
{
const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene);
return tool_settings->snap_flag;
}
int SEQ_tool_settings_snap_distance_get(Scene *scene)
{
const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene);
return tool_settings->snap_distance;
}
void SEQ_tool_settings_fit_method_set(Scene *scene, eSeqImageFitMethod fit_method)
{
SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene);
tool_settings->fit_method = fit_method;
}
eSeqOverlapMode SEQ_tool_settings_overlap_mode_get(Scene *scene)
{
const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene);
return tool_settings->overlap_mode;
}
int SEQ_tool_settings_pivot_point_get(Scene *scene)
{
const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene);
return tool_settings->pivot_point;
}
ListBase *SEQ_active_seqbase_get(const Editing *ed)
{
if (ed == NULL) {
return NULL;
}
return ed->seqbasep;
}
void SEQ_seqbase_active_set(Editing *ed, ListBase *seqbase)
{
ed->seqbasep = seqbase;
}
MetaStack *SEQ_meta_stack_alloc(Editing *ed, Sequence *seq_meta)
{
MetaStack *ms = MEM_mallocN(sizeof(MetaStack), "metastack");
BLI_addtail(&ed->metastack, ms);
ms->parseq = seq_meta;
ms->oldbasep = ed->seqbasep;
ms->old_channels = ed->displayed_channels;
copy_v2_v2_int(ms->disp_range, &ms->parseq->startdisp);
return ms;
}
void SEQ_meta_stack_free(Editing *ed, MetaStack *ms)
{
BLI_remlink(&ed->metastack, ms);
MEM_freeN(ms);
}
MetaStack *SEQ_meta_stack_active_get(const Editing *ed)
{
if (ed == NULL) {
return NULL;
}
return ed->metastack.last;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Duplicate Functions
* \{ */
static Sequence *seq_dupli(const Scene *scene_src,
Scene *scene_dst,
ListBase *new_seq_list,
Sequence *seq,
int dupe_flag,
const int flag)
{
Sequence *seqn = MEM_dupallocN(seq);
if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) {
SEQ_relations_session_uuid_generate(seqn);
}
seq->tmp = seqn;
seqn->strip = MEM_dupallocN(seq->strip);
seqn->stereo3d_format = MEM_dupallocN(seq->stereo3d_format);
/* XXX: add F-Curve duplication stuff? */
if (seq->strip->crop) {
seqn->strip->crop = MEM_dupallocN(seq->strip->crop);
}
if (seq->strip->transform) {
seqn->strip->transform = MEM_dupallocN(seq->strip->transform);
}
if (seq->strip->proxy) {
seqn->strip->proxy = MEM_dupallocN(seq->strip->proxy);
seqn->strip->proxy->anim = NULL;
}
if (seq->prop) {
seqn->prop = IDP_CopyProperty_ex(seq->prop, flag);
}
if (seqn->modifiers.first) {
BLI_listbase_clear(&seqn->modifiers);
SEQ_modifier_list_copy(seqn, seq);
}
if (seq->type == SEQ_TYPE_META) {
seqn->strip->stripdata = NULL;
BLI_listbase_clear(&seqn->seqbase);
/* WARNING: This meta-strip is not recursively duplicated here - do this after! */
// seq_dupli_recursive(&seq->seqbase, &seqn->seqbase);
BLI_listbase_clear(&seqn->channels);
SEQ_channels_duplicate(&seqn->channels, &seq->channels);
}
else if (seq->type == SEQ_TYPE_SCENE) {
seqn->strip->stripdata = NULL;
if (seq->scene_sound) {
seqn->scene_sound = BKE_sound_scene_add_scene_sound_defaults(scene_dst, seqn);
}
}
else if (seq->type == SEQ_TYPE_MOVIECLIP) {
/* avoid assert */
}
else if (seq->type == SEQ_TYPE_MASK) {
/* avoid assert */
}
else if (seq->type == SEQ_TYPE_MOVIE) {
seqn->strip->stripdata = MEM_dupallocN(seq->strip->stripdata);
BLI_listbase_clear(&seqn->anims);
}
else if (seq->type == SEQ_TYPE_SOUND_RAM) {
seqn->strip->stripdata = MEM_dupallocN(seq->strip->stripdata);
seqn->scene_sound = NULL;
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
id_us_plus((ID *)seqn->sound);
}
}
else if (seq->type == SEQ_TYPE_IMAGE) {
seqn->strip->stripdata = MEM_dupallocN(seq->strip->stripdata);
}
else if (seq->type & SEQ_TYPE_EFFECT) {
struct SeqEffectHandle sh;
sh = SEQ_effect_handle_get(seq);
if (sh.copy) {
sh.copy(seqn, seq, flag);
}
seqn->strip->stripdata = NULL;
}
else {
/* sequence type not handled in duplicate! Expect a crash now... */
BLI_assert_unreachable();
}
/* When using SEQ_DUPE_UNIQUE_NAME, it is mandatory to add new sequences in relevant container
* (scene or meta's one), *before* checking for unique names. Otherwise the meta's list is empty
* and hence we miss all seqs in that meta that have already been duplicated (see T55668).
* Note that unique name check itself could be done at a later step in calling code, once all
* seqs have bee duplicated (that was first, simpler solution), but then handling of animation
* data will be broken (see T60194). */
if (new_seq_list != NULL) {
BLI_addtail(new_seq_list, seqn);
}
if (scene_src == scene_dst) {
if (dupe_flag & SEQ_DUPE_UNIQUE_NAME) {
SEQ_sequence_base_unique_name_recursive(scene_dst, &scene_dst->ed->seqbase, seqn);
}
}
return seqn;
}
static Sequence *sequence_dupli_recursive_do(const Scene *scene_src,
Scene *scene_dst,
ListBase *new_seq_list,
Sequence *seq,
const int dupe_flag)
{
Sequence *seqn;
seq->tmp = NULL;
seqn = seq_dupli(scene_src, scene_dst, new_seq_list, seq, dupe_flag, 0);
if (seq->type == SEQ_TYPE_META) {
Sequence *s;
for (s = seq->seqbase.first; s; s = s->next) {
sequence_dupli_recursive_do(scene_src, scene_dst, &seqn->seqbase, s, dupe_flag);
}
}
return seqn;
}
Sequence *SEQ_sequence_dupli_recursive(
const Scene *scene_src, Scene *scene_dst, ListBase *new_seq_list, Sequence *seq, int dupe_flag)
{
Sequence *seqn = sequence_dupli_recursive_do(scene_src, scene_dst, new_seq_list, seq, dupe_flag);
/* This does not need to be in recursive call itself, since it is already recursive... */
seq_new_fix_links_recursive(seqn);
return seqn;
}
void SEQ_sequence_base_dupli_recursive(const Scene *scene_src,
Scene *scene_dst,
ListBase *nseqbase,
const ListBase *seqbase,
int dupe_flag,
const int flag)
{
Sequence *seq;
Sequence *seqn = NULL;
for (seq = seqbase->first; seq; seq = seq->next) {
seq->tmp = NULL;
if ((seq->flag & SELECT) || (dupe_flag & SEQ_DUPE_ALL)) {
seqn = seq_dupli(scene_src, scene_dst, nseqbase, seq, dupe_flag, flag);
if (seqn == NULL) {
continue; /* Should never fail. */
}
if (seq->type == SEQ_TYPE_META) {
/* Always include meta all strip children. */
int dupe_flag_recursive = dupe_flag | SEQ_DUPE_ALL | SEQ_DUPE_IS_RECURSIVE_CALL;
SEQ_sequence_base_dupli_recursive(
scene_src, scene_dst, &seqn->seqbase, &seq->seqbase, dupe_flag_recursive, flag);
}
}
}
/* Fix modifier links recursively from the top level only, when all sequences have been
* copied. */
if (dupe_flag & SEQ_DUPE_IS_RECURSIVE_CALL) {
return;
}
/* fix modifier linking */
for (seq = nseqbase->first; seq; seq = seq->next) {
seq_new_fix_links_recursive(seq);
}
}
bool SEQ_valid_strip_channel(Sequence *seq)
{
if (seq->machine < 1) {
return false;
}
if (seq->machine > MAXSEQ) {
return false;
}
return true;
}
SequencerToolSettings *SEQ_tool_settings_copy(SequencerToolSettings *tool_settings)
{
SequencerToolSettings *tool_settings_copy = MEM_dupallocN(tool_settings);
return tool_settings_copy;
}
/** \} */
static bool seq_set_strip_done_cb(Sequence *seq, void *UNUSED(userdata))
{
if (seq->strip) {
seq->strip->done = false;
}
return true;
}
static bool seq_write_data_cb(Sequence *seq, void *userdata)
{
BlendWriter *writer = (BlendWriter *)userdata;
BLO_write_struct(writer, Sequence, seq);
if (seq->strip && seq->strip->done == 0) {
/* Write strip with 'done' at 0 because read-file. */
/* TODO this doesn't depend on the `Strip` data to be present? */
if (seq->effectdata) {
switch (seq->type) {
case SEQ_TYPE_COLOR:
BLO_write_struct(writer, SolidColorVars, seq->effectdata);
break;
case SEQ_TYPE_SPEED:
BLO_write_struct(writer, SpeedControlVars, seq->effectdata);
break;
case SEQ_TYPE_WIPE:
BLO_write_struct(writer, WipeVars, seq->effectdata);
break;
case SEQ_TYPE_GLOW:
BLO_write_struct(writer, GlowVars, seq->effectdata);
break;
case SEQ_TYPE_TRANSFORM:
BLO_write_struct(writer, TransformVars, seq->effectdata);
break;
case SEQ_TYPE_GAUSSIAN_BLUR:
BLO_write_struct(writer, GaussianBlurVars, seq->effectdata);
break;
case SEQ_TYPE_TEXT:
BLO_write_struct(writer, TextVars, seq->effectdata);
break;
case SEQ_TYPE_COLORMIX:
BLO_write_struct(writer, ColorMixVars, seq->effectdata);
break;
}
}
BLO_write_struct(writer, Stereo3dFormat, seq->stereo3d_format);
Strip *strip = seq->strip;
BLO_write_struct(writer, Strip, strip);
if (strip->crop) {
BLO_write_struct(writer, StripCrop, strip->crop);
}
if (strip->transform) {
BLO_write_struct(writer, StripTransform, strip->transform);
}
if (strip->proxy) {
BLO_write_struct(writer, StripProxy, strip->proxy);
}
if (seq->type == SEQ_TYPE_IMAGE) {
BLO_write_struct_array(writer,
StripElem,
MEM_allocN_len(strip->stripdata) / sizeof(struct StripElem),
strip->stripdata);
}
else if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_SOUND_RAM, SEQ_TYPE_SOUND_HD)) {
BLO_write_struct(writer, StripElem, strip->stripdata);
}
strip->done = true;
}
if (seq->prop) {
IDP_BlendWrite(writer, seq->prop);
}
SEQ_modifier_blend_write(writer, &seq->modifiers);
LISTBASE_FOREACH (SeqTimelineChannel *, channel, &seq->channels) {
BLO_write_struct(writer, SeqTimelineChannel, channel);
}
return true;
}
void SEQ_blend_write(BlendWriter *writer, ListBase *seqbase)
{
/* reset write flags */
SEQ_for_each_callback(seqbase, seq_set_strip_done_cb, NULL);
SEQ_for_each_callback(seqbase, seq_write_data_cb, writer);
}
static bool seq_read_data_cb(Sequence *seq, void *user_data)
{
BlendDataReader *reader = (BlendDataReader *)user_data;
/* Do as early as possible, so that other parts of reading can rely on valid session UUID. */
SEQ_relations_session_uuid_generate(seq);
BLO_read_data_address(reader, &seq->seq1);
BLO_read_data_address(reader, &seq->seq2);
BLO_read_data_address(reader, &seq->seq3);
/* a patch: after introduction of effects with 3 input strips */
if (seq->seq3 == NULL) {
seq->seq3 = seq->seq2;
}
BLO_read_data_address(reader, &seq->effectdata);
BLO_read_data_address(reader, &seq->stereo3d_format);
if (seq->type & SEQ_TYPE_EFFECT) {
seq->flag |= SEQ_EFFECT_NOT_LOADED;
}
if (seq->type == SEQ_TYPE_TEXT) {
TextVars *t = seq->effectdata;
t->text_blf_id = SEQ_FONT_NOT_LOADED;
}
BLO_read_data_address(reader, &seq->prop);
IDP_BlendDataRead(reader, &seq->prop);
BLO_read_data_address(reader, &seq->strip);
if (seq->strip && seq->strip->done == 0) {
seq->strip->done = true;
if (ELEM(seq->type, SEQ_TYPE_IMAGE, SEQ_TYPE_MOVIE, SEQ_TYPE_SOUND_RAM, SEQ_TYPE_SOUND_HD)) {
BLO_read_data_address(reader, &seq->strip->stripdata);
}
else {
seq->strip->stripdata = NULL;
}
BLO_read_data_address(reader, &seq->strip->crop);
BLO_read_data_address(reader, &seq->strip->transform);
BLO_read_data_address(reader, &seq->strip->proxy);
if (seq->strip->proxy) {
seq->strip->proxy->anim = NULL;
}
else if (seq->flag & SEQ_USE_PROXY) {
SEQ_proxy_set(seq, true);
}
/* need to load color balance to it could be converted to modifier */
BLO_read_data_address(reader, &seq->strip->color_balance);
}
SEQ_modifier_blend_read_data(reader, &seq->modifiers);
BLO_read_list(reader, &seq->channels);
return true;
}
void SEQ_blend_read(BlendDataReader *reader, ListBase *seqbase)
{
SEQ_for_each_callback(seqbase, seq_read_data_cb, reader);
}
typedef struct Read_lib_data {
BlendLibReader *reader;
Scene *scene;
} Read_lib_data;
static bool seq_read_lib_cb(Sequence *seq, void *user_data)
{
Read_lib_data *data = (Read_lib_data *)user_data;
BlendLibReader *reader = data->reader;
Scene *sce = data->scene;
IDP_BlendReadLib(reader, seq->prop);
if (seq->ipo) {
/* XXX: deprecated - old animation system. */
BLO_read_id_address(reader, sce->id.lib, &seq->ipo);
}
seq->scene_sound = NULL;
if (seq->scene) {
BLO_read_id_address(reader, sce->id.lib, &seq->scene);
seq->scene_sound = NULL;
}
if (seq->clip) {
BLO_read_id_address(reader, sce->id.lib, &seq->clip);
}
if (seq->mask) {
BLO_read_id_address(reader, sce->id.lib, &seq->mask);
}
if (seq->scene_camera) {
BLO_read_id_address(reader, sce->id.lib, &seq->scene_camera);
}
if (seq->sound) {
seq->scene_sound = NULL;
if (seq->type == SEQ_TYPE_SOUND_HD) {
seq->type = SEQ_TYPE_SOUND_RAM;
}
else {
BLO_read_id_address(reader, sce->id.lib, &seq->sound);
}
if (seq->sound) {
id_us_plus_no_lib((ID *)seq->sound);
seq->scene_sound = NULL;
}
}
if (seq->type == SEQ_TYPE_TEXT) {
TextVars *t = seq->effectdata;
BLO_read_id_address(reader, sce->id.lib, &t->text_font);
}
BLI_listbase_clear(&seq->anims);
SEQ_modifier_blend_read_lib(reader, sce, &seq->modifiers);
seq->flag &= ~SEQ_FLAG_SKIP_THUMBNAILS;
return true;
}
void SEQ_blend_read_lib(BlendLibReader *reader, Scene *scene, ListBase *seqbase)
{
Read_lib_data data = {reader, scene};
SEQ_for_each_callback(seqbase, seq_read_lib_cb, &data);
}
static bool seq_blend_read_expand(Sequence *seq, void *user_data)
{
BlendExpander *expander = (BlendExpander *)user_data;
IDP_BlendReadExpand(expander, seq->prop);
if (seq->scene) {
BLO_expand(expander, seq->scene);
}
if (seq->scene_camera) {
BLO_expand(expander, seq->scene_camera);
}
if (seq->clip) {
BLO_expand(expander, seq->clip);
}
if (seq->mask) {
BLO_expand(expander, seq->mask);
}
if (seq->sound) {
BLO_expand(expander, seq->sound);
}
if (seq->type == SEQ_TYPE_TEXT && seq->effectdata) {
TextVars *data = seq->effectdata;
BLO_expand(expander, data->text_font);
}
return true;
}
void SEQ_blend_read_expand(BlendExpander *expander, ListBase *seqbase)
{
SEQ_for_each_callback(seqbase, seq_blend_read_expand, expander);
}
/* Depsgraph update functions. */
static bool seq_disable_sound_strips_cb(Sequence *seq, void *user_data)
{
Scene *scene = (Scene *)user_data;
if (seq->scene_sound != NULL) {
BKE_sound_remove_scene_sound(scene, seq->scene_sound);
seq->scene_sound = NULL;
}
return true;
}
static bool seq_update_seq_cb(Sequence *seq, void *user_data)
{
Scene *scene = (Scene *)user_data;
if (seq->scene_sound == NULL) {
if (seq->sound != NULL) {
seq->scene_sound = BKE_sound_add_scene_sound_defaults(scene, seq);
}
else if (seq->type == SEQ_TYPE_SCENE) {
if (seq->scene != NULL) {
BKE_sound_ensure_scene(seq->scene);
seq->scene_sound = BKE_sound_scene_add_scene_sound_defaults(scene, seq);
}
}
}
if (seq->scene_sound != NULL) {
/* Make sure changing volume via sequence's properties panel works correct.
*
* Ideally, the entire BKE_scene_update_sound() will happen from a dependency graph, so
* then it is no longer needed to do such manual forced updates. */
if (seq->type == SEQ_TYPE_SCENE && seq->scene != NULL) {
BKE_sound_set_scene_volume(seq->scene, seq->scene->audio.volume);
if ((seq->flag & SEQ_SCENE_STRIPS) == 0 && seq->scene->sound_scene != NULL &&
seq->scene->ed != NULL) {
SEQ_for_each_callback(&seq->scene->ed->seqbase, seq_disable_sound_strips_cb, seq->scene);
}
}
if (seq->sound != NULL) {
if (scene->id.recalc & ID_RECALC_AUDIO || seq->sound->id.recalc & ID_RECALC_AUDIO) {
BKE_sound_update_scene_sound(seq->scene_sound, seq->sound);
}
}
BKE_sound_set_scene_sound_volume(
seq->scene_sound, seq->volume, (seq->flag & SEQ_AUDIO_VOLUME_ANIMATED) != 0);
BKE_sound_set_scene_sound_pitch(
seq->scene_sound, seq->pitch, (seq->flag & SEQ_AUDIO_PITCH_ANIMATED) != 0);
BKE_sound_set_scene_sound_pan(
seq->scene_sound, seq->pan, (seq->flag & SEQ_AUDIO_PAN_ANIMATED) != 0);
}
return true;
}
void SEQ_eval_sequences(Depsgraph *depsgraph, Scene *scene, ListBase *seqbase)
{
DEG_debug_print_eval(depsgraph, __func__, scene->id.name, scene);
BKE_sound_ensure_scene(scene);
SEQ_for_each_callback(seqbase, seq_update_seq_cb, scene);
SEQ_edit_update_muting(scene->ed);
SEQ_sound_update_bounds_all(scene);
}