[WIP] VSE: share ImBufAnim resource between strips #118670
|
@ -1 +1 @@
|
|||
Subproject commit d2eea8a8a6b22d6ec6849deea72e141fc5b384d4
|
||||
Subproject commit d983ed32a1e760130ba31ad8a98a94ea943267a2
|
|
@ -1 +1 @@
|
|||
Subproject commit 38a8f38d98987efc9aebe878ca941d99756e914e
|
||||
Subproject commit 7181d6dccb9fe4184340f9f5b1c381f8089fe4ec
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -29,7 +29,6 @@ class SequenceBackup {
|
|||
bool isEmpty() const;
|
||||
|
||||
void *scene_sound;
|
||||
ListBase anims;
|
||||
};
|
||||
|
||||
} // namespace blender::deg
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -862,7 +862,10 @@ 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;
|
||||
}
|
||||
|
||||
|
@ -877,7 +880,7 @@ static PointerRNA rna_MovieSequence_metadata_get(ID *scene_id, Sequence *seq)
|
|||
}
|
||||
|
||||
PointerRNA ptr = RNA_pointer_create(scene_id, &RNA_IDPropertyWrapPtr, metadata);
|
||||
return ptr;
|
||||
return ptr;*/
|
||||
}
|
||||
|
||||
static PointerRNA rna_SequenceEditor_meta_stack_get(CollectionPropertyIterator *iter)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue