This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/sequencer/intern/proxy.c
Richard Antalik 302b04a5a3 VSE: Improved Retiming system
Patch implements better way to control playback speed than it is
possible to do with speed effect. Speed factor property can be set in
Time panel.

There are 2 layers of control:

Option to retime movie to match scene FPS rate.
Custom speed factor to control playback rate.
Since playback rate is strip property, it is now possible to manipulate
strip as normal one even if it is retimed.

To facilitate manipulation, some functions need to consider speed factor
and apply necessary corrections to strip offset or strip start. These
corrections may need to be float numbers, so start and offsets must be
float as well.

Sound strips now use speed factor instead of pitch. This means, that
strips will change length to match usable length. In addition, it is
possible to group movie and sound strip and change speed of meta strip.
2022-06-29 12:48:34 +02:00

606 lines
16 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2001-2002 NaN Holding BV. All rights reserved.
* 2003-2009 Blender Foundation.
* 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de> */
/** \file
* \ingroup bke
*/
#include "MEM_guardedalloc.h"
#include "DNA_anim_types.h"
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
#include "DNA_space_types.h"
#include "BLI_fileops.h"
#include "BLI_listbase.h"
#include "BLI_path_util.h"
#include "BLI_session_uuid.h"
#include "BLI_string.h"
#ifdef WIN32
# include "BLI_winstuff.h"
#else
# include <unistd.h>
#endif
#include "BKE_global.h"
#include "BKE_image.h"
#include "BKE_main.h"
#include "BKE_scene.h"
#include "DEG_depsgraph.h"
#include "IMB_colormanagement.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "IMB_metadata.h"
#include "SEQ_iterator.h"
#include "SEQ_proxy.h"
#include "SEQ_relations.h"
#include "SEQ_render.h"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
#include "multiview.h"
#include "proxy.h"
#include "render.h"
#include "sequencer.h"
#include "strip_time.h"
#include "utils.h"
typedef struct SeqIndexBuildContext {
struct IndexBuildContext *index_context;
int tc_flags;
int size_flags;
int quality;
bool overwrite;
int view_id;
Main *bmain;
Depsgraph *depsgraph;
Scene *scene;
Sequence *seq, *orig_seq;
SessionUUID orig_seq_uuid;
} SeqIndexBuildContext;
int SEQ_rendersize_to_proxysize(int render_size)
{
switch (render_size) {
case SEQ_RENDER_SIZE_PROXY_25:
return IMB_PROXY_25;
case SEQ_RENDER_SIZE_PROXY_50:
return IMB_PROXY_50;
case SEQ_RENDER_SIZE_PROXY_75:
return IMB_PROXY_75;
case SEQ_RENDER_SIZE_PROXY_100:
return IMB_PROXY_100;
}
return IMB_PROXY_NONE;
}
double SEQ_rendersize_to_scale_factor(int render_size)
{
switch (render_size) {
case SEQ_RENDER_SIZE_PROXY_25:
return 0.25;
case SEQ_RENDER_SIZE_PROXY_50:
return 0.50;
case SEQ_RENDER_SIZE_PROXY_75:
return 0.75;
}
return 1.0;
}
bool seq_proxy_get_custom_file_fname(Sequence *seq, char *name, const int view_id)
{
char fname[FILE_MAXFILE];
char suffix[24];
StripProxy *proxy = seq->strip->proxy;
if (proxy == NULL) {
return false;
}
BLI_join_dirfile(fname, PROXY_MAXFILE, proxy->dir, proxy->file);
BLI_path_abs(fname, BKE_main_blendfile_path_from_global());
if (view_id > 0) {
BLI_snprintf(suffix, sizeof(suffix), "_%d", view_id);
/* TODO(sergey): This will actually append suffix after extension
* which is weird but how was originally coded in multi-view branch.
*/
BLI_snprintf(name, PROXY_MAXFILE, "%s_%s", fname, suffix);
}
else {
BLI_strncpy(name, fname, PROXY_MAXFILE);
}
return true;
}
static bool seq_proxy_get_fname(Scene *scene,
Sequence *seq,
int timeline_frame,
eSpaceSeq_Proxy_RenderSize render_size,
char *name,
const int view_id)
{
char dir[PROXY_MAXFILE];
char suffix[24] = {'\0'};
Editing *ed = SEQ_editing_get(scene);
StripProxy *proxy = seq->strip->proxy;
if (proxy == NULL) {
return false;
}
/* Multi-view suffix. */
if (view_id > 0) {
BLI_snprintf(suffix, sizeof(suffix), "_%d", view_id);
}
/* Per strip with Custom file situation is handled separately. */
if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE &&
ed->proxy_storage != SEQ_EDIT_PROXY_DIR_STORAGE) {
if (seq_proxy_get_custom_file_fname(seq, name, view_id)) {
return true;
}
}
if (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE) {
/* Per project default. */
if (ed->proxy_dir[0] == 0) {
BLI_strncpy(dir, "//BL_proxy", sizeof(dir));
}
else { /* Per project with custom dir. */
BLI_strncpy(dir, ed->proxy_dir, sizeof(dir));
}
BLI_path_abs(name, BKE_main_blendfile_path_from_global());
}
else {
/* Pre strip with custom dir. */
if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) {
BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir));
}
else { /* Per strip default. */
BLI_snprintf(dir, PROXY_MAXFILE, "%s/BL_proxy", seq->strip->dir);
}
}
/* Proxy size number to be used in path. */
int proxy_size_number = SEQ_rendersize_to_scale_factor(render_size) * 100;
BLI_snprintf(name,
PROXY_MAXFILE,
"%s/images/%d/%s_proxy%s",
dir,
proxy_size_number,
SEQ_render_give_stripelem(scene, seq, timeline_frame)->name,
suffix);
BLI_path_abs(name, BKE_main_blendfile_path_from_global());
strcat(name, ".jpg");
return true;
}
bool SEQ_can_use_proxy(const struct SeqRenderData *context, Sequence *seq, int psize)
{
if (seq->strip->proxy == NULL || !context->use_proxies) {
return false;
}
short size_flags = seq->strip->proxy->build_size_flags;
return (seq->flag & SEQ_USE_PROXY) != 0 && psize != IMB_PROXY_NONE && (size_flags & psize) != 0;
}
ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int timeline_frame)
{
char name[PROXY_MAXFILE];
StripProxy *proxy = seq->strip->proxy;
const eSpaceSeq_Proxy_RenderSize psize = 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))) {
return NULL;
}
if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE) {
int frameno = (int)seq_give_frame_index(context->scene, seq, timeline_frame) +
seq->anim_startofs;
if (proxy->anim == NULL) {
if (seq_proxy_get_fname(
context->scene, seq, timeline_frame, psize, name, context->view_id) == 0) {
return NULL;
}
proxy->anim = openanim(name, IB_rect, 0, seq->strip->colorspace_settings.name);
}
if (proxy->anim == NULL) {
return NULL;
}
seq_open_anim_file(context->scene, seq, true);
sanim = seq->anims.first;
frameno = IMB_anim_index_get_frame_index(
sanim ? sanim->anim : NULL, seq->strip->proxy->tc, frameno);
return IMB_anim_absolute(proxy->anim, frameno, IMB_TC_NONE, IMB_PROXY_NONE);
}
if (seq_proxy_get_fname(context->scene, seq, timeline_frame, psize, name, context->view_id) ==
0) {
return NULL;
}
if (BLI_exists(name)) {
ImBuf *ibuf = IMB_loadiffname(name, IB_rect, NULL);
if (ibuf) {
seq_imbuf_assign_spaces(context->scene, ibuf);
}
return ibuf;
}
return NULL;
}
static void seq_proxy_build_frame(const SeqRenderData *context,
SeqRenderState *state,
Sequence *seq,
int timeline_frame,
int proxy_render_size,
const bool overwrite)
{
char name[PROXY_MAXFILE];
int quality;
int rectx, recty;
ImBuf *ibuf_tmp, *ibuf;
Scene *scene = context->scene;
if (!seq_proxy_get_fname(
scene, seq, timeline_frame, proxy_render_size, name, context->view_id)) {
return;
}
if (!overwrite && BLI_exists(name)) {
return;
}
ibuf_tmp = seq_render_strip(context, state, seq, timeline_frame);
rectx = (proxy_render_size * ibuf_tmp->x) / 100;
recty = (proxy_render_size * ibuf_tmp->y) / 100;
if (ibuf_tmp->x != rectx || ibuf_tmp->y != recty) {
ibuf = IMB_dupImBuf(ibuf_tmp);
IMB_metadata_copy(ibuf, ibuf_tmp);
IMB_freeImBuf(ibuf_tmp);
IMB_scalefastImBuf(ibuf, (short)rectx, (short)recty);
}
else {
ibuf = ibuf_tmp;
}
/* depth = 32 is intentionally left in, otherwise ALPHA channels
* won't work... */
quality = seq->strip->proxy->quality;
ibuf->ftype = IMB_FTYPE_JPG;
ibuf->foptions.quality = quality;
/* unsupported feature only confuses other s/w */
if (ibuf->planes == 32) {
ibuf->planes = 24;
}
BLI_make_existing_file(name);
const bool ok = IMB_saveiff(ibuf, name, IB_rect | IB_zbuf | IB_zbuffloat);
if (ok == false) {
perror(name);
}
IMB_freeImBuf(ibuf);
}
/**
* Returns whether the file this context would read from even exist,
* if not, don't create the context
*/
static bool seq_proxy_multiview_context_invalid(Sequence *seq, Scene *scene, const int view_id)
{
if ((scene->r.scemode & R_MULTIVIEW) == 0) {
return false;
}
if ((seq->type == SEQ_TYPE_IMAGE) && (seq->views_format == R_IMF_VIEWS_INDIVIDUAL)) {
static char prefix[FILE_MAX];
static const char *ext = NULL;
char str[FILE_MAX];
if (view_id == 0) {
char path[FILE_MAX];
BLI_join_dirfile(path, sizeof(path), seq->strip->dir, seq->strip->stripdata->name);
BLI_path_abs(path, BKE_main_blendfile_path_from_global());
BKE_scene_multiview_view_prefix_get(scene, path, prefix, &ext);
}
else {
prefix[0] = '\0';
}
if (prefix[0] == '\0') {
return view_id != 0;
}
seq_multiview_name(scene, view_id, prefix, ext, str, FILE_MAX);
if (BLI_access(str, R_OK) == 0) {
return false;
}
return view_id != 0;
}
return false;
}
/**
* This returns the maximum possible number of required contexts
*/
static int seq_proxy_context_count(Sequence *seq, Scene *scene)
{
int num_views = 1;
if ((scene->r.scemode & R_MULTIVIEW) == 0) {
return 1;
}
switch (seq->type) {
case SEQ_TYPE_MOVIE: {
num_views = BLI_listbase_count(&seq->anims);
break;
}
case SEQ_TYPE_IMAGE: {
switch (seq->views_format) {
case R_IMF_VIEWS_INDIVIDUAL:
num_views = BKE_scene_multiview_num_views_get(&scene->r);
break;
case R_IMF_VIEWS_STEREO_3D:
num_views = 2;
break;
case R_IMF_VIEWS_MULTIVIEW:
/* not supported at the moment */
/* pass through */
default:
num_views = 1;
}
break;
}
}
return num_views;
}
static bool seq_proxy_need_rebuild(Sequence *seq, struct anim *anim)
{
if ((seq->strip->proxy->build_flags & SEQ_PROXY_SKIP_EXISTING) == 0) {
return true;
}
IMB_Proxy_Size required_proxies = seq->strip->proxy->build_size_flags;
IMB_Proxy_Size built_proxies = IMB_anim_proxy_get_existing(anim);
return (required_proxies & built_proxies) != required_proxies;
}
bool SEQ_proxy_rebuild_context(Main *bmain,
Depsgraph *depsgraph,
Scene *scene,
Sequence *seq,
struct GSet *file_list,
ListBase *queue,
bool build_only_on_bad_performance)
{
SeqIndexBuildContext *context;
Sequence *nseq;
LinkData *link;
int num_files;
int i;
if (!seq->strip || !seq->strip->proxy) {
return true;
}
if (!(seq->flag & SEQ_USE_PROXY)) {
return true;
}
num_files = seq_proxy_context_count(seq, scene);
for (i = 0; i < num_files; i++) {
if (seq_proxy_multiview_context_invalid(seq, scene, i)) {
continue;
}
/* 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 = BLI_findlink(&seq->anims, i);
if (sanim->anim && !seq_proxy_need_rebuild(seq, sanim->anim)) {
continue;
}
SEQ_relations_sequence_free_anim(seq);
context = MEM_callocN(sizeof(SeqIndexBuildContext), "seq proxy rebuild context");
nseq = SEQ_sequence_dupli_recursive(scene, scene, NULL, seq, 0);
context->tc_flags = nseq->strip->proxy->build_tc_flags;
context->size_flags = nseq->strip->proxy->build_size_flags;
context->quality = nseq->strip->proxy->quality;
context->overwrite = (nseq->strip->proxy->build_flags & SEQ_PROXY_SKIP_EXISTING) == 0;
context->bmain = bmain;
context->depsgraph = depsgraph;
context->scene = scene;
context->orig_seq = seq;
context->orig_seq_uuid = seq->runtime.session_uuid;
context->seq = nseq;
context->view_id = i; /* only for images */
if (nseq->type == SEQ_TYPE_MOVIE) {
seq_open_anim_file(scene, nseq, true);
sanim = BLI_findlink(&nseq->anims, i);
if (sanim->anim) {
context->index_context = IMB_anim_index_rebuild_context(sanim->anim,
context->tc_flags,
context->size_flags,
context->quality,
context->overwrite,
file_list,
build_only_on_bad_performance);
}
if (!context->index_context) {
MEM_freeN(context);
return false;
}
}
link = BLI_genericNodeN(context);
BLI_addtail(queue, link);
}
return true;
}
void SEQ_proxy_rebuild(SeqIndexBuildContext *context,
short *stop,
short *do_update,
float *progress)
{
const bool overwrite = context->overwrite;
SeqRenderData render_context;
Sequence *seq = context->seq;
Scene *scene = context->scene;
Main *bmain = context->bmain;
int timeline_frame;
if (seq->type == SEQ_TYPE_MOVIE) {
if (context->index_context) {
IMB_anim_index_rebuild(context->index_context, stop, do_update, progress);
}
return;
}
if (!(seq->flag & SEQ_USE_PROXY)) {
return;
}
/* that's why it is called custom... */
if (seq->strip->proxy && seq->strip->proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE) {
return;
}
/* fail safe code */
SEQ_render_new_render_data(bmain,
context->depsgraph,
context->scene,
roundf((scene->r.size * (float)scene->r.xsch) / 100.0f),
roundf((scene->r.size * (float)scene->r.ysch) / 100.0f),
100,
false,
&render_context);
render_context.skip_cache = true;
render_context.is_proxy_render = true;
render_context.view_id = context->view_id;
SeqRenderState state;
seq_render_state_init(&state);
for (timeline_frame = SEQ_time_left_handle_frame_get(scene, seq);
timeline_frame < SEQ_time_right_handle_frame_get(scene, seq);
timeline_frame++) {
if (context->size_flags & IMB_PROXY_25) {
seq_proxy_build_frame(&render_context, &state, seq, timeline_frame, 25, overwrite);
}
if (context->size_flags & IMB_PROXY_50) {
seq_proxy_build_frame(&render_context, &state, seq, timeline_frame, 50, overwrite);
}
if (context->size_flags & IMB_PROXY_75) {
seq_proxy_build_frame(&render_context, &state, seq, timeline_frame, 75, overwrite);
}
if (context->size_flags & IMB_PROXY_100) {
seq_proxy_build_frame(&render_context, &state, seq, timeline_frame, 100, overwrite);
}
*progress = (float)(timeline_frame - SEQ_time_left_handle_frame_get(scene, seq)) /
(SEQ_time_right_handle_frame_get(scene, seq) -
SEQ_time_left_handle_frame_get(scene, seq));
*do_update = true;
if (*stop || G.is_break) {
break;
}
}
}
void SEQ_proxy_rebuild_finish(SeqIndexBuildContext *context, bool stop)
{
if (context->index_context) {
StripAnim *sanim;
for (sanim = context->seq->anims.first; sanim; sanim = sanim->next) {
IMB_close_anim_proxies(sanim->anim);
}
IMB_anim_index_rebuild_finish(context->index_context, stop);
}
seq_free_sequence_recurse(NULL, context->seq, true);
MEM_freeN(context);
}
void SEQ_proxy_set(struct Sequence *seq, bool value)
{
if (value) {
seq->flag |= SEQ_USE_PROXY;
if (seq->strip->proxy == NULL) {
seq->strip->proxy = seq_strip_proxy_alloc();
}
}
else {
seq->flag &= ~SEQ_USE_PROXY;
}
}
void seq_proxy_index_dir_set(struct anim *anim, const char *base_dir)
{
char dir[FILE_MAX];
char fname[FILE_MAXFILE];
IMB_anim_get_fname(anim, fname, FILE_MAXFILE);
BLI_strncpy(dir, base_dir, sizeof(dir));
BLI_path_append(dir, sizeof(dir), fname);
IMB_anim_set_index_dir(anim, dir);
}
void free_proxy_seq(Sequence *seq)
{
if (seq->strip && seq->strip->proxy && seq->strip->proxy->anim) {
IMB_free_anim(seq->strip->proxy->anim);
seq->strip->proxy->anim = NULL;
}
}