438 lines
12 KiB
C
438 lines
12 KiB
C
/*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
|
|
* All rights reserved.
|
|
*
|
|
* - Blender Foundation, 2003-2009
|
|
* - Peter Schlaile <peter [at] schlaile [dot] de> 2005/2006
|
|
*/
|
|
|
|
/** \file
|
|
* \ingroup bke
|
|
*/
|
|
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_sequence_types.h"
|
|
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_math.h"
|
|
|
|
#include "BKE_movieclip.h"
|
|
#include "BKE_scene.h"
|
|
#include "BKE_sound.h"
|
|
|
|
#include "IMB_imbuf.h"
|
|
|
|
#include "SEQ_render.h"
|
|
#include "SEQ_sequencer.h"
|
|
#include "SEQ_time.h"
|
|
|
|
#include "strip_time.h"
|
|
#include "utils.h"
|
|
|
|
float seq_give_frame_index(Sequence *seq, float timeline_frame)
|
|
{
|
|
float frame_index;
|
|
int sta = seq->start;
|
|
int end = seq->start + seq->len - 1;
|
|
|
|
if (seq->type & SEQ_TYPE_EFFECT) {
|
|
end = seq->enddisp;
|
|
}
|
|
|
|
if (end < sta) {
|
|
return -1;
|
|
}
|
|
|
|
if (seq->flag & SEQ_REVERSE_FRAMES) {
|
|
/*reverse frame in this sequence */
|
|
if (timeline_frame <= sta) {
|
|
frame_index = end - sta;
|
|
}
|
|
else if (timeline_frame >= end) {
|
|
frame_index = 0;
|
|
}
|
|
else {
|
|
frame_index = end - timeline_frame;
|
|
}
|
|
}
|
|
else {
|
|
if (timeline_frame <= sta) {
|
|
frame_index = 0;
|
|
}
|
|
else if (timeline_frame >= end) {
|
|
frame_index = end - sta;
|
|
}
|
|
else {
|
|
frame_index = timeline_frame - sta;
|
|
}
|
|
}
|
|
|
|
if (seq->strobe < 1.0f) {
|
|
seq->strobe = 1.0f;
|
|
}
|
|
|
|
if (seq->strobe > 1.0f) {
|
|
frame_index -= fmodf((double)frame_index, (double)seq->strobe);
|
|
}
|
|
|
|
return frame_index;
|
|
}
|
|
|
|
static int metaseq_start(Sequence *metaseq)
|
|
{
|
|
return metaseq->start + metaseq->startofs;
|
|
}
|
|
|
|
static int metaseq_end(Sequence *metaseq)
|
|
{
|
|
return metaseq->start + metaseq->len - metaseq->endofs;
|
|
}
|
|
|
|
static void seq_update_sound_bounds_recursive_impl(Scene *scene,
|
|
Sequence *metaseq,
|
|
int start,
|
|
int end)
|
|
{
|
|
Sequence *seq;
|
|
|
|
/* for sound we go over full meta tree to update bounds of the sound strips,
|
|
* since sound is played outside of evaluating the imbufs, */
|
|
for (seq = metaseq->seqbase.first; seq; seq = seq->next) {
|
|
if (seq->type == SEQ_TYPE_META) {
|
|
seq_update_sound_bounds_recursive_impl(
|
|
scene, seq, max_ii(start, metaseq_start(seq)), min_ii(end, metaseq_end(seq)));
|
|
}
|
|
else if (ELEM(seq->type, SEQ_TYPE_SOUND_RAM, SEQ_TYPE_SCENE)) {
|
|
if (seq->scene_sound) {
|
|
int startofs = seq->startofs;
|
|
int endofs = seq->endofs;
|
|
if (seq->startofs + seq->start < start) {
|
|
startofs = start - seq->start;
|
|
}
|
|
|
|
if (seq->start + seq->len - seq->endofs > end) {
|
|
endofs = seq->start + seq->len - end;
|
|
}
|
|
|
|
BKE_sound_move_scene_sound(scene,
|
|
seq->scene_sound,
|
|
seq->start + startofs,
|
|
seq->start + seq->len - endofs,
|
|
startofs + seq->anim_startofs);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void seq_update_sound_bounds_recursive(Scene *scene, Sequence *metaseq)
|
|
{
|
|
seq_update_sound_bounds_recursive_impl(
|
|
scene, metaseq, metaseq_start(metaseq), metaseq_end(metaseq));
|
|
}
|
|
|
|
void SEQ_time_update_sequence_bounds(Scene *scene, Sequence *seq)
|
|
{
|
|
if (seq->startofs && seq->startstill) {
|
|
seq->startstill = 0;
|
|
}
|
|
if (seq->endofs && seq->endstill) {
|
|
seq->endstill = 0;
|
|
}
|
|
|
|
seq->startdisp = seq->start + seq->startofs - seq->startstill;
|
|
seq->enddisp = seq->start + seq->len - seq->endofs + seq->endstill;
|
|
|
|
if (seq->type == SEQ_TYPE_META) {
|
|
seq_update_sound_bounds_recursive(scene, seq);
|
|
}
|
|
}
|
|
|
|
void SEQ_time_update_sequence(Scene *scene, Sequence *seq)
|
|
{
|
|
Sequence *seqm;
|
|
int min, max;
|
|
|
|
/* check all metas recursively */
|
|
seqm = seq->seqbase.first;
|
|
while (seqm) {
|
|
if (seqm->seqbase.first) {
|
|
SEQ_time_update_sequence(scene, seqm);
|
|
}
|
|
seqm = seqm->next;
|
|
}
|
|
|
|
/* effects and meta: automatic start and end */
|
|
if (seq->type & SEQ_TYPE_EFFECT) {
|
|
if (seq->seq1) {
|
|
seq->startofs = seq->endofs = seq->startstill = seq->endstill = 0;
|
|
if (seq->seq3) {
|
|
seq->start = seq->startdisp = max_iii(
|
|
seq->seq1->startdisp, seq->seq2->startdisp, seq->seq3->startdisp);
|
|
seq->enddisp = min_iii(seq->seq1->enddisp, seq->seq2->enddisp, seq->seq3->enddisp);
|
|
}
|
|
else if (seq->seq2) {
|
|
seq->start = seq->startdisp = max_ii(seq->seq1->startdisp, seq->seq2->startdisp);
|
|
seq->enddisp = min_ii(seq->seq1->enddisp, seq->seq2->enddisp);
|
|
}
|
|
else {
|
|
seq->start = seq->startdisp = seq->seq1->startdisp;
|
|
seq->enddisp = seq->seq1->enddisp;
|
|
}
|
|
/* we cant help if strips don't overlap, it wont give useful results.
|
|
* but at least ensure 'len' is never negative which causes bad bugs elsewhere. */
|
|
if (seq->enddisp < seq->startdisp) {
|
|
/* simple start/end swap */
|
|
seq->start = seq->enddisp;
|
|
seq->enddisp = seq->startdisp;
|
|
seq->startdisp = seq->start;
|
|
seq->flag |= SEQ_INVALID_EFFECT;
|
|
}
|
|
else {
|
|
seq->flag &= ~SEQ_INVALID_EFFECT;
|
|
}
|
|
|
|
seq->len = seq->enddisp - seq->startdisp;
|
|
}
|
|
else {
|
|
SEQ_time_update_sequence_bounds(scene, seq);
|
|
}
|
|
}
|
|
else {
|
|
if (seq->type == SEQ_TYPE_META) {
|
|
seqm = seq->seqbase.first;
|
|
if (seqm) {
|
|
min = MAXFRAME * 2;
|
|
max = -MAXFRAME * 2;
|
|
while (seqm) {
|
|
if (seqm->startdisp < min) {
|
|
min = seqm->startdisp;
|
|
}
|
|
if (seqm->enddisp > max) {
|
|
max = seqm->enddisp;
|
|
}
|
|
seqm = seqm->next;
|
|
}
|
|
seq->start = min + seq->anim_startofs;
|
|
seq->len = max - min;
|
|
seq->len -= seq->anim_startofs;
|
|
seq->len -= seq->anim_endofs;
|
|
}
|
|
seq_update_sound_bounds_recursive(scene, seq);
|
|
}
|
|
SEQ_time_update_sequence_bounds(scene, seq);
|
|
}
|
|
}
|
|
|
|
/** Comparison function suitable to be used with BLI_listbase_sort()... */
|
|
int SEQ_time_cmp_time_startdisp(const void *a, const void *b)
|
|
{
|
|
const Sequence *seq_a = a;
|
|
const Sequence *seq_b = b;
|
|
|
|
return (seq_a->startdisp > seq_b->startdisp);
|
|
}
|
|
|
|
int SEQ_time_find_next_prev_edit(Scene *scene,
|
|
int timeline_frame,
|
|
const short side,
|
|
const bool do_skip_mute,
|
|
const bool do_center,
|
|
const bool do_unselected)
|
|
{
|
|
Editing *ed = SEQ_editing_get(scene, false);
|
|
Sequence *seq;
|
|
|
|
int dist, best_dist, best_frame = timeline_frame;
|
|
int seq_frames[2], seq_frames_tot;
|
|
|
|
/* In case where both is passed,
|
|
* frame just finds the nearest end while frame_left the nearest start. */
|
|
|
|
best_dist = MAXFRAME * 2;
|
|
|
|
if (ed == NULL) {
|
|
return timeline_frame;
|
|
}
|
|
|
|
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
|
|
int i;
|
|
|
|
if (do_skip_mute && (seq->flag & SEQ_MUTE)) {
|
|
continue;
|
|
}
|
|
|
|
if (do_unselected && (seq->flag & SELECT)) {
|
|
continue;
|
|
}
|
|
|
|
if (do_center) {
|
|
seq_frames[0] = (seq->startdisp + seq->enddisp) / 2;
|
|
seq_frames_tot = 1;
|
|
}
|
|
else {
|
|
seq_frames[0] = seq->startdisp;
|
|
seq_frames[1] = seq->enddisp;
|
|
|
|
seq_frames_tot = 2;
|
|
}
|
|
|
|
for (i = 0; i < seq_frames_tot; i++) {
|
|
const int seq_frame = seq_frames[i];
|
|
|
|
dist = MAXFRAME * 2;
|
|
|
|
switch (side) {
|
|
case SEQ_SIDE_LEFT:
|
|
if (seq_frame < timeline_frame) {
|
|
dist = timeline_frame - seq_frame;
|
|
}
|
|
break;
|
|
case SEQ_SIDE_RIGHT:
|
|
if (seq_frame > timeline_frame) {
|
|
dist = seq_frame - timeline_frame;
|
|
}
|
|
break;
|
|
case SEQ_SIDE_BOTH:
|
|
dist = abs(seq_frame - timeline_frame);
|
|
break;
|
|
}
|
|
|
|
if (dist < best_dist) {
|
|
best_frame = seq_frame;
|
|
best_dist = dist;
|
|
}
|
|
}
|
|
}
|
|
|
|
return best_frame;
|
|
}
|
|
|
|
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 = seq->anims.first;
|
|
if (strip_anim->anim == NULL) {
|
|
return 0.0f;
|
|
}
|
|
short frs_sec;
|
|
float frs_sec_base;
|
|
if (IMB_anim_get_fps(strip_anim->anim, &frs_sec, &frs_sec_base, true)) {
|
|
return (float)frs_sec / frs_sec_base;
|
|
}
|
|
break;
|
|
}
|
|
case SEQ_TYPE_MOVIECLIP:
|
|
if (seq->clip != NULL) {
|
|
return BKE_movieclip_get_fps(seq->clip);
|
|
}
|
|
break;
|
|
case SEQ_TYPE_SCENE:
|
|
if (seq->scene != NULL) {
|
|
return (float)seq->scene->r.frs_sec / seq->scene->r.frs_sec_base;
|
|
}
|
|
break;
|
|
}
|
|
return 0.0f;
|
|
}
|
|
|
|
/**
|
|
* Define boundary rectangle of sequencer timeline and fill in rect data
|
|
*
|
|
* \param scene: Scene in which strips are located
|
|
* \param seqbase: ListBase in which strips are located
|
|
* \param rect: data structure describing rectangle, that will be filled in by this function
|
|
*/
|
|
void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *rect)
|
|
{
|
|
rect->xmin = scene->r.sfra;
|
|
rect->xmax = scene->r.efra + 1;
|
|
rect->ymin = 0.0f;
|
|
rect->ymax = 8.0f;
|
|
|
|
if (seqbase == NULL) {
|
|
return;
|
|
}
|
|
|
|
LISTBASE_FOREACH (Sequence *, seq, seqbase) {
|
|
if (rect->xmin > seq->startdisp - 1) {
|
|
rect->xmin = seq->startdisp - 1;
|
|
}
|
|
if (rect->xmax < seq->enddisp + 1) {
|
|
rect->xmax = seq->enddisp + 1;
|
|
}
|
|
if (rect->ymax < seq->machine + 2) {
|
|
rect->ymax = seq->machine + 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Find first gap between strips after initial_frame and describe it by filling data of r_gap_info
|
|
*
|
|
* \param scene: Scene in which strips are located
|
|
* \param seqbase: ListBase in which strips are located
|
|
* \param initial_frame: frame on timeline from where gaps are searched for
|
|
* \param r_gap_info: data structure describing gap, that will be filled in by this function
|
|
*/
|
|
void seq_time_gap_info_get(const Scene *scene,
|
|
ListBase *seqbase,
|
|
const int initial_frame,
|
|
GapInfo *r_gap_info)
|
|
{
|
|
rctf rectf;
|
|
/* Get first and last frame. */
|
|
SEQ_timeline_boundbox(scene, seqbase, &rectf);
|
|
const int sfra = (int)rectf.xmin;
|
|
const int efra = (int)rectf.xmax;
|
|
int timeline_frame = initial_frame;
|
|
r_gap_info->gap_exists = false;
|
|
|
|
if (SEQ_render_evaluate_frame(seqbase, initial_frame) == 0) {
|
|
/* Search backward for gap_start_frame. */
|
|
for (; timeline_frame >= sfra; timeline_frame--) {
|
|
if (SEQ_render_evaluate_frame(seqbase, timeline_frame) != 0) {
|
|
break;
|
|
}
|
|
}
|
|
r_gap_info->gap_start_frame = timeline_frame + 1;
|
|
timeline_frame = initial_frame;
|
|
}
|
|
else {
|
|
/* Search forward for gap_start_frame. */
|
|
for (; timeline_frame <= efra; timeline_frame++) {
|
|
if (SEQ_render_evaluate_frame(seqbase, timeline_frame) == 0) {
|
|
r_gap_info->gap_start_frame = timeline_frame;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* Search forward for gap_end_frame. */
|
|
for (; timeline_frame <= efra; timeline_frame++) {
|
|
if (SEQ_render_evaluate_frame(seqbase, timeline_frame) != 0) {
|
|
const int gap_end_frame = timeline_frame;
|
|
r_gap_info->gap_length = gap_end_frame - r_gap_info->gap_start_frame;
|
|
r_gap_info->gap_exists = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|