VSE: Python API, allow creation of VSE Movie strips with missing file

It was already possible to create Sound and Image strips that reference
non-existing files. Now it's also possible to create Movie strips
referencing missing files via the Python API call
`Sequences.new_movie()`. In this case, the duration of the strip will be
set to 1 frame.

Note that this commit does not change anything in the user interface.

The Python API of the `MovieStrip` class is extended with a function
`reload_if_needed()`. This function only performs disk I/O if the movie
strip cannot produce frames, that is either when its filepath points to
a non-existing file, or when the video sequence editor has not been
shown yet (for example because it is in an inactive workspace).

This allows for the following:

```
import bpy

scene = bpy.context.scene
vse = scene.sequence_editor_create()

filepath = bpy.path.abspath('//demo.mkv')
strip = vse.sequences.new_movie("movie", filepath,
    channel=2,
    frame_start=47,
    file_must_exist=False)
strip.frame_final_end = 327
```

This will create a new movie strip, even when `demo.mkv` does not exist.

Once `demo.mkv` has appeared at the expected location, either
`strip.reload_if_needed()` or `strip.filepath = strip.filepath` will
load it.

Differential Revision: https://developer.blender.org/D8257

Reviewed By: Sergey, ISS
This commit is contained in:
Sybren A. Stüvel 2020-07-13 15:02:25 +02:00
parent 9db0c36af1
commit b9f565881e
Notes: blender-bot 2023-02-14 08:38:14 +01:00
Referenced by issue #78768, Allow creation of VSE strips referencing a non-existent file
6 changed files with 120 additions and 20 deletions

View File

@ -285,6 +285,11 @@ void BKE_sequence_reload_new_file(struct Main *bmain,
struct Scene *scene,
struct Sequence *seq,
const bool lock_range);
void BKE_sequence_movie_reload_if_needed(struct Main *bmain,
struct Scene *scene,
struct Sequence *seq,
bool *r_was_reloaded,
bool *r_can_produce_frames);
int BKE_sequencer_evaluate_frame(struct Scene *scene, int cfra);
int BKE_sequencer_get_shown_sequences(struct ListBase *seqbasep,
int cfra,

View File

@ -1090,6 +1090,64 @@ void BKE_sequence_reload_new_file(Main *bmain, Scene *scene, Sequence *seq, cons
BKE_sequence_calc(scene, seq);
}
void BKE_sequence_movie_reload_if_needed(struct Main *bmain,
struct Scene *scene,
struct Sequence *seq,
bool *r_was_reloaded,
bool *r_can_produce_frames)
{
BLI_assert(seq->type == SEQ_TYPE_MOVIE ||
!"This function is only implemented for movie strips.");
bool must_reload = false;
/* The Sequence struct allows for multiple anim structs to be associated with one strip. 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)) {
/* 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)) {
/* Anim cannot produce frames, try reloading. */
must_reload = true;
break;
}
};
}
if (!must_reload) {
/* There are one or more anims, and all can produce frames. */
*r_was_reloaded = false;
*r_can_produce_frames = true;
return;
}
BKE_sequence_reload_new_file(bmain, scene, seq, true);
*r_was_reloaded = true;
if (BLI_listbase_is_empty(&seq->anims)) {
/* No anims present after reloading => no frames can be produced. */
*r_can_produce_frames = false;
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)) {
/* There still is an anim that cannot produce frames. */
*r_can_produce_frames = false;
return;
}
};
/* There are one or more anims, and all can produce frames. */
*r_can_produce_frames = true;
}
void BKE_sequencer_sort(Scene *scene)
{
/* all strips together per kind, and in order of y location ("machine") */

View File

@ -367,6 +367,7 @@ struct anim *IMB_open_anim(const char *name,
void IMB_suffix_anim(struct anim *anim, const char *suffix);
void IMB_close_anim(struct anim *anim);
void IMB_close_anim_proxies(struct anim *anim);
bool IMB_anim_can_produce_frames(const struct anim *anim);
/**
*

View File

@ -304,6 +304,21 @@ struct anim *IMB_open_anim(const char *name,
return (anim);
}
bool IMB_anim_can_produce_frames(const struct anim *anim)
{
#ifdef WITH_AVI
if (anim->avi != NULL) {
return true;
}
#endif
#ifdef WITH_FFMPEG
if (anim->pCodecCtx != NULL) {
return true;
}
#endif
return false;
}
void IMB_suffix_anim(struct anim *anim, const char *suffix)
{
BLI_strncpy(anim->suffix, suffix, sizeof(anim->suffix));

View File

@ -749,6 +749,25 @@ static IDProperty *rna_Sequence_idprops(PointerRNA *ptr, bool create)
return seq->prop;
}
static bool rna_MovieSequence_reload_if_needed(ID *scene_id, Sequence *seq, Main *bmain)
{
Scene *scene = (Scene *)scene_id;
bool has_reloaded;
bool can_produce_frames;
BKE_sequence_movie_reload_if_needed(bmain, scene, seq, &has_reloaded, &can_produce_frames);
if (has_reloaded && can_produce_frames) {
BKE_sequence_calc(scene, seq);
BKE_sequence_invalidate_cache_raw(scene, seq);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene);
}
return can_produce_frames;
}
static PointerRNA rna_MovieSequence_metadata_get(Sequence *seq)
{
if (seq == NULL || seq->anims.first == NULL) {
@ -2385,6 +2404,13 @@ static void rna_def_movie(BlenderRNA *brna)
"rna_Sequence_filepath_set");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_filepath_update");
func = RNA_def_function(srna, "reload_if_needed", "rna_MovieSequence_reload_if_needed");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
/* return type */
parm = RNA_def_boolean(
func, "can_produce_frames", 0, "True if the strip can produce frames, False otherwise", "");
RNA_def_function_return(func, parm);
/* metadata */
func = RNA_def_function(srna, "metadata", "rna_MovieSequence_metadata_get");
RNA_def_function_ui_description(func, "Retrieve metadata of the movie file");

View File

@ -205,33 +205,28 @@ static Sequence *rna_Sequences_new_image(ID *id,
return seq;
}
static Sequence *rna_Sequences_new_movie(ID *id,
Editing *ed,
ReportList *reports,
const char *name,
const char *file,
int channel,
int frame_start)
static Sequence *rna_Sequences_new_movie(
ID *id, Editing *ed, const char *name, const char *file, int channel, int frame_start)
{
Scene *scene = (Scene *)id;
Sequence *seq;
StripAnim *sanim;
struct anim *an = openanim(file, IB_rect, 0, NULL);
if (an == NULL) {
BKE_report(reports, RPT_ERROR, "Sequences.new_movie: unable to open movie file");
return NULL;
}
seq = alloc_generic_sequence(ed, name, frame_start, channel, SEQ_TYPE_MOVIE, file);
sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim");
BLI_addtail(&seq->anims, sanim);
sanim->anim = an;
struct anim *an = openanim(file, IB_rect, 0, NULL);
if (an == NULL) {
/* Without anim, the strip gets duration 0, which makes it impossible to select in the UI. */
seq->len = 1;
}
else {
sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim");
BLI_addtail(&seq->anims, sanim);
sanim->anim = an;
seq->anim_preseek = IMB_anim_get_preseek(an);
seq->len = IMB_anim_get_duration(an, IMB_TC_RECORD_RUN);
seq->anim_preseek = IMB_anim_get_preseek(an);
seq->len = IMB_anim_get_duration(an, IMB_TC_RECORD_RUN);
}
BKE_sequence_calc_disp(scene, seq);
BKE_sequence_invalidate_cache_composite(scene, seq);
@ -667,7 +662,7 @@ void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "new_movie", "rna_Sequences_new_movie");
RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_SELF_ID);
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
RNA_def_function_ui_description(func, "Add a new movie sequence");
parm = RNA_def_string(func, "name", "Name", 0, "", "Name for the new sequence");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);