[WIP] VSE: share ImBufAnim resource between strips #118670

Draft
Richard Antalik wants to merge 25 commits from iss/blender:anim-sharing-2 into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
27 changed files with 680 additions and 425 deletions

@ -1 +1 @@
Subproject commit d2eea8a8a6b22d6ec6849deea72e141fc5b384d4
Subproject commit d983ed32a1e760130ba31ad8a98a94ea943267a2

@ -1 +1 @@
Subproject commit 38a8f38d98987efc9aebe878ca941d99756e914e
Subproject commit 7181d6dccb9fe4184340f9f5b1c381f8089fe4ec

View File

@ -1344,6 +1344,7 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
ed->cache = nullptr;
ed->prefetch_job = nullptr;
ed->runtime.sequence_lookup = nullptr;
ed->runtime.anim_lookup = nullptr;
/* recursive link sequences, lb will be correctly initialized */
link_recurs_seq(reader, &ed->seqbase);

View File

@ -20,28 +20,24 @@ SequenceBackup::SequenceBackup(const Depsgraph * /*depsgraph*/)
void SequenceBackup::reset()
{
scene_sound = nullptr;
BLI_listbase_clear(&anims);
}
void SequenceBackup::init_from_sequence(Sequence *sequence)
{
scene_sound = sequence->scene_sound;
anims = sequence->anims;
sequence->scene_sound = nullptr;
BLI_listbase_clear(&sequence->anims);
}
void SequenceBackup::restore_to_sequence(Sequence *sequence)
{
sequence->scene_sound = scene_sound;
sequence->anims = anims;
reset();
}
bool SequenceBackup::isEmpty() const
{
return (scene_sound == nullptr) && BLI_listbase_is_empty(&anims);
return (scene_sound == nullptr);
}
} // namespace blender::deg

View File

@ -29,7 +29,6 @@ class SequenceBackup {
bool isEmpty() const;
void *scene_sound;
ListBase anims;
};
} // namespace blender::deg

View File

@ -2786,7 +2786,7 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op)
prop = RNA_struct_find_property(&seq_ptr, "filepath");
RNA_property_string_set(&seq_ptr, prop, filepath);
RNA_property_update(C, &seq_ptr, prop);
SEQ_relations_sequence_free_anim(seq);
SEQ_relations_sequence_free_anim(scene, seq);
}
SEQ_relations_invalidate_cache_raw(scene, seq);

View File

@ -153,7 +153,7 @@ static void thumbnail_start_job(void *data, wmJobWorkerStatus *worker_status)
val->seq_dupli, tj->pixelx, tj->pixely, &frame_step, tj->thumb_height, nullptr, nullptr);
SEQ_render_thumbnails(
&tj->context, val->seq_dupli, seq_orig, frame_step, tj->view_area, &worker_status->stop);
SEQ_relations_sequence_free_anim(val->seq_dupli);
SEQ_relations_sequence_free_anim(scene, val->seq_dupli);
}
BLI_ghashIterator_step(&gh_iter);
}
@ -170,7 +170,7 @@ static void thumbnail_start_job(void *data, wmJobWorkerStatus *worker_status)
val->seq_dupli, tj->pixelx, tj->pixely, &frame_step, tj->thumb_height, nullptr, nullptr);
SEQ_render_thumbnails_base_set(
&tj->context, val->seq_dupli, seq_orig, tj->view_area, &worker_status->stop);
SEQ_relations_sequence_free_anim(val->seq_dupli);
SEQ_relations_sequence_free_anim(scene, val->seq_dupli);
}
BLI_ghashIterator_step(&gh_iter);
}

View File

@ -212,7 +212,7 @@ typedef struct Sequence {
/** For MASK strips. */
struct Mask *mask;
/** For MOVIE strips. */
ListBase anims;
ListBase anims DNA_DEPRECATED;
float effect_fader;
/* DEPRECATED, only used for versioning. */
@ -298,6 +298,8 @@ typedef struct SeqTimelineChannel {
typedef struct EditingRuntime {
struct SequenceLookup *sequence_lookup;
struct AnimManager *anim_lookup;
void *_pad0;
} EditingRuntime;
typedef struct Editing {

View File

@ -607,9 +607,9 @@ static bool seq_find_colorspace_settings_cb(Sequence *seq, void *user_data)
return true;
}
static bool seq_free_anim_cb(Sequence *seq, void * /*user_data*/)
static bool seq_free_anim_cb(Sequence *seq, void *user_data)
{
SEQ_relations_sequence_free_anim(seq);
SEQ_relations_sequence_free_anim(static_cast<Scene *>(user_data), seq);
return true;
}
@ -659,7 +659,7 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain,
Sequence *seq = cb_data.r_seq;
if (seq) {
SEQ_relations_sequence_free_anim(seq);
SEQ_relations_sequence_free_anim(scene, seq);
if (seq->strip->proxy && seq->strip->proxy->anim) {
IMB_free_anim(seq->strip->proxy->anim);
@ -669,7 +669,7 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain,
SEQ_relations_invalidate_cache_raw(scene, seq);
}
else {
SEQ_for_each_callback(&scene->ed->seqbase, seq_free_anim_cb, nullptr);
SEQ_for_each_callback(&scene->ed->seqbase, seq_free_anim_cb, scene);
}
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, nullptr);

View File

@ -862,22 +862,25 @@ static bool rna_MovieSequence_reload_if_needed(ID *scene_id, Sequence *seq, Main
static PointerRNA rna_MovieSequence_metadata_get(ID *scene_id, Sequence *seq)
{
if (seq == nullptr || seq->anims.first == nullptr) {
return PointerRNA_NULL;
// XXXXX
/*if (seq == nullptr || seq->anims.first == nullptr) {
return PointerRNA_NULL;
}
StripAnim *sanim = static_cast<StripAnim *>(seq->anims.first);
if (sanim->anim == nullptr) {
return PointerRNA_NULL;
}
StripAnim *sanim = static_cast<StripAnim *>(seq->anims.first);
if (sanim->anim == nullptr) {
return PointerRNA_NULL;
}
IDProperty *metadata = IMB_anim_load_metadata(sanim->anim);
if (metadata == nullptr) {
return PointerRNA_NULL;
}
IDProperty *metadata = IMB_anim_load_metadata(sanim->anim);
if (metadata == nullptr) {
return PointerRNA_NULL;
}
PointerRNA ptr = RNA_pointer_create(scene_id, &RNA_IDPropertyWrapPtr, metadata);
return ptr;
return ptr;*/
}
static PointerRNA rna_SequenceEditor_meta_stack_get(CollectionPropertyIterator *iter)

View File

@ -41,6 +41,8 @@ set(SRC
SEQ_transform.hh
SEQ_utils.hh
intern/anim_manager.hh
intern/anim_manager.cc
intern/animation.cc
intern/channels.cc
intern/disk_cache.cc

View File

@ -19,10 +19,6 @@ struct Sequence;
* Check if one sequence is input to the other.
*/
bool SEQ_relation_is_effect_of_strip(const Sequence *effect, const Sequence *input);
/**
* Function to free imbuf and anim data on changes.
*/
void SEQ_relations_sequence_free_anim(Sequence *seq);
bool SEQ_relations_check_scene_recursion(Scene *scene, ReportList *reports);
/**
* Check if "seq_main" (indirectly) uses strip "seq".
@ -39,10 +35,6 @@ void SEQ_relations_invalidate_cache_in_range(Scene *scene,
Sequence *seq,
Sequence *range_mask,
int invalidate_types);
/**
* Release FFmpeg handles of strips that are not currently displayed to minimize memory usage.
*/
void SEQ_relations_free_all_anim_ibufs(Scene *scene, int timeline_frame);
/**
* A debug and development function which checks whether sequences have unique UIDs.
* Errors will be reported to the console.
@ -66,3 +58,8 @@ Sequence *SEQ_find_metastrip_by_sequence(ListBase *seqbase /* = ed->seqbase */,
Sequence *meta /* = NULL */,
Sequence *seq);
bool SEQ_exists_in_seqbase(const Sequence *seq, const ListBase *seqbase);
/**
* Function to free imbuf and anim data on changes.
*/
void SEQ_relations_sequence_free_anim(const Scene *scene, const Sequence *seq);

View File

@ -0,0 +1,386 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup sequencer
*/
#include "BLI_blenlib.h"
#include "BLI_hash_mm3.hh"
#include "BLI_index_range.hh"
#include "BLI_math_base.h"
#include "BLI_task.hh"
#include "BLI_timeit.hh"
#include "BKE_image.h"
#include "BKE_main.hh"
#include "BKE_scene.hh"
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "SEQ_iterator.hh"
#include "SEQ_relations.hh"
#include "SEQ_sequencer.hh"
#include "SEQ_utils.hh"
#include "anim_manager.hh"
#include "multiview.hh"
#include "proxy.hh"
#include "render.hh"
#include "strip_time.hh"
/* This is arbitrary, it is possible to prefetch n strips ahead, but if strips are too short, but
* it may be better to prefetch frame range. */
#define PREFETCH_DIST 512
static void anim_filepath_get(const Scene *scene,
const Sequence *seq,
size_t filepath_size,
char *r_filepath)
{
BLI_path_join(r_filepath, filepath_size, seq->strip->dirpath, seq->strip->stripdata->filename);
BLI_path_abs(r_filepath, ID_BLEND_PATH_FROM_GLOBAL(&scene->id));
}
static bool use_proxy(Editing *ed, Sequence *seq)
{
StripProxy *proxy = seq->strip->proxy;
return proxy && ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) != 0 ||
(ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE));
}
static void proxy_dir_get(Editing *ed, Sequence *seq, size_t str_len, char *r_proxy_dirpath)
{
if (use_proxy(ed, seq)) {
if (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE) {
if (ed->proxy_dir[0] == 0) {
BLI_strncpy(r_proxy_dirpath, "//BL_proxy", str_len);
}
else {
BLI_strncpy(r_proxy_dirpath, ed->proxy_dir, str_len);
}
}
else {
BLI_strncpy(r_proxy_dirpath, seq->strip->proxy->dirpath, str_len);
}
BLI_path_abs(r_proxy_dirpath, BKE_main_blendfile_path_from_global());
}
}
static void index_dir_set(Editing *ed, Sequence *seq, ImBufAnim *anim)
{
if (!use_proxy(ed, seq)) {
return;
}
char proxy_dirpath[FILE_MAX];
proxy_dir_get(ed, seq, sizeof(proxy_dirpath), proxy_dirpath);
seq_proxy_index_dir_set(anim, proxy_dirpath);
}
static ImBufAnim *anim_get(Sequence *seq, const char *filepath, bool openfile)
{
ImBufAnim *anim = nullptr;
if (openfile) {
anim = openanim(filepath,
IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
seq->streamindex,
seq->strip->colorspace_settings.name);
}
else {
anim = openanim_noload(filepath,
IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
seq->streamindex,
seq->strip->colorspace_settings.name);
}
return anim;
}
static bool is_multiview(const Scene *scene, Sequence *seq, const char *filepath)
{
bool use_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && (scene->r.scemode & R_MULTIVIEW) != 0;
char prefix[FILE_MAX];
const char *ext = nullptr;
BKE_scene_multiview_view_prefix_get(const_cast<Scene *>(scene), filepath, prefix, &ext);
return use_multiview && seq->views_format == R_IMF_VIEWS_INDIVIDUAL && prefix[0] != '\0';
}
static blender::Vector<ImBufAnim *> multiview_anims_get(const Scene *scene,
Sequence *seq,
const char *filepath)
{
int totfiles = seq_num_files(scene, seq->views_format, true);
char prefix[FILE_MAX];
const char *ext = nullptr;
BKE_scene_multiview_view_prefix_get(const_cast<Scene *>(scene), filepath, prefix, &ext);
blender::Vector<ImBufAnim *> anims;
for (int i = 0; i < totfiles; i++) {
const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, i);
char filepath_view[FILE_MAX];
SNPRINTF(filepath_view, "%s%s%s", prefix, suffix, ext);
/* Multiview files must be loaded, otherwise it is not possible to detect failure. */
ImBufAnim *anim = anim_get(seq, filepath_view, true);
if (anim != nullptr) {
anims.append(anim);
}
}
return anims;
}
void ShareableAnim::release_from_strip(Sequence *seq)
{
if (anims.size() == 0) {
return;
}
mutex->lock();
users.remove_if([seq](Sequence *seq_user) { return seq == seq_user; });
if (users.size() == 0) {
for (ImBufAnim *anim : anims) {
IMB_free_anim(anim);
}
anims.clear();
}
mutex->unlock();
};
void ShareableAnim::release_from_all_strips(void)
{
for (Sequence *user : users) {
release_from_strip(user);
}
}
void ShareableAnim::acquire_anims(const Scene *scene, Sequence *seq, bool openfile)
{
char filepath[FILE_MAX];
anim_filepath_get(scene, seq, sizeof(filepath), filepath);
if (is_multiview(scene, seq, filepath)) {
anims = multiview_anims_get(scene, seq, filepath);
multiview_loaded = true;
return;
}
ImBufAnim *anim = anim_get(seq, filepath, openfile);
if (anim != nullptr) {
anims.append(anim);
}
for (int i = 0; i < anims.size(); i++) {
index_dir_set(SEQ_editing_get(scene), seq, anims[i]);
char filepath[FILE_MAX];
anim_filepath_get(scene, seq, sizeof(filepath), filepath);
if (is_multiview(scene, seq, filepath)) {
const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, i);
IMB_suffix_anim(anims[i], suffix);
}
}
users.append(seq);
}
bool ShareableAnim::has_anim(const Scene *scene, Sequence *seq)
{
char filepath[FILE_MAX];
anim_filepath_get(scene, seq, sizeof(filepath), filepath);
if (is_multiview(scene, seq, filepath) && !multiview_loaded) {
return false;
}
return !anims.is_empty();
}
bool ShareableAnim::try_lock()
{
return mutex->try_lock();
}
void ShareableAnim::unlock()
{
mutex->unlock();
}
static blender::Vector<Sequence *> strips_to_prefetch_get(const Scene *scene)
{
Editing *ed = SEQ_editing_get(scene);
ListBase *seqbase = SEQ_active_seqbase_get(ed);
blender::VectorSet<Sequence *> strips = SEQ_query_all_strips_recursive(seqbase);
const int timeline_frame = BKE_scene_frame_get(scene);
strips.remove_if([scene, timeline_frame](Sequence *seq) {
if (seq->type != SEQ_TYPE_MOVIE) {
return true;
}
if (seq_time_distance_from_frame(scene, seq, timeline_frame) > PREFETCH_DIST) {
return true;
}
return false;
});
blender::Vector<Sequence *> strips_sorted = strips.as_span();
std::sort(strips_sorted.begin(), strips_sorted.end(), [&](const Sequence *a, const Sequence *b) {
return seq_time_distance_from_frame(scene, a, timeline_frame) <
seq_time_distance_from_frame(scene, b, timeline_frame);
});
return strips_sorted;
}
AnimManager *seq_anim_manager_ensure(Editing *ed)
{
if (ed->runtime.anim_lookup == nullptr) {
ed->runtime.anim_lookup = MEM_new<AnimManager>(__func__);
}
return ed->runtime.anim_lookup;
}
void AnimManager::free_unused_anims(blender::Vector<Sequence *> &strips)
{
mutex.lock();
for (ShareableAnim &sh_anim : anims.values()) {
bool strips_use_anim = false;
for (Sequence *user : sh_anim.users) {
if (strips.contains(user)) {
strips_use_anim = true;
break;
}
}
if (!strips_use_anim && sh_anim.users.size() > 0) {
sh_anim.release_from_all_strips();
}
}
mutex.unlock();
}
void AnimManager::parallel_load_anims(const Scene *scene,
blender::Vector<Sequence *> &strips,
bool unlock)
{
/* Ensure cache items. */
// XXX why is this needed, when cache_entry_get is locking vector? if this is not done before
// parallel for loop, it causes use after free in ShareableAnim::anims...
for (Sequence *seq : strips) {
if (seq->type == SEQ_TYPE_MOVIE) {
cache_entry_get(scene, seq);
}
}
using namespace blender;
threading::parallel_for(strips.index_range(), 1, [&](const IndexRange range) {
for (int i : range) {
Sequence *seq = strips[i];
if (seq->type != SEQ_TYPE_MOVIE) {
continue;
}
ShareableAnim &sh_anim = cache_entry_get(scene, seq);
if (!sh_anim.mutex->try_lock()) {
continue;
}
if (sh_anim.has_anim(scene, seq)) {
if (unlock) {
sh_anim.unlock();
}
continue;
}
sh_anim.acquire_anims(scene, seq, true);
if (unlock) {
sh_anim.unlock();
}
}
});
}
void AnimManager::free_unused_and_prefetch_anims(const Scene *scene)
{
blender::Vector<Sequence *> strips = strips_to_prefetch_get(scene);
free_unused_anims(strips);
parallel_load_anims(scene, strips, true);
}
void AnimManager::manage_anims(const Scene *scene)
{
if (prefetch_thread.joinable()) {
prefetch_thread.join();
}
else {
prefetch_thread = std::thread(&AnimManager::free_unused_and_prefetch_anims, this, scene);
}
}
ShareableAnim &AnimManager::cache_entry_get(const Scene *scene, const Sequence *seq)
{
char filepath[FILE_MAX];
anim_filepath_get(scene, seq, sizeof(filepath), filepath);
mutex.lock();
ShareableAnim &sh_anim = anims.lookup_or_add_default(std::string(filepath));
mutex.unlock();
return sh_anim;
}
void AnimManager::strip_anims_acquire(const Scene *scene, blender::Vector<Sequence *> &strips)
{
parallel_load_anims(scene, strips, false);
}
void AnimManager::strip_anims_acquire(const Scene *scene, Sequence *seq)
{
ShareableAnim &sh_anim = cache_entry_get(scene, seq);
if (!sh_anim.mutex->try_lock()) {
return;
}
if (!sh_anim.has_anim(scene, seq)) {
sh_anim.acquire_anims(scene, seq, true);
}
}
void AnimManager::strip_anims_release(const Scene *scene, blender::Vector<Sequence *> &strips)
{
for (Sequence *seq : strips) {
if (seq->type == SEQ_TYPE_MOVIE) {
ShareableAnim &sh_anim = cache_entry_get(scene, seq);
sh_anim.unlock();
}
}
}
void AnimManager::strip_anims_release(const Scene *scene, Sequence *seq)
{
ShareableAnim &sh_anim = cache_entry_get(scene, seq);
sh_anim.unlock();
}
blender::Vector<ImBufAnim *> &AnimManager::strip_anims_get(const Scene *scene, const Sequence *seq)
{
ShareableAnim &sh_anim = cache_entry_get(scene, seq);
return sh_anim.anims;
}
void AnimManager::free_anims_by_seq(const Scene *scene, const Sequence *seq)
{
ShareableAnim &sh_anim = cache_entry_get(scene, seq);
sh_anim.release_from_all_strips();
}

View File

@ -0,0 +1,68 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup sequencer
*/
struct Scene;
struct Sequence;
#include <mutex>
#include <thread>
#include "BLI_map.hh"
class ShareableAnim {
public:
blender::Vector<ImBufAnim *> anims; /* Ordered by view_id. */
blender::Vector<Sequence *> users;
bool multiview_loaded = false;
std::unique_ptr<std::mutex> mutex = std::make_unique<std::mutex>();
void release_from_strip(Sequence *seq);
void release_from_all_strips(void);
void acquire_anims(const Scene *scene, Sequence *seq, bool openfile);
bool has_anim(const Scene *scene, Sequence *seq);
bool try_lock();
void unlock();
};
class AnimManager {
public:
blender::Map<std::string, ShareableAnim> anims;
std::mutex mutex;
std::thread prefetch_thread;
/**
* Free anims of strips behind current frame and prefetch anims that are to be played.
*/
void manage_anims(const Scene *scene);
/**
* Load anims used by strips and lock them so they won't be freed.
*/
void strip_anims_acquire(const Scene *scene, blender::Vector<Sequence *> &strips);
void strip_anims_acquire(const Scene *scene, Sequence *seq);
/**
* Unlock anims used by strips.
*/
void strip_anims_release(const Scene *scene, blender::Vector<Sequence *> &strips);
void strip_anims_release(const Scene *scene, Sequence *seq);
/**
* Get anims used by `seq`.
*/
blender::Vector<ImBufAnim *> &strip_anims_get(const Scene *scene, const Sequence *seq);
/**
* Free anims used by `seq`.
*/
void free_anims_by_seq(const Scene *scene, const Sequence *seq);
private:
ShareableAnim &cache_entry_get(const Scene *scene, const Sequence *seq);
void free_unused_anims(blender::Vector<Sequence *> &strips);
void free_unused_and_prefetch_anims(const Scene *scene);
void parallel_load_anims(const Scene *scene, blender::Vector<Sequence *> &strips, bool unlock);
};
AnimManager *seq_anim_manager_ensure(Editing *ed);

View File

@ -24,7 +24,7 @@ void seq_anim_add_suffix(Scene *scene, ImBufAnim *anim, const int view_id)
IMB_suffix_anim(anim, suffix);
}
int seq_num_files(Scene *scene, char views_format, const bool is_multiview)
int seq_num_files(const Scene *scene, char views_format, const bool is_multiview)
{
if (!is_multiview) {
return 1;

View File

@ -23,4 +23,4 @@ void seq_multiview_name(
/**
* The number of files will vary according to the stereo format.
*/
int seq_num_files(Scene *scene, char views_format, bool is_multiview);
int seq_num_files(const Scene *scene, char views_format, bool is_multiview);

View File

@ -42,11 +42,11 @@
#include "SEQ_sequencer.hh"
#include "SEQ_time.hh"
#include "anim_manager.hh"
#include "multiview.hh"
#include "proxy.hh"
#include "render.hh"
#include "sequencer.hh"
#include "utils.hh"
struct SeqIndexBuildContext {
IndexBuildContext *index_context;
@ -200,7 +200,6 @@ ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int timeline
StripProxy *proxy = seq->strip->proxy;
const eSpaceSeq_Proxy_RenderSize psize = eSpaceSeq_Proxy_RenderSize(
context->preview_render_size);
StripAnim *sanim;
/* only use proxies, if they are enabled (even if present!) */
if (!SEQ_can_use_proxy(context, seq, SEQ_rendersize_to_proxysize(psize))) {
@ -223,12 +222,15 @@ ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int timeline
return nullptr;
}
seq_open_anim_file(context->scene, seq, true);
sanim = static_cast<StripAnim *>(seq->anims.first);
AnimManager *manager = seq_anim_manager_ensure(SEQ_editing_get(context->scene));
manager->strip_anims_acquire(context->scene, seq);
blender::Vector<ImBufAnim *> anims = manager->strip_anims_get(context->scene, seq);
ImBufAnim *anim = anims.size() > 0 ? anims[0] : nullptr;
frameno = IMB_anim_index_get_frame_index(
sanim ? sanim->anim : nullptr, IMB_Timecode_Type(seq->strip->proxy->tc), frameno);
anim, IMB_Timecode_Type(seq->strip->proxy->tc), frameno);
manager->strip_anims_release(context->scene, seq);
return IMB_anim_absolute(proxy->anim, frameno, IMB_TC_NONE, IMB_PROXY_NONE);
}
@ -369,6 +371,16 @@ static bool seq_proxy_multiview_context_invalid(Sequence *seq,
return false;
}
static int seq_anims_count(const Scene *scene, Sequence *seq)
{
AnimManager *manager = seq_anim_manager_ensure(SEQ_editing_get(scene));
manager->strip_anims_acquire(scene, seq);
blender::Vector<ImBufAnim *> anims = manager->strip_anims_get(scene, seq);
int count = anims.size();
manager->strip_anims_release(scene, seq);
return count;
}
/**
* This returns the maximum possible number of required contexts
*/
@ -382,7 +394,7 @@ static int seq_proxy_context_count(Sequence *seq, Scene *scene)
switch (seq->type) {
case SEQ_TYPE_MOVIE: {
num_views = BLI_listbase_count(&seq->anims);
num_views = seq_anims_count(scene, seq);
break;
}
case SEQ_TYPE_IMAGE: {
@ -449,13 +461,14 @@ bool SEQ_proxy_rebuild_context(Main *bmain,
/* Check if proxies are already built here, because actually opening anims takes a lot of
* time. */
seq_open_anim_file(scene, seq, false);
StripAnim *sanim = static_cast<StripAnim *>(BLI_findlink(&seq->anims, i));
if (sanim->anim && !seq_proxy_need_rebuild(seq, sanim->anim)) {
AnimManager *manager = seq_anim_manager_ensure(SEQ_editing_get(scene));
manager->strip_anims_acquire(scene, seq);
blender::Vector<ImBufAnim *> anims = manager->strip_anims_get(scene, seq);
if (anims.size() >= i && !seq_proxy_need_rebuild(seq, anims[i])) {
manager->strip_anims_release(scene, seq);
continue;
}
SEQ_relations_sequence_free_anim(seq);
manager->strip_anims_release(scene, seq);
context = static_cast<SeqIndexBuildContext *>(
MEM_callocN(sizeof(SeqIndexBuildContext), "seq proxy rebuild context"));
@ -477,12 +490,11 @@ bool SEQ_proxy_rebuild_context(Main *bmain,
context->view_id = i; /* only for images */
if (nseq->type == SEQ_TYPE_MOVIE) {
seq_open_anim_file(scene, nseq, true);
sanim = static_cast<StripAnim *>(BLI_findlink(&nseq->anims, i));
if (sanim->anim) {
manager->strip_anims_acquire(scene, nseq);
anims = manager->strip_anims_get(scene, nseq);
if (anims.size() > i) {
context->index_context = IMB_anim_index_rebuild_context(
sanim->anim,
anims[i],
IMB_Timecode_Type(context->tc_flags),
context->size_flags,
context->quality,
@ -491,9 +503,11 @@ bool SEQ_proxy_rebuild_context(Main *bmain,
build_only_on_bad_performance);
}
if (!context->index_context) {
manager->strip_anims_release(scene, seq);
MEM_freeN(context);
return false;
}
manager->strip_anims_release(scene, nseq);
}
link = BLI_genericNodeN(context);
@ -575,14 +589,20 @@ void SEQ_proxy_rebuild(SeqIndexBuildContext *context, wmJobWorkerStatus *worker_
void SEQ_proxy_rebuild_finish(SeqIndexBuildContext *context, bool stop)
{
AnimManager *manager = seq_anim_manager_ensure(SEQ_editing_get(context->scene));
manager->strip_anims_acquire(context->scene, context->seq);
if (context->index_context) {
LISTBASE_FOREACH (StripAnim *, sanim, &context->seq->anims) {
IMB_close_anim_proxies(sanim->anim);
blender::Vector<ImBufAnim *> anims = manager->strip_anims_get(context->scene, context->seq);
for (ImBufAnim *anim : anims) {
IMB_close_anim_proxies(anim);
}
IMB_anim_index_rebuild_finish(context->index_context, stop);
}
manager->strip_anims_release(context->scene, context->seq);
seq_free_sequence_recurse(nullptr, context->seq, true);
MEM_freeN(context);

View File

@ -64,28 +64,36 @@
#include "SEQ_transform.hh"
#include "SEQ_utils.hh"
#include "anim_manager.hh"
#include "effects.hh"
#include "image_cache.hh"
#include "multiview.hh"
#include "prefetch.hh"
#include "proxy.hh"
#include "render.hh"
#include "utils.hh"
#include <algorithm>
using namespace blender;
ThreadMutex seq_render_mutex = BLI_MUTEX_INITIALIZER;
static ImBuf *seq_render_strip_stack(const SeqRenderData *context,
SeqRenderState *state,
ListBase *channels,
ListBase *seqbasep,
float timeline_frame,
int chanshown);
static ThreadMutex seq_render_mutex = BLI_MUTEX_INITIALIZER;
SequencerDrawView sequencer_view3d_fn = nullptr; /* nullptr in background mode */
void seq_render_mutex_lock(void)
{
BLI_mutex_lock(&seq_render_mutex);
}
void seq_render_mutex_unlock(void)
{
BLI_mutex_unlock(&seq_render_mutex);
}
/* -------------------------------------------------------------------- */
/** \name Color-space utility functions
* \{ */
@ -1151,7 +1159,7 @@ static IMB_Timecode_Type seq_render_movie_strip_timecode_get(Sequence *seq)
static ImBuf *seq_render_movie_strip_view(const SeqRenderData *context,
Sequence *seq,
float timeline_frame,
StripAnim *sanim,
ImBufAnim *anim,
bool *r_is_proxy_image)
{
ImBuf *ibuf = nullptr;
@ -1167,11 +1175,9 @@ static ImBuf *seq_render_movie_strip_view(const SeqRenderData *context,
{
ibuf = seq_render_movie_strip_custom_file_proxy(context, seq, timeline_frame);
}
else {
ibuf = IMB_anim_absolute(sanim->anim,
frame_index + seq->anim_startofs,
seq_render_movie_strip_timecode_get(seq),
psize);
else if (anim != nullptr) {
ibuf = IMB_anim_absolute(
anim, frame_index + seq->anim_startofs, seq_render_movie_strip_timecode_get(seq), psize);
}
if (ibuf != nullptr) {
@ -1180,8 +1186,8 @@ static ImBuf *seq_render_movie_strip_view(const SeqRenderData *context,
}
/* Fetching for requested proxy size failed, try fetching the original instead. */
if (ibuf == nullptr) {
ibuf = IMB_anim_absolute(sanim->anim,
if (ibuf == nullptr && anim != nullptr) {
ibuf = IMB_anim_absolute(anim,
frame_index + seq->anim_startofs,
seq_render_movie_strip_timecode_get(seq),
IMB_PROXY_NONE);
@ -1205,33 +1211,34 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context,
float timeline_frame,
bool *r_is_proxy_image)
{
/* Load all the videos. */
seq_open_anim_file(context->scene, seq, false);
ImBuf *ibuf = nullptr;
StripAnim *sanim = static_cast<StripAnim *>(seq->anims.first);
Scene *scene = context->scene;
AnimManager *anim_manager = seq_anim_manager_ensure(SEQ_editing_get(context->scene));
blender::Vector<ImBufAnim *> anims = anim_manager->strip_anims_get(scene, seq);
const int anim_count = anims.size();
const int totfiles = seq_num_files(context->scene, seq->views_format, true);
bool is_multiview_render = (seq->flag & SEQ_USE_VIEWS) != 0 &&
(context->scene->r.scemode & R_MULTIVIEW) != 0 &&
BLI_listbase_count_at_most(&seq->anims, totfiles + 1) == totfiles;
std::min(anim_count, totfiles + 1) == totfiles;
if (anims.size() == 0) {
return nullptr;
}
if (is_multiview_render) {
ImBuf **ibuf_arr;
int totviews = BKE_scene_multiview_num_views_get(&context->scene->r);
int totviews = std::min(BKE_scene_multiview_num_views_get(&context->scene->r), anim_count);
ibuf_arr = static_cast<ImBuf **>(
MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs"));
int ibuf_view_id;
for (ibuf_view_id = 0, sanim = static_cast<StripAnim *>(seq->anims.first); sanim;
sanim = sanim->next, ibuf_view_id++)
{
if (sanim->anim) {
ibuf_arr[ibuf_view_id] = seq_render_movie_strip_view(
context, seq, timeline_frame, sanim, r_is_proxy_image);
}
for (int view_id = 0; view_id < totviews; view_id++) {
ibuf_arr[view_id] = seq_render_movie_strip_view(
context, seq, timeline_frame, anims[view_id], r_is_proxy_image);
}
if (seq->views_format == R_IMF_VIEWS_STEREO_3D) {
if (seq->views_format == R_IMF_VIEWS_STEREO_3D && totviews == 2) {
if (ibuf_arr[0] == nullptr) {
/* Probably proxy hasn't been created yet. */
MEM_freeN(ibuf_arr);
@ -1245,14 +1252,16 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context,
SeqRenderData localcontext = *context;
localcontext.view_id = view_id;
if (view_id != context->view_id) {
if (view_id != context->view_id && ibuf_arr[view_id]) {
ibuf_arr[view_id] = seq_render_preprocess_ibuf(
&localcontext, seq, ibuf_arr[view_id], timeline_frame, true, false);
}
}
/* Return the original requested ImBuf. */
ibuf = ibuf_arr[context->view_id];
if (context->view_id < totviews) {
ibuf = ibuf_arr[context->view_id];
}
/* Remove the others (decrease their refcount). */
for (int view_id = 0; view_id < totviews; view_id++) {
@ -1264,7 +1273,7 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context,
MEM_freeN(ibuf_arr);
}
else {
ibuf = seq_render_movie_strip_view(context, seq, timeline_frame, sanim, r_is_proxy_image);
ibuf = seq_render_movie_strip_view(context, seq, timeline_frame, anims[0], r_is_proxy_image);
}
if (ibuf == nullptr) {
@ -1272,10 +1281,10 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context,
}
if (*r_is_proxy_image == false) {
if (sanim && sanim->anim) {
if (anims[0]) {
short fps_denom;
float fps_num;
IMB_anim_get_fps(sanim->anim, true, &fps_denom, &fps_num);
IMB_anim_get_fps(anims[0], true, &fps_denom, &fps_num);
seq->strip->stripdata->orig_fps = fps_denom / fps_num;
}
seq->strip->stripdata->orig_width = ibuf->x;
@ -2096,11 +2105,14 @@ ImBuf *SEQ_render_give_ibuf(const SeqRenderData *context, float timeline_frame,
}
seq_cache_free_temp_cache(context->scene, context->task_id, timeline_frame);
/* Make sure we only keep the `anim` data for strips that are in view. */
SEQ_relations_free_all_anim_ibufs(context->scene, timeline_frame);
AnimManager *anim_manager = seq_anim_manager_ensure(ed);
if (!strips.is_empty() && !out) {
BLI_mutex_lock(&seq_render_mutex);
/* Load anims in main thread for the first time and lock them, so they can't be freed. */
anim_manager->strip_anims_acquire(scene, strips);
out = seq_render_strip_stack(context, &state, channels, seqbasep, timeline_frame, chanshown);
if (context->is_prefetch_render) {
@ -2110,10 +2122,12 @@ ImBuf *SEQ_render_give_ibuf(const SeqRenderData *context, float timeline_frame,
seq_cache_put_if_possible(
context, strips.last(), timeline_frame, SEQ_CACHE_STORE_FINAL_OUT, out);
}
BLI_mutex_unlock(&seq_render_mutex);
}
BLI_mutex_unlock(&seq_render_mutex);
anim_manager->strip_anims_release(scene, strips);
}
seq_prefetch_start(context, timeline_frame);
anim_manager->manage_anims(scene);
return out;
}

View File

@ -48,3 +48,5 @@ ImBuf *seq_render_mask(const SeqRenderData *context,
float frame_index,
bool make_float);
void seq_imbuf_assign_spaces(const Scene *scene, ImBuf *ibuf);
void seq_render_mutex_lock(void);
void seq_render_mutex_unlock(void);

View File

@ -171,8 +171,6 @@ static void seq_sequence_free_ex(Scene *scene,
seq_free_strip(seq->strip);
}
SEQ_relations_sequence_free_anim(seq);
if (seq->type & SEQ_TYPE_EFFECT) {
SeqEffectHandle sh = SEQ_effect_handle_get(seq);
sh.free(seq, do_id_user);

View File

@ -48,6 +48,7 @@
#include "SEQ_transform.hh"
#include "SEQ_utils.hh"
#include "anim_manager.hh"
#include "multiview.hh"
#include "proxy.hh"
#include "sequencer.hh"
@ -379,63 +380,39 @@ Sequence *SEQ_add_meta_strip(Scene *scene, ListBase *seqbase, SeqLoadData *load_
return seqm;
}
Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data)
Sequence *SEQ_add_movie_strip(Main * /*bmain*/,
Scene *scene,
ListBase *seqbase,
SeqLoadData *load_data)
{
char path[sizeof(load_data->path)];
STRNCPY(path, load_data->path);
BLI_path_abs(path, BKE_main_blendfile_path(bmain));
char colorspace[64] = "\0"; /* MAX_COLORSPACE_NAME */
bool is_multiview_loaded = false;
const int totfiles = seq_num_files(scene, load_data->views_format, load_data->use_multiview);
ImBufAnim **anim_arr = static_cast<ImBufAnim **>(
MEM_callocN(sizeof(ImBufAnim *) * totfiles, "Video files"));
int i;
int orig_width = 0;
int orig_height = 0;
Sequence *seq = SEQ_sequence_alloc(
seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_MOVIE);
Strip *strip = seq->strip;
/* We only need 1 element for MOVIE strips. */
strip->stripdata = static_cast<StripElem *>(MEM_callocN(sizeof(StripElem), "stripelem"));
StripElem *se = strip->stripdata;
BLI_path_split_dir_file(
load_data->path, strip->dirpath, sizeof(strip->dirpath), se->filename, sizeof(se->filename));
if (load_data->use_multiview && (load_data->views_format == R_IMF_VIEWS_INDIVIDUAL)) {
char prefix[FILE_MAX];
const char *ext = nullptr;
size_t j = 0;
AnimManager *manager = seq_anim_manager_ensure(SEQ_editing_get(scene));
manager->strip_anims_acquire(scene, seq);
blender::Vector<ImBufAnim *> anims = manager->strip_anims_get(scene, seq);
BKE_scene_multiview_view_prefix_get(scene, path, prefix, &ext);
if (prefix[0] != '\0') {
for (i = 0; i < totfiles; i++) {
char filepath[FILE_MAX];
seq_multiview_name(scene, i, prefix, ext, filepath, sizeof(filepath));
anim_arr[j] = openanim(filepath, IB_rect, 0, colorspace);
if (anim_arr[j]) {
seq_anim_add_suffix(scene, anim_arr[j], i);
j++;
}
}
is_multiview_loaded = true;
}
}
if (is_multiview_loaded == false) {
anim_arr[0] = openanim(path, IB_rect, 0, colorspace);
}
if (anim_arr[0] == nullptr && !load_data->allow_invalid_file) {
MEM_freeN(anim_arr);
if (anims.size() == 0 && !load_data->allow_invalid_file) {
manager->strip_anims_release(scene, seq);
BLI_remlink(seqbase, seq);
SEQ_sequence_free(scene, seq);
return nullptr;
}
float video_fps = 0.0f;
load_data->r_video_stream_start = 0.0;
if (anim_arr[0] != nullptr) {
if (anims.size() > 0) {
short fps_denom;
float fps_num;
IMB_anim_get_fps(anim_arr[0], true, &fps_denom, &fps_num);
IMB_anim_get_fps(anims[0], true, &fps_denom, &fps_num);
video_fps = fps_denom / fps_num;
strip->stripdata->orig_fps = fps_denom / fps_num;
/* Adjust scene's frame rate settings to match. */
if (load_data->flags & SEQ_LOAD_MOVIE_SYNC_FPS) {
@ -444,12 +421,9 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL
DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_FPS | ID_RECALC_SEQUENCER_STRIPS);
}
load_data->r_video_stream_start = IMD_anim_get_offset(anim_arr[0]);
load_data->r_video_stream_start = IMD_anim_get_offset(anims[0]);
}
Sequence *seq = SEQ_sequence_alloc(
seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_MOVIE);
/* Multiview settings. */
if (load_data->use_multiview) {
seq->flag |= SEQ_USE_VIEWS;
@ -459,31 +433,22 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL
seq->stereo3d_format = load_data->stereo3d_format;
}
for (i = 0; i < totfiles; i++) {
if (anim_arr[i]) {
StripAnim *sanim = static_cast<StripAnim *>(MEM_mallocN(sizeof(StripAnim), "Strip Anim"));
BLI_addtail(&seq->anims, sanim);
sanim->anim = anim_arr[i];
}
else {
break;
}
}
int orig_width = 0;
int orig_height = 0;
if (anims.size() > 0) {
seq->len = IMB_anim_get_duration(anims[0], IMB_TC_RECORD_RUN);
if (anim_arr[0] != nullptr) {
seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN);
IMB_anim_load_metadata(anim_arr[0]);
IMB_anim_load_metadata(anims[0]);
/* Set initial scale based on load_data->fit_method. */
orig_width = IMB_anim_get_image_width(anim_arr[0]);
orig_height = IMB_anim_get_image_height(anim_arr[0]);
orig_width = IMB_anim_get_image_width(anims[0]);
orig_height = IMB_anim_get_image_height(anims[0]);
SEQ_set_scale_to_fit(
seq, orig_width, orig_height, scene->r.xsch, scene->r.ysch, load_data->fit_method);
short frs_sec;
float frs_sec_base;
if (IMB_anim_get_fps(anim_arr[0], true, &frs_sec, &frs_sec_base)) {
if (IMB_anim_get_fps(anims[0], true, &frs_sec, &frs_sec_base)) {
seq->media_playback_rate = float(frs_sec) / frs_sec_base;
}
}
@ -493,23 +458,17 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL
seq->flag |= SEQ_AUTO_PLAYBACK_RATE;
}
STRNCPY(seq->strip->colorspace_settings.name, colorspace);
Strip *strip = seq->strip;
/* We only need 1 element for MOVIE strips. */
StripElem *se;
strip->stripdata = se = static_cast<StripElem *>(MEM_callocN(sizeof(StripElem), "stripelem"));
strip->stripdata->orig_width = orig_width;
strip->stripdata->orig_height = orig_height;
strip->stripdata->orig_fps = video_fps;
BLI_path_split_dir_file(
load_data->path, strip->dirpath, sizeof(strip->dirpath), se->filename, sizeof(se->filename));
char colorspace[64] = "\0"; /* MAX_COLORSPACE_NAME */
STRNCPY(seq->strip->colorspace_settings.name, colorspace);
seq_add_set_view_transform(scene, seq, load_data);
seq_add_set_name(scene, seq, load_data);
seq_add_generic_update(scene, seq);
MEM_freeN(anim_arr);
manager->strip_anims_release(scene, seq);
return seq;
}
@ -550,75 +509,24 @@ void SEQ_add_reload_new_file(Main *bmain, Scene *scene, Sequence *seq, const boo
break;
}
case SEQ_TYPE_MOVIE: {
char filepath[FILE_MAX];
StripAnim *sanim;
bool is_multiview_loaded = false;
const bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 &&
(scene->r.scemode & R_MULTIVIEW) != 0;
AnimManager *manager = seq_anim_manager_ensure(SEQ_editing_get(scene));
manager->free_anims_by_seq(scene, seq);
manager->strip_anims_acquire(scene, seq);
blender::Vector<ImBufAnim *> anims = manager->strip_anims_get(scene, seq);
BLI_path_join(
filepath, sizeof(filepath), seq->strip->dirpath, seq->strip->stripdata->filename);
BLI_path_abs(filepath, BKE_main_blendfile_path_from_global());
SEQ_relations_sequence_free_anim(seq);
if (is_multiview && (seq->views_format == R_IMF_VIEWS_INDIVIDUAL)) {
char prefix[FILE_MAX];
const char *ext = nullptr;
const int totfiles = seq_num_files(scene, seq->views_format, true);
int i = 0;
BKE_scene_multiview_view_prefix_get(scene, filepath, prefix, &ext);
if (prefix[0] != '\0') {
for (i = 0; i < totfiles; i++) {
ImBufAnim *anim;
char filepath_view[FILE_MAX];
seq_multiview_name(scene, i, prefix, ext, filepath_view, sizeof(filepath_view));
anim = openanim(filepath_view,
IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
seq->streamindex,
seq->strip->colorspace_settings.name);
if (anim) {
seq_anim_add_suffix(scene, anim, i);
sanim = static_cast<StripAnim *>(MEM_mallocN(sizeof(StripAnim), "Strip Anim"));
BLI_addtail(&seq->anims, sanim);
sanim->anim = anim;
}
}
is_multiview_loaded = true;
}
}
if (is_multiview_loaded == false) {
ImBufAnim *anim;
anim = openanim(filepath,
IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
seq->streamindex,
seq->strip->colorspace_settings.name);
if (anim) {
sanim = static_cast<StripAnim *>(MEM_mallocN(sizeof(StripAnim), "Strip Anim"));
BLI_addtail(&seq->anims, sanim);
sanim->anim = anim;
}
}
/* use the first video as reference for everything */
sanim = static_cast<StripAnim *>(seq->anims.first);
if ((!sanim) || (!sanim->anim)) {
if (anims.size() == 0) {
return;
}
IMB_anim_load_metadata(sanim->anim);
IMB_anim_load_metadata(anims[0]);
seq->len = IMB_anim_get_duration(
sanim->anim,
anims[0],
IMB_Timecode_Type(seq->strip->proxy ? IMB_Timecode_Type(seq->strip->proxy->tc) :
IMB_TC_RECORD_RUN));
manager->strip_anims_release(scene, seq);
seq->len -= seq->anim_startofs;
seq->len -= seq->anim_endofs;
if (seq->len < 0) {
@ -699,19 +607,24 @@ void SEQ_add_movie_reload_if_needed(
* This function will return true only if there is at least one 'anim' AND all anims can
* produce frames. */
if (BLI_listbase_is_empty(&seq->anims)) {
AnimManager *manager = seq_anim_manager_ensure(SEQ_editing_get(scene));
manager->strip_anims_acquire(scene, seq);
blender::Vector<ImBufAnim *> anims = manager->strip_anims_get(scene, seq);
if (anims.size() == 0) {
/* No anim present, so reloading is always necessary. */
must_reload = true;
}
else {
LISTBASE_FOREACH (StripAnim *, sanim, &seq->anims) {
if (!IMB_anim_can_produce_frames(sanim->anim)) {
for (ImBufAnim *anim : anims) {
if (!IMB_anim_can_produce_frames(anim)) {
/* Anim cannot produce frames, try reloading. */
must_reload = true;
break;
}
};
}
}
manager->strip_anims_release(scene, seq);
if (!must_reload) {
/* There are one or more anims, and all can produce frames. */
@ -723,21 +636,27 @@ void SEQ_add_movie_reload_if_needed(
SEQ_add_reload_new_file(bmain, scene, seq, true);
*r_was_reloaded = true;
if (BLI_listbase_is_empty(&seq->anims)) {
manager->strip_anims_acquire(scene, seq);
anims = manager->strip_anims_get(scene, seq);
if (anims.size() == 0) {
/* No anims present after reloading => no frames can be produced. */
*r_can_produce_frames = false;
manager->strip_anims_release(scene, seq);
return;
}
/* Check if there are still anims that cannot produce frames. */
LISTBASE_FOREACH (StripAnim *, sanim, &seq->anims) {
if (!IMB_anim_can_produce_frames(sanim->anim)) {
for (ImBufAnim *anim : anims) {
if (!IMB_anim_can_produce_frames(anim)) {
/* There still is an anim that cannot produce frames. */
manager->strip_anims_release(scene, seq);
*r_can_produce_frames = false;
return;
}
};
manager->strip_anims_release(scene, seq);
/* There are one or more anims, and all can produce frames. */
*r_can_produce_frames = true;
}

View File

@ -29,6 +29,7 @@
#include "SEQ_sequencer.hh"
#include "SEQ_time.hh"
#include "anim_manager.hh"
#include "effects.hh"
#include "image_cache.hh"
#include "utils.hh"
@ -256,7 +257,7 @@ void SEQ_relations_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render)
if (seq->strip) {
if (seq->type == SEQ_TYPE_MOVIE) {
SEQ_relations_sequence_free_anim(seq);
SEQ_relations_sequence_free_anim(scene, seq);
}
if (seq->type == SEQ_TYPE_SPEED) {
seq_effect_speed_rebuild_map(scene, seq);
@ -272,48 +273,6 @@ void SEQ_relations_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render)
}
}
static void sequencer_all_free_anim_ibufs(const Scene *scene,
ListBase *seqbase,
int timeline_frame,
const int frame_range[2])
{
Editing *ed = SEQ_editing_get(scene);
for (Sequence *seq = static_cast<Sequence *>(seqbase->first); seq != nullptr; seq = seq->next) {
if (!SEQ_time_strip_intersects_frame(scene, seq, timeline_frame) ||
!((frame_range[0] <= timeline_frame) && (frame_range[1] > timeline_frame)))
{
SEQ_relations_sequence_free_anim(seq);
}
if (seq->type == SEQ_TYPE_META) {
int meta_range[2];
MetaStack *ms = SEQ_meta_stack_active_get(ed);
if (ms != nullptr && ms->parseq == seq) {
meta_range[0] = -MAXFRAME;
meta_range[1] = MAXFRAME;
}
else {
/* Limit frame range to meta strip. */
meta_range[0] = max_ii(frame_range[0], SEQ_time_left_handle_frame_get(scene, seq));
meta_range[1] = min_ii(frame_range[1], SEQ_time_right_handle_frame_get(scene, seq));
}
sequencer_all_free_anim_ibufs(scene, &seq->seqbase, timeline_frame, meta_range);
}
}
}
void SEQ_relations_free_all_anim_ibufs(Scene *scene, int timeline_frame)
{
Editing *ed = SEQ_editing_get(scene);
if (ed == nullptr) {
return;
}
const int frame_range[2] = {-MAXFRAME, MAXFRAME};
sequencer_all_free_anim_ibufs(scene, &ed->seqbase, timeline_frame, frame_range);
}
static Sequence *sequencer_check_scene_recursion(Scene *scene, ListBase *seqbase)
{
LISTBASE_FOREACH (Sequence *, seq, seqbase) {
@ -390,21 +349,6 @@ bool SEQ_relations_render_loop_check(Sequence *seq_main, Sequence *seq)
return false;
}
void SEQ_relations_sequence_free_anim(Sequence *seq)
{
while (seq->anims.last) {
StripAnim *sanim = static_cast<StripAnim *>(seq->anims.last);
if (sanim->anim) {
IMB_free_anim(sanim->anim);
sanim->anim = nullptr;
}
BLI_freelinkN(&seq->anims, sanim);
}
BLI_listbase_clear(&seq->anims);
}
void SEQ_relations_session_uid_generate(Sequence *sequence)
{
sequence->runtime.session_uid = BLI_session_uid_generate();
@ -471,3 +415,10 @@ bool SEQ_exists_in_seqbase(const Sequence *seq, const ListBase *seqbase)
}
return false;
}
/* This function frees anim from `seq`. */
void SEQ_relations_sequence_free_anim(const Scene *scene, const Sequence *seq)
{
AnimManager *manager = seq_anim_manager_ensure(SEQ_editing_get(scene));
manager->free_anims_by_seq(scene, seq);
}

View File

@ -31,9 +31,9 @@
#include "SEQ_time.hh"
#include "SEQ_transform.hh"
#include "anim_manager.hh"
#include "sequencer.hh"
#include "strip_time.hh"
#include "utils.hh"
float SEQ_time_media_playback_rate_factor_get(const Scene *scene, const Sequence *seq)
{
@ -326,17 +326,20 @@ float SEQ_time_sequence_get_fps(Scene *scene, Sequence *seq)
{
switch (seq->type) {
case SEQ_TYPE_MOVIE: {
seq_open_anim_file(scene, seq, true);
if (BLI_listbase_is_empty(&seq->anims)) {
return 0.0f;
}
StripAnim *strip_anim = static_cast<StripAnim *>(seq->anims.first);
if (strip_anim->anim == nullptr) {
AnimManager *manager = seq_anim_manager_ensure(SEQ_editing_get(scene));
manager->strip_anims_acquire(scene, seq);
blender::Vector<ImBufAnim *> anims = manager->strip_anims_get(scene, seq);
if (anims.size() == 0) {
manager->strip_anims_release(scene, seq);
return 0.0f;
}
short frs_sec;
float frs_sec_base;
if (IMB_anim_get_fps(strip_anim->anim, true, &frs_sec, &frs_sec_base)) {
if (IMB_anim_get_fps(anims[0], true, &frs_sec, &frs_sec_base)) {
manager->strip_anims_release(scene, seq);
return float(frs_sec) / frs_sec_base;
}
break;
@ -618,3 +621,19 @@ void SEQ_time_slip_strip(const Scene *scene, Sequence *seq, int delta)
{
seq_time_slip_strip_ex(scene, seq, delta, false);
}
int seq_time_distance_from_frame(const Scene *scene, const Sequence *seq, int timeline_frame)
{
const int left_handle = SEQ_time_left_handle_frame_get(scene, seq);
const int right_handle = SEQ_time_right_handle_frame_get(scene, seq);
if (SEQ_time_strip_intersects_frame(scene, seq, timeline_frame)) {
return 0;
}
if (timeline_frame < left_handle) {
return left_handle - timeline_frame;
}
return timeline_frame - right_handle;
}

View File

@ -45,3 +45,4 @@ void seq_time_translate_handles(const Scene *scene, Sequence *seq, const int off
float seq_time_media_playback_rate_factor_get(const Scene *scene, const Sequence *seq);
int seq_time_strip_original_content_length_get(const Scene *scene, const Sequence *seq);
float seq_retiming_evaluate(const Sequence *seq, const float frame_index);
int seq_time_distance_from_frame(const Scene *scene, const Sequence *seq, int timeline_frame);

View File

@ -202,128 +202,6 @@ ListBase *SEQ_get_seqbase_from_sequence(Sequence *seq, ListBase **r_channels, in
return seqbase;
}
static void open_anim_filepath(Sequence *seq,
StripAnim *sanim,
const char *filepath,
bool openfile)
{
if (openfile) {
sanim->anim = openanim(filepath,
IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
seq->streamindex,
seq->strip->colorspace_settings.name);
}
else {
sanim->anim = openanim_noload(filepath,
IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
seq->streamindex,
seq->strip->colorspace_settings.name);
}
}
static bool use_proxy(Editing *ed, Sequence *seq)
{
StripProxy *proxy = seq->strip->proxy;
return proxy && ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) != 0 ||
(ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE));
}
static void proxy_dir_get(Editing *ed, Sequence *seq, size_t str_len, char *r_proxy_dirpath)
{
if (use_proxy(ed, seq)) {
if (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE) {
if (ed->proxy_dir[0] == 0) {
BLI_strncpy(r_proxy_dirpath, "//BL_proxy", str_len);
}
else {
BLI_strncpy(r_proxy_dirpath, ed->proxy_dir, str_len);
}
}
else {
BLI_strncpy(r_proxy_dirpath, seq->strip->proxy->dirpath, str_len);
}
BLI_path_abs(r_proxy_dirpath, BKE_main_blendfile_path_from_global());
}
}
static void index_dir_set(Editing *ed, Sequence *seq, StripAnim *sanim)
{
if (sanim->anim == nullptr || !use_proxy(ed, seq)) {
return;
}
char proxy_dirpath[FILE_MAX];
proxy_dir_get(ed, seq, sizeof(proxy_dirpath), proxy_dirpath);
seq_proxy_index_dir_set(sanim->anim, proxy_dirpath);
}
static bool open_anim_file_multiview(Scene *scene, Sequence *seq, char *filepath, bool openfile)
{
char prefix[FILE_MAX];
const char *ext = nullptr;
BKE_scene_multiview_view_prefix_get(scene, filepath, prefix, &ext);
if (seq->views_format != R_IMF_VIEWS_INDIVIDUAL || prefix[0] == '\0') {
return false;
}
Editing *ed = scene->ed;
bool is_multiview_loaded = false;
int totfiles = seq_num_files(scene, seq->views_format, true);
for (int i = 0; i < totfiles; i++) {
const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, i);
char filepath_view[FILE_MAX];
SNPRINTF(filepath_view, "%s%s%s", prefix, suffix, ext);
StripAnim *sanim = static_cast<StripAnim *>(MEM_mallocN(sizeof(StripAnim), "Strip Anim"));
open_anim_filepath(seq, sanim, filepath_view, openfile);
if (sanim->anim == nullptr) {
MEM_freeN(sanim);
return false; /* Multiview render failed. */
}
index_dir_set(ed, seq, sanim);
BLI_addtail(&seq->anims, sanim);
IMB_suffix_anim(sanim->anim, suffix);
is_multiview_loaded = true;
}
return is_multiview_loaded;
}
void seq_open_anim_file(Scene *scene, Sequence *seq, bool openfile)
{
if ((seq->anims.first != nullptr) && (((StripAnim *)seq->anims.first)->anim != nullptr) &&
!openfile)
{
return;
}
/* Reset all the previously created anims. */
SEQ_relations_sequence_free_anim(seq);
Editing *ed = scene->ed;
char filepath[FILE_MAX];
BLI_path_join(filepath, sizeof(filepath), seq->strip->dirpath, seq->strip->stripdata->filename);
BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&scene->id));
bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && (scene->r.scemode & R_MULTIVIEW) != 0;
bool multiview_is_loaded = false;
if (is_multiview) {
multiview_is_loaded = open_anim_file_multiview(scene, seq, filepath, openfile);
}
if (!is_multiview || !multiview_is_loaded) {
StripAnim *sanim = static_cast<StripAnim *>(MEM_mallocN(sizeof(StripAnim), "Strip Anim"));
BLI_addtail(&seq->anims, sanim);
open_anim_filepath(seq, sanim, filepath, openfile);
index_dir_set(ed, seq, sanim);
}
}
const Sequence *SEQ_get_topmost_sequence(const Scene *scene, int frame)
{
Editing *ed = scene->ed;

View File

@ -12,5 +12,4 @@ struct ListBase;
struct Scene;
bool sequencer_seq_generates_image(Sequence *seq);
void seq_open_anim_file(Scene *scene, Sequence *seq, bool openfile);
Sequence *SEQ_get_meta_by_seqbase(ListBase *seqbase_main, ListBase *meta_seqbase);

@ -1 +1 @@
Subproject commit 4f2e16db7ce3bdea79f63c50a9bd5a645f23037f
Subproject commit 753727081d73e1469f45fb9b36ad081c12cfcfab