Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/blenkernel/BKE_image.h2
-rw-r--r--source/blender/blenkernel/BKE_sequencer.h6
-rw-r--r--source/blender/blenkernel/intern/image.c20
-rw-r--r--source/blender/blenkernel/intern/sequencer.c316
-rw-r--r--source/blender/blenloader/intern/readfile.c6
-rw-r--r--source/blender/editors/interface/interface_templates.c11
-rw-r--r--source/blender/editors/space_image/image_buttons.c4
-rw-r--r--source/blender/editors/space_sequencer/SConscript3
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c137
-rw-r--r--source/blender/editors/space_sequencer/sequencer_intern.h2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_ops.c1
-rw-r--r--source/blender/imbuf/CMakeLists.txt1
-rw-r--r--source/blender/imbuf/IMB_imbuf.h69
-rw-r--r--source/blender/imbuf/intern/IMB_anim.h24
-rw-r--r--source/blender/imbuf/intern/IMB_indexer.h135
-rw-r--r--source/blender/imbuf/intern/allocimbuf.c13
-rw-r--r--source/blender/imbuf/intern/anim_movie.c566
-rw-r--r--source/blender/imbuf/intern/filter.c2
-rw-r--r--source/blender/imbuf/intern/indexer.c1135
-rw-r--r--source/blender/imbuf/intern/indexer_dv.c391
-rw-r--r--source/blender/imbuf/intern/thumbs.c4
-rw-r--r--source/blender/imbuf/intern/util.c7
-rw-r--r--source/blender/makesdna/DNA_sequence_types.h35
-rw-r--r--source/blender/makesdna/DNA_space_types.h1
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c93
-rw-r--r--source/blender/makesrna/intern/rna_space.c1
-rw-r--r--source/blender/windowmanager/WM_api.h2
-rw-r--r--source/blender/windowmanager/intern/wm_jobs.c14
28 files changed, 2693 insertions, 308 deletions
diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h
index 10910493ec9..0c31083a266 100644
--- a/source/blender/blenkernel/BKE_image.h
+++ b/source/blender/blenkernel/BKE_image.h
@@ -60,7 +60,7 @@ int BKE_ftype_to_imtype(int ftype);
int BKE_imtype_to_ftype(int imtype);
int BKE_imtype_is_movie(int imtype);
-struct anim *openanim(char * name, int flags);
+struct anim *openanim(char * name, int flags, int streamindex);
void image_de_interlace(struct Image *ima, int odd);
diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h
index bedd58876bc..b20811724f4 100644
--- a/source/blender/blenkernel/BKE_sequencer.h
+++ b/source/blender/blenkernel/BKE_sequencer.h
@@ -177,6 +177,7 @@ int seq_recursive_apply(struct Sequence *seq, int (*apply_func)(struct Sequence
/* maintainance functions, mostly for RNA */
// extern
void seq_free_sequence(struct Scene *scene, struct Sequence *seq);
+void seq_free_sequence_recurse(struct Scene *scene, struct Sequence *seq);
void seq_free_strip(struct Strip *strip);
void seq_free_editing(struct Scene *scene);
void seq_free_clipboard(void);
@@ -199,6 +200,11 @@ void update_changed_seq_and_deps(struct Scene *scene, struct Sequence *changed_s
int input_have_to_preprocess(
SeqRenderData context, struct Sequence * seq, float cfra);
+void seq_proxy_rebuild(struct Main * bmain,
+ struct Scene *scene, struct Sequence * seq,
+ short *stop, short *do_update, float *progress);
+
+
/* **********************************************************************
seqcache.c
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index ab67d7e3f25..4ce5de78895 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -1371,15 +1371,15 @@ void BKE_makepicstring(char *string, const char *base, int frame, int imtype, co
}
/* used by sequencer too */
-struct anim *openanim(char *name, int flags)
+struct anim *openanim(char *name, int flags, int streamindex)
{
struct anim *anim;
struct ImBuf *ibuf;
- anim = IMB_open_anim(name, flags);
+ anim = IMB_open_anim(name, flags, streamindex);
if (anim == NULL) return NULL;
- ibuf = IMB_anim_absolute(anim, 0);
+ ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
if (ibuf == NULL) {
if(BLI_exists(name))
printf("not an anim: %s\n", name);
@@ -1773,20 +1773,26 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame)
else
BLI_path_abs(str, G.main->name);
- ima->anim = openanim(str, IB_rect);
+ /* FIXME: make several stream accessible in image editor, too*/
+ ima->anim = openanim(str, IB_rect, 0);
/* let's initialize this user */
if(ima->anim && iuser && iuser->frames==0)
- iuser->frames= IMB_anim_get_duration(ima->anim);
+ iuser->frames= IMB_anim_get_duration(ima->anim,
+ IMB_TC_RECORD_RUN);
}
if(ima->anim) {
- int dur = IMB_anim_get_duration(ima->anim);
+ int dur = IMB_anim_get_duration(ima->anim,
+ IMB_TC_RECORD_RUN);
int fra= frame-1;
if(fra<0) fra = 0;
if(fra>(dur-1)) fra= dur-1;
- ibuf = IMB_anim_absolute(ima->anim, fra);
+ ibuf = IMB_makeSingleUser(
+ IMB_anim_absolute(ima->anim, fra,
+ IMB_TC_RECORD_RUN,
+ IMB_PROXY_NONE));
if(ibuf) {
image_initialize_after_load(ima, ibuf);
diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c
index 3aebbea789f..60479da64a9 100644
--- a/source/blender/blenkernel/intern/sequencer.c
+++ b/source/blender/blenkernel/intern/sequencer.c
@@ -218,6 +218,18 @@ void seq_free_sequence(Scene *scene, Sequence *seq)
MEM_freeN(seq);
}
+void seq_free_sequence_recurse(Scene *scene, Sequence *seq)
+{
+ Sequence *iseq;
+
+ for(iseq= seq->seqbase.first; iseq; iseq= iseq->next) {
+ seq_free_sequence_recurse(scene, iseq);
+ }
+
+ seq_free_sequence(scene, seq);
+}
+
+
Editing *seq_give_editing(Scene *scene, int alloc)
{
if (scene->ed == NULL && alloc) {
@@ -683,13 +695,16 @@ void reload_sequence_new_file(Scene *scene, Sequence * seq, int lock_range)
}
case SEQ_MOVIE:
if(seq->anim) IMB_free_anim(seq->anim);
- seq->anim = openanim(str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0));
+ seq->anim = openanim(str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), seq->streamindex);
if (!seq->anim) {
return;
}
- seq->len = IMB_anim_get_duration(seq->anim);
+ seq->len = IMB_anim_get_duration(seq->anim,
+ seq->strip->proxy ?
+ seq->strip->proxy->tc :
+ IMB_TC_RECORD_RUN);
seq->anim_preseek = IMB_anim_get_preseek(seq->anim);
@@ -1117,7 +1132,7 @@ static int get_shown_sequences( ListBase * seqbasep, int cfra, int chanshown, Se
return cnt;
}
-
+
/* **********************************************************************
proxy management
@@ -1125,48 +1140,105 @@ static int get_shown_sequences( ListBase * seqbasep, int cfra, int chanshown, Se
#define PROXY_MAXFILE (2*FILE_MAXDIR+FILE_MAXFILE)
+static IMB_Proxy_Size seq_rendersize_to_proxysize(int size)
+{
+ if (size >= 100) {
+ return IMB_PROXY_NONE;
+ }
+ if (size >= 99) {
+ return IMB_PROXY_100;
+ }
+ if (size >= 75) {
+ return IMB_PROXY_75;
+ }
+ if (size >= 50) {
+ return IMB_PROXY_50;
+ }
+ return IMB_PROXY_25;
+}
+
+static void seq_open_anim_file(Sequence * seq)
+{
+ char name[FILE_MAXDIR+FILE_MAXFILE];
+ StripProxy * proxy;
+
+ if(seq->anim != NULL) {
+ return;
+ }
+
+ BLI_join_dirfile(name, sizeof(name),
+ seq->strip->dir, seq->strip->stripdata->name);
+ BLI_path_abs(name, G.main->name);
+
+ seq->anim = openanim(name, IB_rect |
+ ((seq->flag & SEQ_FILTERY) ?
+ IB_animdeinterlace : 0), seq->streamindex);
+
+ if (seq->anim == NULL) {
+ return;
+ }
+
+ proxy = seq->strip->proxy;
+
+ if (proxy == NULL) {
+ return;
+ }
+
+ if (seq->flag & SEQ_USE_PROXY_CUSTOM_DIR) {
+ IMB_anim_set_index_dir(seq->anim, seq->strip->proxy->dir);
+ }
+}
+
+
static int seq_proxy_get_fname(SeqRenderData context, Sequence * seq, int cfra, char * name)
{
int frameno;
char dir[FILE_MAXDIR];
+ int render_size = context.preview_render_size;
if (!seq->strip->proxy) {
return FALSE;
}
+ /* MOVIE tracks (only exception: custom files) are now handled
+ internally by ImBuf module for various reasons: proper time code
+ support, quicker index build, using one file instead
+ of a full directory of jpeg files, etc. Trying to support old
+ and new method at once could lead to funny effects, if people
+ have both, a directory full of jpeg files and proxy avis, so
+ sorry folks, please rebuild your proxies... */
+
if (seq->flag & (SEQ_USE_PROXY_CUSTOM_DIR|SEQ_USE_PROXY_CUSTOM_FILE)) {
strcpy(dir, seq->strip->proxy->dir);
+ } else if (seq->type == SEQ_IMAGE) {
+ snprintf(dir, PROXY_MAXFILE, "%s/BL_proxy", seq->strip->dir);
} else {
- if (ELEM(seq->type, SEQ_IMAGE, SEQ_MOVIE)) {
- snprintf(dir, FILE_MAXDIR, "%s/BL_proxy", seq->strip->dir);
- } else {
- return FALSE;
- }
+ return FALSE;
}
if (seq->flag & SEQ_USE_PROXY_CUSTOM_FILE) {
- BLI_join_dirfile(name, FILE_MAX, dir, seq->strip->proxy->file); /* XXX, not real length */
+ BLI_join_dirfile(name, PROXY_MAXFILE,
+ dir, seq->strip->proxy->file);
BLI_path_abs(name, G.main->name);
return TRUE;
}
+ /* dirty hack to distinguish 100% render size from PROXY_100 */
+ if (render_size == 99) {
+ render_size = 100;
+ }
+
/* generate a separate proxy directory for each preview size */
- switch(seq->type) {
- case SEQ_IMAGE:
+ if (seq->type == SEQ_IMAGE) {
snprintf(name, PROXY_MAXFILE, "%s/images/%d/%s_proxy", dir,
context.preview_render_size,
give_stripelem(seq, cfra)->name);
frameno = 1;
- break;
- case SEQ_MOVIE:
- frameno = (int) give_stripelem_index(seq, cfra) + seq->anim_startofs;
- snprintf(name, PROXY_MAXFILE, "%s/%s/%d/####", dir,
- seq->strip->stripdata->name, context.preview_render_size);
- break;
- default:
- frameno = (int) give_stripelem_index(seq, cfra) + seq->anim_startofs;
+ } else {
+ frameno = (int) give_stripelem_index(seq, cfra)
+ + seq->anim_startofs;
snprintf(name, PROXY_MAXFILE, "%s/proxy_misc/%d/####", dir,
context.preview_render_size);
}
@@ -1182,13 +1254,18 @@ static int seq_proxy_get_fname(SeqRenderData context, Sequence * seq, int cfra,
static struct ImBuf * seq_proxy_fetch(SeqRenderData context, Sequence * seq, int cfra)
{
char name[PROXY_MAXFILE];
+ IMB_Proxy_Size psize = seq_rendersize_to_proxysize(
+ context.preview_render_size);
+ int size_flags;
if (!(seq->flag & SEQ_USE_PROXY)) {
return NULL;
}
- /* rendering at 100% ? No real sense in proxy-ing, right? */
- if (context.preview_render_size == 100) {
+ size_flags = seq->strip->proxy->build_size_flags;
+
+ /* only use proxies, if they are enabled (even if present!) */
+ if (psize != IMB_PROXY_NONE && ((size_flags & psize) != psize)) {
return NULL;
}
@@ -1199,13 +1276,19 @@ static struct ImBuf * seq_proxy_fetch(SeqRenderData context, Sequence * seq, int
return NULL;
}
- seq->strip->proxy->anim = openanim(name, IB_rect);
+ seq->strip->proxy->anim = openanim(name, IB_rect, 0);
}
if (seq->strip->proxy->anim==NULL) {
return NULL;
}
- return IMB_anim_absolute(seq->strip->proxy->anim, frameno);
+ seq_open_anim_file(seq);
+
+ frameno = IMB_anim_index_get_frame_index(
+ seq->anim, seq->strip->proxy->tc, frameno);
+
+ return IMB_anim_absolute(seq->strip->proxy->anim, frameno,
+ IMB_TC_NONE, IMB_PROXY_NONE);
}
if (seq_proxy_get_fname(context, seq, cfra, name) == 0) {
@@ -1219,67 +1302,30 @@ static struct ImBuf * seq_proxy_fetch(SeqRenderData context, Sequence * seq, int
}
}
-#if 0
-static void do_build_seq_ibuf(Scene *scene, Sequence * seq, TStripElem *se, int cfra,
- int build_proxy_run, int preview_render_size);
-
-static void seq_proxy_build_frame(Scene *scene, Sequence * seq, int cfra, int preview_render_size, int seqrectx, int seqrecty)
+static void seq_proxy_build_frame(SeqRenderData context,
+ Sequence* seq, int cfra,
+ int proxy_render_size)
{
char name[PROXY_MAXFILE];
int quality;
- TStripElem * se;
- int ok;
int rectx, recty;
+ int ok;
struct ImBuf * ibuf;
- if (!(seq->flag & SEQ_USE_PROXY)) {
- return;
- }
-
- /* rendering at 100% ? No real sense in proxy-ing, right? */
- if (preview_render_size == 100) {
+ if (!seq_proxy_get_fname(context, seq, cfra, name)) {
return;
}
- /* that's why it is called custom... */
- if (seq->flag & SEQ_USE_PROXY_CUSTOM_FILE) {
- return;
- }
+ ibuf = seq_render_strip(context, seq, cfra);
- if (!seq_proxy_get_fname(scene, seq, cfra, name, preview_render_size)) {
- return;
- }
-
- se = give_tstripelem(seq, cfra);
- if (!se) {
- return;
- }
-
- if(se->ibuf) {
- IMB_freeImBuf(se->ibuf);
- se->ibuf = 0;
- }
-
- do_build_seq_ibuf(scene, seq, se, cfra, TRUE, preview_render_size,
- seqrectx, seqrecty);
-
- if (!se->ibuf) {
- return;
- }
-
- rectx= (preview_render_size*scene->r.xsch)/100;
- recty= (preview_render_size*scene->r.ysch)/100;
-
- ibuf = se->ibuf;
+ rectx = (proxy_render_size * context.scene->r.xsch) / 100;
+ recty = (proxy_render_size * context.scene->r.ysch) / 100;
if (ibuf->x != rectx || ibuf->y != recty) {
IMB_scalefastImBuf(ibuf, (short)rectx, (short)recty);
}
- /* quality is fixed, otherwise one has to generate separate
- directories for every quality...
-
- depth = 32 is intentionally left in, otherwise ALPHA channels
+ /* depth = 32 is intentionally left in, otherwise ALPHA channels
won't work... */
quality = seq->strip->proxy->quality;
ibuf->ftype= JPG | quality;
@@ -1292,69 +1338,80 @@ static void seq_proxy_build_frame(Scene *scene, Sequence * seq, int cfra, int pr
}
IMB_freeImBuf(ibuf);
- se->ibuf = 0;
}
-static void seq_proxy_rebuild(Scene *scene, Sequence * seq, int seqrectx,
- int seqrecty)
+void seq_proxy_rebuild(struct Main * bmain, Scene *scene, Sequence * seq,
+ short *stop, short *do_update, float *progress)
{
+ SeqRenderData context;
int cfra;
- float rsize = seq->strip->proxy->size;
+ int tc_flags;
+ int size_flags;
+ int quality;
+
+ if (!seq->strip || !seq->strip->proxy) {
+ return;
+ }
+
+ if (!(seq->flag & SEQ_USE_PROXY)) {
+ return;
+ }
- waitcursor(1);
+ tc_flags = seq->strip->proxy->build_tc_flags;
+ size_flags = seq->strip->proxy->build_size_flags;
+ quality = seq->strip->proxy->quality;
- G.afbreek = 0;
+ if (seq->type == SEQ_MOVIE) {
+ seq_open_anim_file(seq);
- /* flag management tries to account for strobe and
- other "non-linearities", that might come in the future...
- better way would be to "touch" the files, so that _really_
- no one is rebuild twice.
- */
+ if (seq->anim) {
+ IMB_anim_index_rebuild(
+ seq->anim, tc_flags, size_flags, quality,
+ stop, do_update, progress);
+ }
+ return;
+ }
- for (cfra = seq->startdisp; cfra < seq->enddisp; cfra++) {
- TStripElem * tse = give_tstripelem(seq, cfra);
+ if (!(seq->flag & SEQ_USE_PROXY)) {
+ return;
+ }
- tse->flag &= ~STRIPELEM_PREVIEW_DONE;
+ /* that's why it is called custom... */
+ if (seq->flag & SEQ_USE_PROXY_CUSTOM_FILE) {
+ return;
}
-
+ /* fail safe code */
- /* a _lot_ faster for movie files, if we read frames in
- sequential order */
- if (seq->flag & SEQ_REVERSE_FRAMES) {
- for (cfra = seq->enddisp-seq->endstill-1;
- cfra >= seq->startdisp + seq->startstill; cfra--) {
- TStripElem * tse = give_tstripelem(seq, cfra);
-
- if (!(tse->flag & STRIPELEM_PREVIEW_DONE)) {
-//XXX set_timecursor(cfra);
- seq_proxy_build_frame(scene, seq, cfra, rsize,
- seqrectx, seqrecty);
- tse->flag |= STRIPELEM_PREVIEW_DONE;
- }
- if (blender_test_break()) {
- break;
- }
+ context = seq_new_render_data(
+ bmain, scene,
+ (scene->r.size * (float) scene->r.xsch) / 100.0f + 0.5f,
+ (scene->r.size * (float) scene->r.ysch) / 100.0f + 0.5f,
+ 100);
+
+ for (cfra = seq->startdisp + seq->startstill;
+ cfra < seq->enddisp - seq->endstill; cfra++) {
+ if (size_flags & IMB_PROXY_25) {
+ seq_proxy_build_frame(context, seq, cfra, 25);
}
- } else {
- for (cfra = seq->startdisp + seq->startstill;
- cfra < seq->enddisp - seq->endstill; cfra++) {
- TStripElem * tse = give_tstripelem(seq, cfra);
-
- if (!(tse->flag & STRIPELEM_PREVIEW_DONE)) {
-//XXX set_timecursor(cfra);
- seq_proxy_build_frame(scene, seq, cfra, rsize,
- seqrectx, seqrecty);
- tse->flag |= STRIPELEM_PREVIEW_DONE;
- }
- if (blender_test_break()) {
- break;
- }
+ if (size_flags & IMB_PROXY_50) {
+ seq_proxy_build_frame(context, seq, cfra, 50);
+ }
+ if (size_flags & IMB_PROXY_75) {
+ seq_proxy_build_frame(context, seq, cfra, 75);
}
+ if (size_flags & IMB_PROXY_100) {
+ seq_proxy_build_frame(context, seq, cfra, 100);
+ }
+
+ *progress= (float)cfra/(seq->enddisp - seq->endstill
+ - seq->startdisp + seq->startstill);
+ *do_update= 1;
+
+ if(*stop || G.afbreek)
+ break;
}
- waitcursor(0);
}
-#endif
/* **********************************************************************
@@ -1571,6 +1628,8 @@ static ImBuf * input_preprocess(
{
float mul;
+ ibuf = IMB_makeSingleUser(ibuf);
+
if((seq->flag & SEQ_FILTERY) && seq->type != SEQ_MOVIE) {
IMB_filtery(ibuf);
}
@@ -2096,17 +2155,20 @@ static ImBuf * seq_render_strip(SeqRenderData context, Sequence * seq, float cfr
}
case SEQ_MOVIE:
{
- if(seq->anim==NULL) {
- BLI_join_dirfile(name, sizeof(name), seq->strip->dir, seq->strip->stripdata->name);
- BLI_path_abs(name, G.main->name);
-
- seq->anim = openanim(name, IB_rect |
- ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0));
- }
+ seq_open_anim_file(seq);
if(seq->anim) {
- IMB_anim_set_preseek(seq->anim, seq->anim_preseek);
- ibuf = IMB_anim_absolute(seq->anim, nr + seq->anim_startofs);
+ IMB_anim_set_preseek(seq->anim,
+ seq->anim_preseek);
+
+ ibuf = IMB_anim_absolute(
+ seq->anim, nr + seq->anim_startofs,
+ seq->strip->proxy ?
+ seq->strip->proxy->tc
+ : IMB_TC_RECORD_RUN,
+ seq_rendersize_to_proxysize(
+ context.preview_render_size));
+
/* we don't need both (speed reasons)! */
if (ibuf && ibuf->rect_float && ibuf->rect)
imb_freerectImBuf(ibuf);
@@ -3584,7 +3646,7 @@ Sequence *sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoadInfo
BLI_strncpy(path, seq_load->path, sizeof(path));
BLI_path_abs(path, G.main->name);
- an = openanim(path, IB_rect);
+ an = openanim(path, IB_rect, 0);
if(an==NULL)
return NULL;
@@ -3600,7 +3662,7 @@ Sequence *sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoadInfo
/* basic defaults */
seq->strip= strip= MEM_callocN(sizeof(Strip), "strip");
- strip->len = seq->len = IMB_anim_get_duration( an );
+ strip->len = seq->len = IMB_anim_get_duration( an, IMB_TC_RECORD_RUN );
strip->us= 1;
/* we only need 1 element for MOVIE strips */
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index f3b478b90f9..df7e1f80cbd 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -9951,12 +9951,6 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
if(ed) {
SEQP_BEGIN(ed, seq) {
if (seq->strip && seq->strip->proxy){
- if (sce->r.size != 100.0) {
- seq->strip->proxy->size
- = sce->r.size;
- } else {
- seq->strip->proxy->size = 25;
- }
seq->strip->proxy->quality =90;
}
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 2faac24fd78..67123476f06 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -2369,6 +2369,7 @@ void uiTemplateOperatorSearch(uiLayout *layout)
#define B_STOPCAST 2
#define B_STOPANIM 3
#define B_STOPCOMPO 4
+#define B_STOPSEQ 5
static void do_running_jobs(bContext *C, void *UNUSED(arg), int event)
{
@@ -2385,6 +2386,9 @@ static void do_running_jobs(bContext *C, void *UNUSED(arg), int event)
case B_STOPCOMPO:
WM_jobs_stop(CTX_wm_manager(C), CTX_wm_area(C), NULL);
break;
+ case B_STOPSEQ:
+ WM_jobs_stop(CTX_wm_manager(C), CTX_wm_area(C), NULL);
+ break;
}
}
@@ -2406,8 +2410,11 @@ void uiTemplateRunningJobs(uiLayout *layout, bContext *C)
if(WM_jobs_test(wm, sa))
owner = sa;
handle_event= B_STOPCOMPO;
- }
- else {
+ } else if (sa->spacetype==SPACE_SEQ) {
+ if(WM_jobs_test(wm, sa))
+ owner = sa;
+ handle_event = B_STOPSEQ;
+ } else {
Scene *scene;
/* another scene can be rendering too, for example via compositor */
for(scene= CTX_data_main(C)->scene.first; scene; scene= scene->id.next)
diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c
index 66e844e67a8..4011f038be8 100644
--- a/source/blender/editors/space_image/image_buttons.c
+++ b/source/blender/editors/space_image/image_buttons.c
@@ -113,7 +113,7 @@ static void image_info(Scene *scene, ImageUser *iuser, Image *ima, ImBuf *ibuf,
if(ima->source==IMA_SRC_MOVIE) {
ofs+= sprintf(str, "Movie");
if(ima->anim)
- ofs+= sprintf(str+ofs, "%d frs", IMB_anim_get_duration(ima->anim));
+ ofs+= sprintf(str+ofs, "%d frs", IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN));
}
else
ofs+= sprintf(str, "Image");
@@ -428,7 +428,7 @@ static void set_frames_cb(bContext *C, void *ima_v, void *iuser_v)
ImageUser *iuser= iuser_v;
if(ima->anim) {
- iuser->frames = IMB_anim_get_duration(ima->anim);
+ iuser->frames = IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN);
BKE_image_user_calc_frame(iuser, scene->r.cfra, 0);
}
}
diff --git a/source/blender/editors/space_sequencer/SConscript b/source/blender/editors/space_sequencer/SConscript
index 65bbf900556..3430c10b766 100644
--- a/source/blender/editors/space_sequencer/SConscript
+++ b/source/blender/editors/space_sequencer/SConscript
@@ -8,4 +8,7 @@ incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include'
incs += ' ../../makesrna ../../blenloader'
incs += ' #/intern/audaspace/intern'
+if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'):
+ incs += ' ' + env['BF_PTHREADS_INC']
+
env.BlenderLib ( 'bf_editors_space_sequencer', sources, Split(incs), [], libtype=['core'], priority=[100] )
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index e876da41bd9..18ff33fd8a9 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -47,6 +47,7 @@
#include "BLI_math.h"
#include "BLI_storage_types.h"
#include "BLI_utildefines.h"
+#include "BLI_threads.h"
#include "DNA_scene_types.h"
#include "DNA_userdef_types.h"
@@ -125,6 +126,111 @@ typedef struct TransSeq {
int len;
} TransSeq;
+/* ********************************************************************** */
+
+/* ***************** proxy job manager ********************** */
+
+typedef struct ProxyBuildJob {
+ Scene *scene;
+ struct Main * main;
+ ListBase queue;
+ ThreadMutex queue_lock;
+} ProxyJob;
+
+static void proxy_freejob(void *pjv)
+{
+ ProxyJob *pj= pjv;
+ Sequence * seq;
+
+ for (seq = pj->queue.first; seq; seq = seq->next) {
+ BLI_remlink(&pj->queue, seq);
+ seq_free_sequence_recurse(pj->scene, seq);
+ }
+
+ BLI_mutex_end(&pj->queue_lock);
+
+ MEM_freeN(pj);
+}
+
+/* only this runs inside thread */
+static void proxy_startjob(void *pjv, short *stop, short *do_update, float *progress)
+{
+ ProxyJob *pj = pjv;
+
+ while (!*stop) {
+ Sequence * seq;
+
+ BLI_mutex_lock(&pj->queue_lock);
+
+ if (!pj->queue.first) {
+ BLI_mutex_unlock(&pj->queue_lock);
+ break;
+ }
+
+ seq = pj->queue.first;
+
+ BLI_remlink(&pj->queue, seq);
+ BLI_mutex_unlock(&pj->queue_lock);
+
+ seq_proxy_rebuild(pj->main, pj->scene, seq,
+ stop, do_update, progress);
+ seq_free_sequence_recurse(pj->scene, seq);
+ }
+
+ if (*stop) {
+ fprintf(stderr,
+ "Canceling proxy rebuild on users request...\n");
+ }
+}
+
+static void proxy_endjob(void *UNUSED(customdata))
+{
+
+}
+
+void seq_proxy_build_job(const bContext *C, Sequence * seq)
+{
+ wmJob * steve;
+ ProxyJob *pj;
+ Scene *scene= CTX_data_scene(C);
+ ScrArea * sa= CTX_wm_area(C);
+
+ seq = seq_dupli_recursive(scene, scene, seq, 0);
+
+ steve = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C),
+ sa, "Building Proxies", WM_JOB_PROGRESS);
+
+ pj = WM_jobs_get_customdata(steve);
+
+ if (!pj) {
+ pj = MEM_callocN(sizeof(ProxyJob), "proxy rebuild job");
+
+ pj->scene= scene;
+ pj->main = CTX_data_main(C);
+
+ BLI_mutex_init(&pj->queue_lock);
+
+ WM_jobs_customdata(steve, pj, proxy_freejob);
+ WM_jobs_timer(steve, 0.1, NC_SCENE|ND_SEQUENCER,
+ NC_SCENE|ND_SEQUENCER);
+ WM_jobs_callbacks(steve, proxy_startjob, NULL, NULL,
+ proxy_endjob);
+ }
+
+ BLI_mutex_lock(&pj->queue_lock);
+ BLI_addtail(&pj->queue, seq);
+ BLI_mutex_unlock(&pj->queue_lock);
+
+ if (!WM_jobs_is_running(steve)) {
+ G.afbreek = 0;
+ WM_jobs_start(CTX_wm_manager(C), steve);
+ }
+
+ ED_area_tag_redraw(CTX_wm_area(C));
+}
+
+/* ********************************************************************** */
+
void seq_rectf(Sequence *seq, rctf *rectf)
{
if(seq->startstill) rectf->xmin= seq->start;
@@ -2690,6 +2796,37 @@ void SEQUENCER_OT_view_ghost_border(wmOperatorType *ot)
WM_operator_properties_gesture_border(ot, FALSE);
}
+/* rebuild_proxy operator */
+static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene = CTX_data_scene(C);
+ Editing *ed = seq_give_editing(scene, FALSE);
+ Sequence * seq;
+
+ SEQP_BEGIN(ed, seq) {
+ if ((seq->flag & SELECT)) {
+ seq_proxy_build_job(C, seq);
+ }
+ }
+ SEQ_END
+
+ return OPERATOR_FINISHED;
+}
+
+void SEQUENCER_OT_rebuild_proxy(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Rebuild Proxy and Timecode Indices";
+ ot->idname= "SEQUENCER_OT_rebuild_proxy";
+ ot->description="Rebuild all selected proxies and timecode indeces using the job system";
+
+ /* api callbacks */
+ ot->exec= sequencer_rebuild_proxy_exec;
+ ot->poll= ED_operator_sequencer_active;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER;
+}
/* change ops */
diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h
index 7ab76f9b6d7..89e9a22c9a1 100644
--- a/source/blender/editors/space_sequencer/sequencer_intern.h
+++ b/source/blender/editors/space_sequencer/sequencer_intern.h
@@ -118,6 +118,8 @@ void SEQUENCER_OT_change_path(struct wmOperatorType *ot);
void SEQUENCER_OT_copy(struct wmOperatorType *ot);
void SEQUENCER_OT_paste(struct wmOperatorType *ot);
+void SEQUENCER_OT_rebuild_proxy(struct wmOperatorType *ot);
+
/* preview specific operators */
void SEQUENCER_OT_view_all_preview(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c
index df33ce73b9c..5c13b57cca8 100644
--- a/source/blender/editors/space_sequencer/sequencer_ops.c
+++ b/source/blender/editors/space_sequencer/sequencer_ops.c
@@ -87,6 +87,7 @@ void sequencer_operatortypes(void)
WM_operatortype_append(SEQUENCER_OT_view_zoom_ratio);
WM_operatortype_append(SEQUENCER_OT_view_ghost_border);
+ WM_operatortype_append(SEQUENCER_OT_rebuild_proxy);
WM_operatortype_append(SEQUENCER_OT_change_effect_input);
WM_operatortype_append(SEQUENCER_OT_change_effect_type);
WM_operatortype_append(SEQUENCER_OT_change_path);
diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt
index 18b5eff5c73..ff13be20d4e 100644
--- a/source/blender/imbuf/CMakeLists.txt
+++ b/source/blender/imbuf/CMakeLists.txt
@@ -73,6 +73,7 @@ set(SRC
intern/tiff.c
intern/util.c
intern/writeimage.c
+ intern/indexer.c
IMB_imbuf.h
IMB_imbuf_types.h
diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h
index 36123592c54..1fbe8e01fd4 100644
--- a/source/blender/imbuf/IMB_imbuf.h
+++ b/source/blender/imbuf/IMB_imbuf.h
@@ -133,6 +133,7 @@ struct ImBuf *IMB_allocImBuf(unsigned int x, unsigned int y,
*/
void IMB_refImBuf(struct ImBuf *ibuf);
+struct ImBuf * IMB_makeSingleUser(struct ImBuf *ibuf);
/**
*
@@ -193,17 +194,70 @@ void IMB_rectblend(struct ImBuf *dbuf, struct ImBuf *sbuf, int destx,
int desty, int srcx, int srcy, int width, int height, IMB_BlendMode mode);
/**
+ *
+ * @attention Defined in indexer.c
+ */
+
+typedef enum IMB_Timecode_Type {
+ IMB_TC_NONE = 0, /* don't use timecode files at all */
+ IMB_TC_RECORD_RUN = 1, /* use images in the order as they are recorded
+ (currently, this is the only one implemented
+ and is a sane default)
+ */
+ IMB_TC_FREE_RUN = 2, /* use global timestamp written by recording
+ device (prosumer camcorders e.g. can do
+ that) */
+ IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN = 4,
+ /* interpolate a global timestamp using the
+ record date and time written by recording
+ device (*every* consumer camcorder can do
+ that :) )*/
+ IMB_TC_MAX_SLOT = 3
+} IMB_Timecode_Type;
+
+typedef enum IMB_Proxy_Size {
+ IMB_PROXY_NONE = 0,
+ IMB_PROXY_25 = 1,
+ IMB_PROXY_50 = 2,
+ IMB_PROXY_75 = 4,
+ IMB_PROXY_100 = 8,
+ IMB_PROXY_MAX_SLOT = 4
+} IMB_Proxy_Size;
+
+/* defaults to BL_proxy within the directory of the animation */
+void IMB_anim_set_index_dir(struct anim * anim, const char * dir);
+
+int IMB_anim_index_get_frame_index(struct anim * anim, IMB_Timecode_Type tc,
+ int position);
+
+/* will rebuild all used indices and proxies at once */
+void IMB_anim_index_rebuild(struct anim * anim,
+ IMB_Timecode_Type build_tcs,
+ IMB_Proxy_Size build_preview_sizes,
+ int build_quality,
+ short *stop, short *do_update, float *progress);
+
+/**
* Return the length (in frames) of the given @a anim.
*/
-int IMB_anim_get_duration(struct anim *anim);
+int IMB_anim_get_duration(struct anim *anim, IMB_Timecode_Type tc);
+
+
+/**
+ * Return the fps contained in movie files (function rval is FALSE,
+ * and frs_sec and frs_sec_base untouched if none available!)
+ */
+int IMB_anim_get_fps(struct anim * anim,
+ short * frs_sec, float * frs_sec_base);
/**
*
* @attention Defined in anim.c
*/
-struct anim *IMB_open_anim(const char *name, int ib_flags);
+struct anim *IMB_open_anim(const char *name, int ib_flags, int streamindex);
void IMB_close_anim(struct anim *anim);
+
/**
*
* @attention Defined in anim.c
@@ -218,7 +272,10 @@ int IMB_anim_get_preseek(struct anim *anim);
* @attention Defined in anim.c
*/
-struct ImBuf *IMB_anim_absolute(struct anim *anim, int position);
+struct ImBuf *IMB_anim_absolute(
+ struct anim *anim, int position,
+ IMB_Timecode_Type tc /* = 1 = IMB_TC_RECORD_RUN */,
+ IMB_Proxy_Size preview_size /* = 0 = IMB_PROXY_NONE */);
/**
*
@@ -231,12 +288,6 @@ struct ImBuf *IMB_anim_previewframe(struct anim *anim);
*
* @attention Defined in anim.c
*/
-void IMB_free_anim_ibuf(struct anim *anim);
-
-/**
- *
- * @attention Defined in anim.c
- */
void IMB_free_anim(struct anim *anim);
/**
diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h
index fba0772dd93..8436846bf2e 100644
--- a/source/blender/imbuf/intern/IMB_anim.h
+++ b/source/blender/imbuf/intern/IMB_anim.h
@@ -127,19 +127,22 @@
#define MAXNUMSTREAMS 50
struct _AviMovie;
+struct anim_index;
struct anim {
int ib_flags;
int curtype;
int curposition; /* index 0 = 1e, 1 = 2e, enz. */
int duration;
+ short frs_sec;
+ float frs_sec_base;
int x, y;
/* voor op nummer */
char name[256];
/* voor sequence */
char first[256];
-
+
/* movie */
void *movie;
void *track;
@@ -148,9 +151,7 @@ struct anim {
size_t framesize;
int interlacing;
int preseek;
-
- /* data */
- struct ImBuf * ibuf1, * ibuf2;
+ int streamindex;
/* avi */
struct _AviMovie *avi;
@@ -179,11 +180,26 @@ struct anim {
AVFrame *pFrameDeinterlaced;
struct SwsContext *img_convert_ctx;
int videoStream;
+
+ struct ImBuf * last_frame;
+ int64_t last_pts;
+ int64_t next_pts;
+ int64_t next_undecoded_pts;
+ AVPacket next_packet;
#endif
#ifdef WITH_REDCODE
struct redcode_handle * redcodeCtx;
#endif
+
+ char index_dir[256];
+
+ int proxies_tried;
+ int indices_tried;
+
+ struct anim * proxy_anim[IMB_PROXY_MAX_SLOT];
+ struct anim_index * curr_idx[IMB_TC_MAX_SLOT];
+
};
#endif
diff --git a/source/blender/imbuf/intern/IMB_indexer.h b/source/blender/imbuf/intern/IMB_indexer.h
new file mode 100644
index 00000000000..ae3b48f76c7
--- /dev/null
+++ b/source/blender/imbuf/intern/IMB_indexer.h
@@ -0,0 +1,135 @@
+/**
+ * IMB_indexer.h
+ *
+ * $Id: IMB_indexer.h
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ *
+ * Contributor(s): Peter Schlaile
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+
+#ifndef IMB_INDEXER_H
+#define IMB_INDEXER_H
+
+#ifdef WIN32
+#include <io.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "BKE_utildefines.h"
+#include "IMB_anim.h"
+
+/*
+ seperate animation index files to solve the following problems:
+
+ a) different timecodes within one file (like DTS/PTS, Timecode-Track,
+ "implicit" timecodes within DV-files and HDV-files etc.)
+ b) seeking difficulties within ffmpeg for files with timestamp holes
+ c) broken files that miss several frames / have varying framerates
+ d) use proxies accordingly
+
+ ... we need index files, that provide us with
+
+ the binary(!) position, where we have to seek into the file *and*
+ the continuous frame number (ignoring the holes) starting from the
+ beginning of the file, so that we know, which proxy frame to serve.
+
+ This index has to be only built once for a file and is written into
+ the BL_proxy directory structure for later reuse in different blender files.
+
+*/
+
+typedef struct anim_index_entry {
+ int frameno;
+ unsigned long long seek_pos;
+ unsigned long long seek_pos_dts;
+ unsigned long long pts;
+} anim_index_entry;
+
+struct anim_index {
+ char name[256];
+
+ int num_entries;
+ struct anim_index_entry * entries;
+};
+
+struct anim_index_builder;
+
+typedef struct anim_index_builder {
+ FILE * fp;
+ char name[FILE_MAXDIR + FILE_MAXFILE];
+ char temp_name[FILE_MAXDIR + FILE_MAXFILE];
+
+ void * private_data;
+
+ void (*delete_priv_data)(struct anim_index_builder * idx);
+ void (*proc_frame)(struct anim_index_builder * idx,
+ unsigned char * buffer,
+ int data_size,
+ struct anim_index_entry * entry);
+} anim_index_builder;
+
+anim_index_builder * IMB_index_builder_create(const char * name);
+void IMB_index_builder_add_entry(anim_index_builder * fp,
+ int frameno, unsigned long long seek_pos,
+ unsigned long long seek_pos_dts,
+ unsigned long long pts);
+
+void IMB_index_builder_proc_frame(anim_index_builder * fp,
+ unsigned char * buffer,
+ int data_size,
+ int frameno, unsigned long long seek_pos,
+ unsigned long long seek_pos_dts,
+ unsigned long long pts);
+
+void IMB_index_builder_finish(anim_index_builder * fp, int rollback);
+
+struct anim_index * IMB_indexer_open(const char * name);
+unsigned long long IMB_indexer_get_seek_pos(
+ struct anim_index * idx, int frameno_index);
+unsigned long long IMB_indexer_get_seek_pos_dts(
+ struct anim_index * idx, int frameno_index);
+
+int IMB_indexer_get_frame_index(struct anim_index * idx, int frameno);
+unsigned long long IMB_indexer_get_pts(struct anim_index * idx,
+ int frame_index);
+int IMB_indexer_get_duration(struct anim_index * idx);
+
+int IMB_indexer_can_scan(struct anim_index * idx,
+ int old_frame_index, int new_frame_index);
+
+void IMB_indexer_close(struct anim_index * idx);
+
+void IMB_free_indices(struct anim * anim);
+
+int IMB_anim_index_get_frame_index(
+ struct anim * anim, IMB_Timecode_Type tc, int position);
+
+struct anim * IMB_anim_open_proxy(
+ struct anim * anim, IMB_Proxy_Size preview_size);
+struct anim_index * IMB_anim_open_index(
+ struct anim * anim, IMB_Timecode_Type tc);
+
+int IMB_proxy_size_to_array_index(IMB_Proxy_Size pr_size);
+int IMB_timecode_to_array_index(IMB_Timecode_Type tc);
+
+#endif
diff --git a/source/blender/imbuf/intern/allocimbuf.c b/source/blender/imbuf/intern/allocimbuf.c
index 59772771f3b..6ce6c9409d1 100644
--- a/source/blender/imbuf/intern/allocimbuf.c
+++ b/source/blender/imbuf/intern/allocimbuf.c
@@ -177,6 +177,19 @@ void IMB_refImBuf(ImBuf *ibuf)
ibuf->refcounter++;
}
+ImBuf * IMB_makeSingleUser(ImBuf *ibuf)
+{
+ ImBuf * rval;
+
+ if (!ibuf || ibuf->refcounter == 0) { return ibuf; }
+
+ rval = IMB_dupImBuf(ibuf);
+
+ IMB_freeImBuf(ibuf);
+
+ return rval;
+}
+
short addzbufImBuf(ImBuf *ibuf)
{
int size;
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c
index 8b0104fcdca..7b172008bee 100644
--- a/source/blender/imbuf/intern/anim_movie.c
+++ b/source/blender/imbuf/intern/anim_movie.c
@@ -57,6 +57,7 @@
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
+#include <math.h>
#ifndef _WIN32
#include <dirent.h>
#else
@@ -66,6 +67,7 @@
#include "BLI_blenlib.h" /* BLI_remlink BLI_filesize BLI_addtail
BLI_countlist BLI_stringdec */
#include "BLI_utildefines.h"
+#include "BLI_math_base.h"
#include "MEM_guardedalloc.h"
@@ -90,6 +92,7 @@
#include "IMB_allocimbuf.h"
#include "IMB_anim.h"
+#include "IMB_indexer.h"
#ifdef WITH_FFMPEG
#include <libavformat/avformat.h>
@@ -304,15 +307,6 @@ static void free_anim_avi (struct anim *anim) {
anim->duration = 0;
}
-void IMB_free_anim_ibuf(struct anim * anim) {
- if (anim == NULL) return;
-
- if (anim->ibuf1) IMB_freeImBuf(anim->ibuf1);
- if (anim->ibuf2) IMB_freeImBuf(anim->ibuf2);
-
- anim->ibuf1 = anim->ibuf2 = NULL;
-}
-
#ifdef WITH_FFMPEG
static void free_anim_ffmpeg(struct anim * anim);
#endif
@@ -326,7 +320,6 @@ void IMB_free_anim(struct anim * anim) {
return;
}
- IMB_free_anim_ibuf(anim);
free_anim_movie(anim);
free_anim_avi(anim);
@@ -339,6 +332,7 @@ void IMB_free_anim(struct anim * anim) {
#ifdef WITH_REDCODE
free_anim_redcode(anim);
#endif
+ IMB_free_indices(anim);
MEM_freeN(anim);
}
@@ -350,13 +344,14 @@ void IMB_close_anim(struct anim * anim) {
}
-struct anim * IMB_open_anim( const char * name, int ib_flags) {
+struct anim * IMB_open_anim( const char * name, int ib_flags, int streamindex) {
struct anim * anim;
anim = (struct anim*)MEM_callocN(sizeof(struct anim), "anim struct");
if (anim != NULL) {
BLI_strncpy(anim->name, name, sizeof(anim->name));
anim->ib_flags = ib_flags;
+ anim->streamindex = streamindex;
}
return(anim);
}
@@ -368,10 +363,13 @@ static int startavi (struct anim *anim) {
#if defined(_WIN32) && !defined(FREE_WINDOWS)
HRESULT hr;
int i, firstvideo = -1;
+ int streamcount;
BYTE abFormat[1024];
LONG l;
LPBITMAPINFOHEADER lpbi;
AVISTREAMINFO avis;
+
+ streamcount = anim->streamindex;
#endif
anim->avi = MEM_callocN (sizeof(AviMovie),"animavi");
@@ -396,6 +394,10 @@ static int startavi (struct anim *anim) {
AVIStreamInfo(anim->pavi[i], &avis, sizeof(avis));
if ((avis.fccType == streamtypeVIDEO) && (firstvideo == -1)) {
+ if (streamcount > 0) {
+ streamcount--;
+ continue;
+ }
anim->pgf = AVIStreamGetFrameOpen(anim->pavi[i], NULL);
if (anim->pgf) {
firstvideo = i;
@@ -496,14 +498,14 @@ static ImBuf * avi_fetchibuf (struct anim *anim, int position) {
for (y=0; y < anim->y; y++) {
memcpy (&(ibuf->rect)[((anim->y-y)-1)*anim->x], &tmp[y*anim->x],
- anim->x * 4);
+ anim->x * 4);
}
MEM_freeN (tmp);
}
-
+
ibuf->profile = IB_PROFILE_SRGB;
-
+
return ibuf;
}
@@ -517,6 +519,9 @@ static int startffmpeg(struct anim * anim) {
AVCodec *pCodec;
AVFormatContext *pFormatCtx;
AVCodecContext *pCodecCtx;
+ int frs_num;
+ double frs_den;
+ int streamcount;
#ifdef FFMPEG_SWSCALE_COLOR_SPACE_SUPPORT
/* The following for color space determination */
@@ -527,6 +532,8 @@ static int startffmpeg(struct anim * anim) {
if (anim == 0) return(-1);
+ streamcount = anim->streamindex;
+
do_init_ffmpeg();
if(av_open_input_file(&pFormatCtx, anim->name, NULL, 0, NULL)!=0) {
@@ -541,12 +548,17 @@ static int startffmpeg(struct anim * anim) {
av_dump_format(pFormatCtx, 0, anim->name, 0);
- /* Find the first video stream */
- videoStream=-1;
- for(i=0; i<pFormatCtx->nb_streams; i++)
- if(pFormatCtx->streams[i]->codec->codec_type
+ /* Find the video stream */
+ videoStream = -1;
+
+ for(i = 0; i < pFormatCtx->nb_streams; i++)
+ if (pFormatCtx->streams[i]->codec->codec_type
== AVMEDIA_TYPE_VIDEO) {
- videoStream=i;
+ if (streamcount > 0) {
+ streamcount--;
+ continue;
+ }
+ videoStream = i;
break;
}
@@ -557,16 +569,16 @@ static int startffmpeg(struct anim * anim) {
pCodecCtx = pFormatCtx->streams[videoStream]->codec;
- /* Find the decoder for the video stream */
- pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
- if(pCodec==NULL) {
+ /* Find the decoder for the video stream */
+ pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
+ if(pCodec == NULL) {
av_close_input_file(pFormatCtx);
return -1;
}
pCodecCtx->workaround_bugs = 1;
- if(avcodec_open(pCodecCtx, pCodec)<0) {
+ if(avcodec_open(pCodecCtx, pCodec) < 0) {
av_close_input_file(pFormatCtx);
return -1;
}
@@ -575,6 +587,19 @@ static int startffmpeg(struct anim * anim) {
* av_q2d(pFormatCtx->streams[videoStream]->r_frame_rate)
/ AV_TIME_BASE);
+ frs_num = pFormatCtx->streams[videoStream]->r_frame_rate.num;
+ frs_den = pFormatCtx->streams[videoStream]->r_frame_rate.den;
+
+ frs_den *= AV_TIME_BASE;
+
+ while (frs_num % 10 == 0 && frs_den >= 2.0 && frs_num > 10) {
+ frs_num /= 10;
+ frs_den /= 10;
+ }
+
+ anim->frs_sec = frs_num;
+ anim->frs_sec_base = frs_den;
+
anim->params = 0;
anim->x = pCodecCtx->width;
@@ -584,6 +609,11 @@ static int startffmpeg(struct anim * anim) {
anim->framesize = anim->x * anim->y * 4;
anim->curposition = -1;
+ anim->last_frame = 0;
+ anim->last_pts = -1;
+ anim->next_pts = -1;
+ anim->next_undecoded_pts = -1;
+ anim->next_packet.stream_index = -1;
anim->pFormatCtx = pFormatCtx;
anim->pCodecCtx = pCodecCtx;
@@ -666,10 +696,19 @@ static int startffmpeg(struct anim * anim) {
return (0);
}
-static void ffmpeg_postprocess(struct anim * anim, ImBuf * ibuf,
- int * filter_y)
+/* postprocess the image in anim->pFrame and do color conversion
+ and deinterlacing stuff.
+
+ Output is anim->last_frame
+*/
+
+static void ffmpeg_postprocess(struct anim * anim)
{
AVFrame * input = anim->pFrame;
+ ImBuf * ibuf = anim->last_frame;
+ int filter_y = 0;
+
+ ibuf->profile = IB_PROFILE_SRGB;
/* This means the data wasnt read properly,
this check stops crashing */
@@ -690,12 +729,16 @@ static void ffmpeg_postprocess(struct anim * anim, ImBuf * ibuf,
anim->pCodecCtx->width,
anim->pCodecCtx->height)
< 0) {
- *filter_y = 1;
+ filter_y = TRUE;
} else {
input = anim->pFrameDeinterlaced;
}
}
+ avpicture_fill((AVPicture*) anim->pFrameRGB,
+ (unsigned char*) ibuf->rect,
+ PIX_FMT_RGBA, anim->x, anim->y);
+
if (ENDIAN_ORDER == B_ENDIAN) {
int * dstStride = anim->pFrameRGB->linesize;
uint8_t** dst = anim->pFrameRGB->data;
@@ -774,150 +817,359 @@ static void ffmpeg_postprocess(struct anim * anim, ImBuf * ibuf,
}
}
}
+
+ if (filter_y) {
+ IMB_filtery(ibuf);
+ }
}
-static ImBuf * ffmpeg_fetchibuf(struct anim * anim, int position) {
- ImBuf * ibuf;
- int frameFinished;
- AVPacket packet;
+/* decode one video frame and load the next packet into anim->packet,
+ so that we can obtain next_pts and next undecoded pts */
+
+static int ffmpeg_decode_video_frame(struct anim * anim)
+{
+ int frameFinished = 0;
+ int rval = 0;
+
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE VIDEO FRAME\n");
+
+ anim->next_undecoded_pts = -1;
+
+ if (anim->next_packet.stream_index == anim->videoStream) {
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG,
+ " DECODE: cached next packet\n");
+
+ avcodec_decode_video2(anim->pCodecCtx,
+ anim->pFrame, &frameFinished,
+ &anim->next_packet);
+
+ if (frameFinished) {
+ av_log(anim->pFormatCtx,
+ AV_LOG_DEBUG,
+ " FRAME DONE: "
+ "next_pts=%lld pkt_pts=%lld\n",
+ (anim->pFrame->pts == AV_NOPTS_VALUE) ?
+ -1 : anim->pFrame->pts,
+ (anim->pFrame->pkt_pts == AV_NOPTS_VALUE) ?
+ -1 : anim->pFrame->pkt_pts);
+ anim->next_pts =
+ av_get_pts_from_frame(anim->pFormatCtx,
+ anim->pFrame);
+ }
+
+ av_free_packet(&anim->next_packet);
+ anim->next_packet.stream_index = -1;
+ }
+
+ while((rval = av_read_frame(anim->pFormatCtx, &anim->next_packet)) >= 0) {
+ av_log(anim->pFormatCtx,
+ AV_LOG_DEBUG,
+ "%sREAD: strID=%d (VID: %d) dts=%lld pts=%lld "
+ "%s\n",
+ (anim->next_packet.stream_index == anim->videoStream)
+ ? "->" : " ",
+ anim->next_packet.stream_index,
+ anim->videoStream,
+ (anim->next_packet.dts == AV_NOPTS_VALUE) ? -1:
+ anim->next_packet.dts,
+ (anim->next_packet.pts == AV_NOPTS_VALUE) ? -1:
+ anim->next_packet.pts,
+ (anim->next_packet.flags & AV_PKT_FLAG_KEY) ?
+ " KEY" : "");
+ if (anim->next_packet.stream_index == anim->videoStream) {
+ if (frameFinished) {
+ av_log(anim->pFormatCtx,
+ AV_LOG_DEBUG,
+ " FRAME finished, we leave\n");
+ anim->next_undecoded_pts
+ = anim->next_packet.dts;
+ break;
+ }
+
+ avcodec_decode_video2(
+ anim->pCodecCtx,
+ anim->pFrame, &frameFinished,
+ &anim->next_packet);
+
+ if (frameFinished) {
+ anim->next_pts = av_get_pts_from_frame(
+ anim->pFormatCtx, anim->pFrame);
+
+ av_log(anim->pFormatCtx,
+ AV_LOG_DEBUG,
+ " FRAME DONE: next_pts=%lld "
+ "pkt_pts=%lld, guessed_pts=%lld\n",
+ (anim->pFrame->pts == AV_NOPTS_VALUE) ?
+ -1 : anim->pFrame->pts,
+ (anim->pFrame->pkt_pts
+ == AV_NOPTS_VALUE) ?
+ -1 : anim->pFrame->pkt_pts,
+ anim->next_pts);
+ }
+ }
+ av_free_packet(&anim->next_packet);
+ anim->next_packet.stream_index = -1;
+ }
+
+ if (rval < 0) {
+ av_log(anim->pFormatCtx,
+ AV_LOG_ERROR, " DECODE READ FAILED: av_read_frame() "
+ "returned error: %d\n", rval);
+ }
+ return (rval >= 0);
+}
+
+static void ffmpeg_decode_video_frame_scan(
+ struct anim * anim, int64_t pts_to_search)
+{
+ /* there seem to exist *very* silly GOP lengths out in the wild... */
+ int count = 1000;
+
+ av_log(anim->pFormatCtx,
+ AV_LOG_DEBUG,
+ "SCAN start: considering pts=%lld in search of %lld\n",
+ anim->next_pts, pts_to_search);
+
+ while (count > 0 && anim->next_pts < pts_to_search) {
+ av_log(anim->pFormatCtx,
+ AV_LOG_DEBUG,
+ " WHILE: pts=%lld in search of %lld\n",
+ anim->next_pts, pts_to_search);
+ if (!ffmpeg_decode_video_frame(anim)) {
+ break;
+ }
+ count--;
+ }
+ if (count == 0) {
+ av_log(anim->pFormatCtx,
+ AV_LOG_ERROR,
+ "SCAN failed: completely lost in stream, "
+ "bailing out at PTS=%lld, searching for PTS=%lld\n",
+ anim->next_pts, pts_to_search);
+ }
+ if (anim->next_pts == pts_to_search) {
+ av_log(anim->pFormatCtx,
+ AV_LOG_DEBUG, "SCAN HAPPY: we found our PTS!\n");
+ } else {
+ av_log(anim->pFormatCtx,
+ AV_LOG_ERROR, "SCAN UNHAPPY: PTS not matched!\n");
+ }
+}
+
+static int match_format(const char *name, AVFormatContext * pFormatCtx)
+{
+ const char *p;
+ int len, namelen;
+
+ const char *names = pFormatCtx->iformat->name;
+
+ if (!name || !names)
+ return 0;
+
+ namelen = strlen(name);
+ while ((p = strchr(names, ','))) {
+ len = MAX2(p - names, namelen);
+ if (!BLI_strncasecmp(name, names, len))
+ return 1;
+ names = p+1;
+ }
+ return !BLI_strcasecmp(name, names);
+}
+
+static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx)
+{
+ static const char * byte_seek_list [] = { "dv", "mpegts", 0 };
+ const char ** p;
+
+ if (pFormatCtx->iformat->flags & AVFMT_TS_DISCONT) {
+ return TRUE;
+ }
+
+ p = byte_seek_list;
+
+ while (*p) {
+ if (match_format(*p++, pFormatCtx)) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static ImBuf * ffmpeg_fetchibuf(struct anim * anim, int position,
+ IMB_Timecode_Type tc) {
int64_t pts_to_search = 0;
- int pos_found = 1;
- int filter_y = 0;
- int seek_by_bytes= 0;
- int preseek_count = 0;
+ double frame_rate;
+ double pts_time_base;
+ long long st_time;
+ struct anim_index * tc_index = 0;
+ AVStream * v_st;
+ int new_frame_index;
+ int old_frame_index;
if (anim == 0) return (0);
- ibuf = IMB_allocImBuf(anim->x, anim->y, 32, IB_rect);
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: pos=%d\n", position);
- avpicture_fill((AVPicture*) anim->pFrameRGB,
- (unsigned char*) ibuf->rect,
- PIX_FMT_RGBA, anim->x, anim->y);
-
- if (position != anim->curposition + 1) {
- if (position > anim->curposition + 1
- && anim->preseek
- && position - (anim->curposition + 1) < anim->preseek) {
- while(av_read_frame(anim->pFormatCtx, &packet)>=0) {
- if (packet.stream_index == anim->videoStream) {
- avcodec_decode_video2(
- anim->pCodecCtx,
- anim->pFrame, &frameFinished,
- &packet);
-
- if (frameFinished) {
- anim->curposition++;
- }
- }
- av_free_packet(&packet);
- if (position == anim->curposition+1) {
- break;
- }
- }
+ if (tc != IMB_TC_NONE) {
+ tc_index = IMB_anim_open_index(anim, tc);
+ }
+
+ v_st = anim->pFormatCtx->streams[anim->videoStream];
+
+ frame_rate = av_q2d(v_st->r_frame_rate);
+
+ st_time = anim->pFormatCtx->start_time;
+ pts_time_base = av_q2d(v_st->time_base);
+
+ if (tc_index) {
+ new_frame_index = IMB_indexer_get_frame_index(
+ tc_index, position);
+ old_frame_index = IMB_indexer_get_frame_index(
+ tc_index, anim->curposition);
+ pts_to_search = IMB_indexer_get_pts(
+ tc_index, new_frame_index);
+ } else {
+ pts_to_search = (long long)
+ floor(((double) position) / pts_time_base / frame_rate + 0.5);
+
+ if (st_time != AV_NOPTS_VALUE) {
+ pts_to_search += st_time / pts_time_base
+ / AV_TIME_BASE;
}
}
-/* disable seek_by_bytes for now, since bitrates are guessed wrong!
- also: MPEG2TS-seeking was fixed in later versions of ffmpeg, so problem
- is somewhat fixed by now (until we add correct timecode management code...)
-*/
-#if 0
- seek_by_bytes = !!(anim->pFormatCtx->iformat->flags & AVFMT_TS_DISCONT);
-#else
- seek_by_bytes = FALSE;
-#endif
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG,
+ "FETCH: looking for PTS=%lld "
+ "(pts_timebase=%g, frame_rate=%g, st_time=%lld)\n",
+ pts_to_search, pts_time_base, frame_rate, st_time);
+
+ if (anim->last_frame &&
+ anim->last_pts <= pts_to_search && anim->next_pts > pts_to_search){
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG,
+ "FETCH: frame repeat: last: %lld next: %lld\n",
+ anim->last_pts, anim->next_pts);
+ IMB_refImBuf(anim->last_frame);
+ anim->curposition = position;
+ return anim->last_frame;
+ }
+
+ IMB_freeImBuf(anim->last_frame);
+
+ if (anim->next_pts <= pts_to_search &&
+ anim->next_undecoded_pts > pts_to_search) {
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG,
+ "FETCH: no seek necessary: "
+ "next: %lld next undecoded: %lld\n",
+ anim->next_pts, anim->next_undecoded_pts);
+
+ /* we are already done :) */
+
+ } else if (position > anim->curposition + 1
+ && anim->preseek
+ && !tc_index
+ && position - (anim->curposition + 1) < anim->preseek) {
+
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG,
+ "FETCH: within preseek interval (no index)\n");
+
+ ffmpeg_decode_video_frame_scan(anim, pts_to_search);
+ } else if (tc_index &&
+ IMB_indexer_can_scan(tc_index, old_frame_index,
+ new_frame_index)) {
+
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG,
+ "FETCH: within preseek interval "
+ "(index tells us)\n");
- if (position != anim->curposition + 1) {
- double frame_rate =
- av_q2d(anim->pFormatCtx->streams[anim->videoStream]
- ->r_frame_rate);
- double pts_time_base = av_q2d(anim->pFormatCtx->streams[anim->videoStream]->time_base);
+ ffmpeg_decode_video_frame_scan(anim, pts_to_search);
+ } else if (position != anim->curposition + 1) {
long long pos;
- long long st_time = anim->pFormatCtx->start_time;
int ret;
- if (seek_by_bytes) {
- pos = position - anim->preseek;
- if (pos < 0) {
- pos = 0;
- }
- preseek_count = position - pos;
+ if (tc_index) {
+ unsigned long long dts;
+
+ pos = IMB_indexer_get_seek_pos(
+ tc_index, new_frame_index);
+ dts = IMB_indexer_get_seek_pos_dts(
+ tc_index, new_frame_index);
+
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG,
+ "TC INDEX seek pos = %lld\n", pos);
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG,
+ "TC INDEX seek dts = %lld\n", dts);
- pos *= anim->pFormatCtx->bit_rate / frame_rate;
- pos /= 8;
+ if (ffmpeg_seek_by_byte(anim->pFormatCtx)) {
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG,
+ "... using BYTE pos\n");
+
+ ret = av_seek_frame(anim->pFormatCtx,
+ -1,
+ pos, AVSEEK_FLAG_BYTE);
+ av_update_cur_dts(anim->pFormatCtx, v_st, dts);
+ } else {
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG,
+ "... using DTS pos\n");
+ ret = av_seek_frame(anim->pFormatCtx,
+ anim->videoStream,
+ dts, AVSEEK_FLAG_BACKWARD);
+ }
} else {
pos = (long long) (position - anim->preseek)
* AV_TIME_BASE / frame_rate;
if (pos < 0) {
pos = 0;
}
-
+
if (st_time != AV_NOPTS_VALUE) {
pos += st_time;
}
- }
- ret = av_seek_frame(anim->pFormatCtx, -1,
- pos,
- AVSEEK_FLAG_BACKWARD | (
- seek_by_bytes
- ? AVSEEK_FLAG_ANY
- | AVSEEK_FLAG_BYTE : 0));
- if (ret < 0) {
- fprintf(stderr, "error while seeking: %d\n", ret);
+ ret = av_seek_frame(anim->pFormatCtx, -1,
+ pos, AVSEEK_FLAG_BACKWARD);
}
- pts_to_search = (long long)
- (((double) position) / pts_time_base / frame_rate);
- if (st_time != AV_NOPTS_VALUE) {
- pts_to_search += st_time / pts_time_base/ AV_TIME_BASE;
+ if (ret < 0) {
+ av_log(anim->pFormatCtx, AV_LOG_ERROR,
+ "FETCH: "
+ "error while seeking to DTS = %lld "
+ "(frameno = %d, PTS = %lld): errcode = %d\n",
+ pos, position, pts_to_search, ret);
}
- pos_found = 0;
avcodec_flush_buffers(anim->pCodecCtx);
- }
- while(av_read_frame(anim->pFormatCtx, &packet)>=0) {
- if(packet.stream_index == anim->videoStream) {
- avcodec_decode_video2(anim->pCodecCtx,
- anim->pFrame, &frameFinished,
- &packet);
+ anim->next_pts = -1;
- if (seek_by_bytes && preseek_count > 0) {
- preseek_count--;
- }
+ if (anim->next_packet.stream_index == anim->videoStream) {
+ av_free_packet(&anim->next_packet);
+ anim->next_packet.stream_index = -1;
+ }
- if (frameFinished && !pos_found) {
- if (seek_by_bytes) {
- if (!preseek_count) {
- pos_found = 1;
- anim->curposition = position;
- }
- } else {
- if (packet.dts >= pts_to_search) {
- pos_found = 1;
- anim->curposition = position;
- }
- }
- }
+ /* memset(anim->pFrame,...) ?? */
- if(frameFinished && pos_found == 1) {
- ffmpeg_postprocess(anim, ibuf, &filter_y);
- av_free_packet(&packet);
- break;
- }
+ if (ret >= 0) {
+ ffmpeg_decode_video_frame_scan(anim, pts_to_search);
}
-
- av_free_packet(&packet);
+ } else if (position == 0 && anim->curposition == -1) {
+ /* first frame without seeking special case... */
+ ffmpeg_decode_video_frame(anim);
}
- if (filter_y && ibuf) {
- IMB_filtery(ibuf);
- }
+ anim->last_frame = IMB_allocImBuf(anim->x, anim->y, 32, IB_rect);
- ibuf->profile = IB_PROFILE_SRGB;
+ ffmpeg_postprocess(anim);
- return(ibuf);
+ anim->last_pts = anim->next_pts;
+
+ ffmpeg_decode_video_frame(anim);
+
+ anim->curposition = position;
+
+ IMB_refImBuf(anim->last_frame);
+
+ return anim->last_frame;
}
static void free_anim_ffmpeg(struct anim * anim) {
@@ -934,6 +1186,10 @@ static void free_anim_ffmpeg(struct anim * anim) {
}
av_free(anim->pFrameDeinterlaced);
sws_freeContext(anim->img_convert_ctx);
+ IMB_freeImBuf(anim->last_frame);
+ if (anim->next_packet.stream_index != -1) {
+ av_free_packet(&anim->next_packet);
+ }
}
anim->duration = 0;
}
@@ -1063,16 +1319,19 @@ struct ImBuf * IMB_anim_previewframe(struct anim * anim) {
struct ImBuf * ibuf = NULL;
int position = 0;
- ibuf = IMB_anim_absolute(anim, 0);
+ ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
if (ibuf) {
IMB_freeImBuf(ibuf);
position = anim->duration / 2;
- ibuf = IMB_anim_absolute(anim, position);
+ ibuf = IMB_anim_absolute(anim, position, IMB_TC_NONE,
+ IMB_PROXY_NONE);
}
return ibuf;
}
-struct ImBuf * IMB_anim_absolute(struct anim * anim, int position) {
+struct ImBuf * IMB_anim_absolute(struct anim * anim, int position,
+ IMB_Timecode_Type tc,
+ IMB_Proxy_Size preview_size) {
struct ImBuf * ibuf = NULL;
char head[256], tail[256];
unsigned short digits;
@@ -1095,6 +1354,18 @@ struct ImBuf * IMB_anim_absolute(struct anim * anim, int position) {
if (position < 0) return(NULL);
if (position >= anim->duration) return(NULL);
+ if (preview_size != IMB_PROXY_NONE) {
+ struct anim * proxy = IMB_anim_open_proxy(anim, preview_size);
+
+ if (proxy) {
+ position = IMB_anim_index_get_frame_index(
+ anim, tc, position);
+ return IMB_anim_absolute(
+ proxy, position,
+ IMB_TC_NONE, IMB_PROXY_NONE);
+ }
+ }
+
switch(anim->curtype) {
case ANIM_SEQUENCE:
pic = an_stringdec(anim->first, head, tail, &digits);
@@ -1127,7 +1398,7 @@ struct ImBuf * IMB_anim_absolute(struct anim * anim, int position) {
#endif
#ifdef WITH_FFMPEG
case ANIM_FFMPEG:
- ibuf = ffmpeg_fetchibuf(anim, position);
+ ibuf = ffmpeg_fetchibuf(anim, position, tc);
if (ibuf)
anim->curposition = position;
filter_y = 0; /* done internally */
@@ -1151,8 +1422,29 @@ struct ImBuf * IMB_anim_absolute(struct anim * anim, int position) {
/***/
-int IMB_anim_get_duration(struct anim *anim) {
- return anim->duration;
+int IMB_anim_get_duration(struct anim *anim, IMB_Timecode_Type tc) {
+ struct anim_index * idx;
+ if (tc == IMB_TC_NONE) {
+ return anim->duration;
+ }
+
+ idx = IMB_anim_open_index(anim, tc);
+ if (!idx) {
+ return anim->duration;
+ }
+
+ return IMB_indexer_get_duration(idx);
+}
+
+int IMB_anim_get_fps(struct anim * anim,
+ short * frs_sec, float * frs_sec_base)
+{
+ if (anim->frs_sec) {
+ *frs_sec = anim->frs_sec;
+ *frs_sec_base = anim->frs_sec_base;
+ return TRUE;
+ }
+ return FALSE;
}
void IMB_anim_set_preseek(struct anim * anim, int preseek)
diff --git a/source/blender/imbuf/intern/filter.c b/source/blender/imbuf/intern/filter.c
index 7f1eef80318..2677913caed 100644
--- a/source/blender/imbuf/intern/filter.c
+++ b/source/blender/imbuf/intern/filter.c
@@ -1,5 +1,7 @@
/*
*
+ * $Id$
+ *
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c
new file mode 100644
index 00000000000..f624694b324
--- /dev/null
+++ b/source/blender/imbuf/intern/indexer.c
@@ -0,0 +1,1135 @@
+/*
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Peter Schlaile <peter [at] schlaile [dot] de> 2011
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+*/
+
+#include "IMB_indexer.h"
+#include "IMB_anim.h"
+#include "AVI_avi.h"
+#include "imbuf.h"
+#include "MEM_guardedalloc.h"
+#include "BLI_utildefines.h"
+#include "BLI_blenlib.h"
+#include "BLI_math_base.h"
+#include "BLI_string.h"
+#include "MEM_guardedalloc.h"
+#include "DNA_userdef_types.h"
+#include "BKE_global.h"
+#include <stdlib.h>
+
+#ifdef WITH_FFMPEG
+
+#include "ffmpeg_compat.h"
+
+#endif //WITH_FFMPEG
+
+
+static char magic[] = "BlenMIdx";
+static char temp_ext [] = "_part";
+
+static int proxy_sizes[] = { IMB_PROXY_25, IMB_PROXY_50, IMB_PROXY_75,
+ IMB_PROXY_100 };
+static float proxy_fac[] = { 0.25, 0.50, 0.75, 1.00 };
+static int tc_types[] = { IMB_TC_RECORD_RUN, IMB_TC_FREE_RUN,
+ IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN };
+
+#define INDEX_FILE_VERSION 1
+
+/* ----------------------------------------------------------------------
+ - special indexers
+ ----------------------------------------------------------------------
+ */
+
+extern void IMB_indexer_dv_new(anim_index_builder * idx);
+
+
+/* ----------------------------------------------------------------------
+ - time code index functions
+ ---------------------------------------------------------------------- */
+
+anim_index_builder * IMB_index_builder_create(const char * name)
+{
+
+ anim_index_builder * rv
+ = MEM_callocN( sizeof(struct anim_index_builder),
+ "index builder");
+
+ fprintf(stderr, "Starting work on index: %s\n", name);
+
+ BLI_strncpy(rv->name, name, sizeof(rv->name));
+ BLI_strncpy(rv->temp_name, name, sizeof(rv->temp_name));
+
+ strcat(rv->temp_name, temp_ext);
+
+ BLI_make_existing_file(rv->temp_name);
+
+ rv->fp = fopen(rv->temp_name, "w");
+
+ if (!rv->fp) {
+ fprintf(stderr, "Couldn't open index target: %s! "
+ "Index build broken!\n", rv->temp_name);
+ MEM_freeN(rv);
+ return NULL;
+ }
+
+ fprintf(rv->fp, "%s%c%.3d", magic, (ENDIAN_ORDER==B_ENDIAN)?'V':'v',
+ INDEX_FILE_VERSION);
+
+ return rv;
+}
+
+void IMB_index_builder_add_entry(anim_index_builder * fp,
+ int frameno,unsigned long long seek_pos,
+ unsigned long long seek_pos_dts,
+ unsigned long long pts)
+{
+ fwrite(&frameno, sizeof(int), 1, fp->fp);
+ fwrite(&seek_pos, sizeof(unsigned long long), 1, fp->fp);
+ fwrite(&seek_pos_dts, sizeof(unsigned long long), 1, fp->fp);
+ fwrite(&pts, sizeof(unsigned long long), 1, fp->fp);
+}
+
+void IMB_index_builder_proc_frame(anim_index_builder * fp,
+ unsigned char * buffer,
+ int data_size,
+ int frameno, unsigned long long seek_pos,
+ unsigned long long seek_pos_dts,
+ unsigned long long pts)
+{
+ if (fp->proc_frame) {
+ anim_index_entry e;
+ e.frameno = frameno;
+ e.seek_pos = seek_pos;
+ e.seek_pos_dts = seek_pos_dts;
+ e.pts = pts;
+
+ fp->proc_frame(fp, buffer, data_size, &e);
+ } else {
+ IMB_index_builder_add_entry(fp, frameno, seek_pos,
+ seek_pos_dts, pts);
+ }
+}
+
+void IMB_index_builder_finish(anim_index_builder * fp, int rollback)
+{
+ if (fp->delete_priv_data) {
+ fp->delete_priv_data(fp);
+ }
+
+ fclose(fp->fp);
+
+ if (rollback) {
+ unlink(fp->temp_name);
+ } else {
+ rename(fp->temp_name, fp->name);
+ }
+
+ MEM_freeN(fp);
+}
+
+struct anim_index * IMB_indexer_open(const char * name)
+{
+ char header[13];
+ struct anim_index * idx;
+ FILE * fp = fopen(name, "rb");
+ int i;
+
+ if (!fp) {
+ return 0;
+ }
+
+ if (fread(header, 12, 1, fp) != 1) {
+ fclose(fp);
+ return 0;
+ }
+
+ header[12] = 0;
+
+ if (memcmp(header, magic, 8) != 0) {
+ fclose(fp);
+ return 0;
+ }
+
+ if (atoi(header+9) != INDEX_FILE_VERSION) {
+ fclose(fp);
+ return 0;
+ }
+
+ idx = MEM_callocN( sizeof(struct anim_index), "anim_index");
+
+ BLI_strncpy(idx->name, name, sizeof(idx->name));
+
+ fseek(fp, 0, SEEK_END);
+
+ idx->num_entries = (ftell(fp) - 12)
+ / (sizeof(int) // framepos
+ + sizeof(unsigned long long) // seek_pos
+ + sizeof(unsigned long long) // seek_pos_dts
+ + sizeof(unsigned long long) // pts
+ );
+
+ fseek(fp, 12, SEEK_SET);
+
+ idx->entries = MEM_callocN( sizeof(struct anim_index_entry)
+ * idx->num_entries, "anim_index_entries");
+
+ for (i = 0; i < idx->num_entries; i++) {
+ fread(&idx->entries[i].frameno,
+ sizeof(int), 1, fp);
+ fread(&idx->entries[i].seek_pos,
+ sizeof(unsigned long long), 1, fp);
+ fread(&idx->entries[i].seek_pos_dts,
+ sizeof(unsigned long long), 1, fp);
+ fread(&idx->entries[i].pts,
+ sizeof(unsigned long long), 1, fp);
+ }
+
+ if (((ENDIAN_ORDER == B_ENDIAN) != (header[8] == 'V'))) {
+ for (i = 0; i < idx->num_entries; i++) {
+ SWITCH_INT(idx->entries[i].frameno);
+ SWITCH_INT64(idx->entries[i].seek_pos);
+ SWITCH_INT64(idx->entries[i].seek_pos_dts);
+ SWITCH_INT64(idx->entries[i].pts);
+ }
+ }
+
+ fclose(fp);
+
+ return idx;
+}
+
+unsigned long long IMB_indexer_get_seek_pos(
+ struct anim_index * idx, int frame_index)
+{
+ if (frame_index < 0) {
+ frame_index = 0;
+ }
+ if (frame_index >= idx->num_entries) {
+ frame_index = idx->num_entries - 1;
+ }
+ return idx->entries[frame_index].seek_pos;
+}
+
+unsigned long long IMB_indexer_get_seek_pos_dts(
+ struct anim_index * idx, int frame_index)
+{
+ if (frame_index < 0) {
+ frame_index = 0;
+ }
+ if (frame_index >= idx->num_entries) {
+ frame_index = idx->num_entries - 1;
+ }
+ return idx->entries[frame_index].seek_pos_dts;
+}
+
+int IMB_indexer_get_frame_index(struct anim_index * idx, int frameno)
+{
+ int len = idx->num_entries;
+ int half;
+ int middle;
+ int first = 0;
+
+ /* bsearch (lower bound) the right index */
+
+ while (len > 0) {
+ half = len >> 1;
+ middle = first;
+
+ middle += half;
+
+ if (idx->entries[middle].frameno < frameno) {
+ first = middle;
+ ++first;
+ len = len - half - 1;
+ } else {
+ len = half;
+ }
+ }
+
+ if (first == idx->num_entries) {
+ return idx->num_entries - 1;
+ } else {
+ return first;
+ }
+}
+
+unsigned long long IMB_indexer_get_pts(struct anim_index * idx,
+ int frame_index)
+{
+ if (frame_index < 0) {
+ frame_index = 0;
+ }
+ if (frame_index >= idx->num_entries) {
+ frame_index = idx->num_entries - 1;
+ }
+ return idx->entries[frame_index].pts;
+}
+
+int IMB_indexer_get_duration(struct anim_index * idx)
+{
+ if (idx->num_entries == 0) {
+ return 0;
+ }
+ return idx->entries[idx->num_entries-1].frameno + 1;
+}
+
+int IMB_indexer_can_scan(struct anim_index * idx,
+ int old_frame_index, int new_frame_index)
+{
+ /* makes only sense, if it is the same I-Frame and we are not
+ trying to run backwards in time... */
+ return (IMB_indexer_get_seek_pos(idx, old_frame_index)
+ == IMB_indexer_get_seek_pos(idx, new_frame_index) &&
+ old_frame_index < new_frame_index);
+}
+
+void IMB_indexer_close(struct anim_index * idx)
+{
+ MEM_freeN(idx->entries);
+ MEM_freeN(idx);
+}
+
+int IMB_proxy_size_to_array_index(IMB_Proxy_Size pr_size)
+{
+ switch (pr_size) {
+ case IMB_PROXY_NONE: /* if we got here, something is broken anyways,
+ so sane defaults... */
+ return 0;
+ case IMB_PROXY_25:
+ return 0;
+ case IMB_PROXY_50:
+ return 1;
+ case IMB_PROXY_75:
+ return 2;
+ case IMB_PROXY_100:
+ return 3;
+ default:
+ return 0;
+ };
+ return 0;
+}
+
+int IMB_timecode_to_array_index(IMB_Timecode_Type tc)
+{
+ switch (tc) {
+ case IMB_TC_NONE: /* if we got here, something is broken anyways,
+ so sane defaults... */
+ return 0;
+ case IMB_TC_RECORD_RUN:
+ return 0;
+ case IMB_TC_FREE_RUN:
+ return 1;
+ case IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN:
+ return 2;
+ default:
+ return 0;
+ };
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------
+ - rebuild helper functions
+ ---------------------------------------------------------------------- */
+
+static void get_index_dir(struct anim * anim, char * index_dir)
+{
+ if (!anim->index_dir[0]) {
+ char fname[FILE_MAXFILE];
+ BLI_strncpy(index_dir, anim->name, FILE_MAXDIR);
+ BLI_splitdirstring(index_dir, fname);
+ BLI_join_dirfile(index_dir, FILE_MAXDIR, index_dir, "BL_proxy");
+ BLI_join_dirfile(index_dir, FILE_MAXDIR, index_dir, fname);
+ } else {
+ BLI_strncpy(index_dir, anim->index_dir, FILE_MAXDIR);
+ }
+}
+
+static void get_proxy_filename(struct anim * anim, IMB_Proxy_Size preview_size,
+ char * fname, int temp)
+{
+ char index_dir[FILE_MAXDIR];
+ int i = IMB_proxy_size_to_array_index(preview_size);
+
+ char proxy_name[256];
+ char proxy_temp_name[256];
+ char stream_suffix[20];
+
+ stream_suffix[0] = 0;
+
+ if (anim->streamindex > 0) {
+ BLI_snprintf(stream_suffix, 20, "_st%d", anim->streamindex);
+ }
+
+ BLI_snprintf(proxy_name, 256, "proxy_%d%s.avi",
+ (int) (proxy_fac[i] * 100), stream_suffix);
+ BLI_snprintf(proxy_temp_name, 256, "proxy_%d%s_part.avi",
+ (int) (proxy_fac[i] * 100), stream_suffix);
+
+ get_index_dir(anim, index_dir);
+
+ BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR, index_dir,
+ temp ? proxy_temp_name : proxy_name);
+}
+
+static void get_tc_filename(struct anim * anim, IMB_Timecode_Type tc,
+ char * fname)
+{
+ char index_dir[FILE_MAXDIR];
+ int i = IMB_timecode_to_array_index(tc);
+ char * index_names[] = {
+ "record_run%s.blen_tc", "free_run%s.blen_tc",
+ "interp_free_run%s.blen_tc" };
+
+ char stream_suffix[20];
+ char index_name[256];
+
+ stream_suffix[0] = 0;
+
+ if (anim->streamindex > 0) {
+ BLI_snprintf(stream_suffix, 20, "_st%d", anim->streamindex);
+ }
+
+ BLI_snprintf(index_name, 256, index_names[i], stream_suffix);
+
+ get_index_dir(anim, index_dir);
+
+ BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR,
+ index_dir, index_name);
+}
+
+/* ----------------------------------------------------------------------
+ - ffmpeg rebuilder
+ ---------------------------------------------------------------------- */
+
+#ifdef WITH_FFMPEG
+
+struct proxy_output_ctx {
+ AVFormatContext* of;
+ AVStream* st;
+ AVCodecContext* c;
+ AVCodec* codec;
+ struct SwsContext * sws_ctx;
+ AVFrame* frame;
+ uint8_t* video_buffer;
+ int video_buffersize;
+ int cfra;
+ int proxy_size;
+ int orig_height;
+ struct anim * anim;
+};
+
+// work around stupid swscaler 16 bytes alignment bug...
+
+static int round_up(int x, int mod)
+{
+ return x + ((mod - (x % mod)) % mod);
+}
+
+static struct proxy_output_ctx * alloc_proxy_output_ffmpeg(
+ struct anim * anim,
+ AVStream * st, int proxy_size, int width, int height,
+ int quality)
+{
+ struct proxy_output_ctx * rv = MEM_callocN(
+ sizeof(struct proxy_output_ctx), "alloc_proxy_output");
+
+ char fname[FILE_MAXDIR+FILE_MAXFILE];
+
+ // JPEG requires this
+ width = round_up(width, 8);
+ height = round_up(height, 8);
+
+ rv->proxy_size = proxy_size;
+ rv->anim = anim;
+
+ get_proxy_filename(rv->anim, rv->proxy_size, fname, TRUE);
+ BLI_make_existing_file(fname);
+
+ rv->of = avformat_alloc_context();
+ rv->of->oformat = av_guess_format("avi", NULL, NULL);
+
+ BLI_snprintf(rv->of->filename, sizeof(rv->of->filename), "%s", fname);
+
+ fprintf(stderr, "Starting work on proxy: %s\n", rv->of->filename);
+
+ rv->st = av_new_stream(rv->of, 0);
+ rv->c = rv->st->codec;
+ rv->c->codec_type = AVMEDIA_TYPE_VIDEO;
+ rv->c->codec_id = CODEC_ID_MJPEG;
+ rv->c->width = width;
+ rv->c->height = height;
+
+ rv->of->oformat->video_codec = rv->c->codec_id;
+ rv->codec = avcodec_find_encoder(rv->c->codec_id);
+
+ if (!rv->codec) {
+ fprintf(stderr, "No ffmpeg MJPEG encoder available? "
+ "Proxy not built!\n");
+ av_free(rv->of);
+ return NULL;
+ }
+
+ if (rv->codec->pix_fmts) {
+ rv->c->pix_fmt = rv->codec->pix_fmts[0];
+ } else {
+ rv->c->pix_fmt = PIX_FMT_YUVJ420P;
+ }
+
+ rv->c->sample_aspect_ratio
+ = rv->st->sample_aspect_ratio
+ = st->codec->sample_aspect_ratio;
+
+ rv->c->time_base.den = 25;
+ rv->c->time_base.num = 1;
+ rv->st->time_base = rv->c->time_base;
+
+ if (rv->of->flags & AVFMT_GLOBALHEADER) {
+ rv->c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+ }
+
+ if (av_set_parameters(rv->of, NULL) < 0) {
+ fprintf(stderr, "Couldn't set output parameters? "
+ "Proxy not built!\n");
+ av_free(rv->of);
+ return 0;
+ }
+
+ if (avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE) < 0) {
+ fprintf(stderr, "Couldn't open outputfile! "
+ "Proxy not built!\n");
+ av_free(rv->of);
+ return 0;
+ }
+
+ avcodec_open(rv->c, rv->codec);
+
+ rv->video_buffersize = 2000000;
+ rv->video_buffer = (uint8_t*)MEM_mallocN(
+ rv->video_buffersize, "FFMPEG video buffer");
+
+ rv->orig_height = st->codec->height;
+
+ if (st->codec->width != width || st->codec->height != height
+ || st->codec->pix_fmt != rv->c->pix_fmt) {
+ rv->frame = avcodec_alloc_frame();
+ avpicture_fill((AVPicture*) rv->frame,
+ MEM_mallocN(avpicture_get_size(
+ rv->c->pix_fmt,
+ round_up(width, 16), height),
+ "alloc proxy output frame"),
+ rv->c->pix_fmt, round_up(width, 16), height);
+
+ rv->sws_ctx = sws_getContext(
+ st->codec->width,
+ st->codec->height,
+ st->codec->pix_fmt,
+ width, height,
+ rv->c->pix_fmt,
+ SWS_FAST_BILINEAR | SWS_PRINT_INFO,
+ NULL, NULL, NULL);
+ }
+
+ av_write_header(rv->of);
+
+ return rv;
+}
+
+static int add_to_proxy_output_ffmpeg(
+ struct proxy_output_ctx * ctx, AVFrame * frame)
+{
+ int outsize = 0;
+
+ if (!ctx) {
+ return 0;
+ }
+
+ if (ctx->sws_ctx && frame &&
+ (frame->data[0] || frame->data[1] ||
+ frame->data[2] || frame->data[3])) {
+ sws_scale(ctx->sws_ctx, (const uint8_t * const*) frame->data,
+ frame->linesize, 0, ctx->orig_height,
+ ctx->frame->data, ctx->frame->linesize);
+ }
+
+ ctx->frame->pts = ctx->cfra++;
+
+ outsize = avcodec_encode_video(
+ ctx->c, ctx->video_buffer, ctx->video_buffersize,
+ ctx->sws_ctx ? (frame ? ctx->frame : 0) : frame);
+
+ if (outsize < 0) {
+ fprintf(stderr, "Error encoding proxy frame %d for '%s'\n",
+ ctx->cfra - 1, ctx->of->filename);
+ return 0;
+ }
+
+ if (outsize != 0) {
+ AVPacket packet;
+ av_init_packet(&packet);
+
+ if (ctx->c->coded_frame->pts != AV_NOPTS_VALUE) {
+ packet.pts = av_rescale_q(ctx->c->coded_frame->pts,
+ ctx->c->time_base,
+ ctx->st->time_base);
+ }
+ if (ctx->c->coded_frame->key_frame)
+ packet.flags |= AV_PKT_FLAG_KEY;
+
+ packet.stream_index = ctx->st->index;
+ packet.data = ctx->video_buffer;
+ packet.size = outsize;
+
+ if (av_interleaved_write_frame(ctx->of, &packet) != 0) {
+ fprintf(stderr, "Error writing proxy frame %d "
+ "into '%s'\n", ctx->cfra - 1,
+ ctx->of->filename);
+ return 0;
+ }
+
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static void free_proxy_output_ffmpeg(struct proxy_output_ctx * ctx,
+ int rollback)
+{
+ int i;
+ char fname[FILE_MAXDIR+FILE_MAXFILE];
+ char fname_tmp[FILE_MAXDIR+FILE_MAXFILE];
+
+ if (!ctx) {
+ return;
+ }
+
+ if (!rollback) {
+ while (add_to_proxy_output_ffmpeg(ctx, NULL)) ;
+ }
+
+ avcodec_flush_buffers(ctx->c);
+
+ av_write_trailer(ctx->of);
+
+ avcodec_close(ctx->c);
+
+ for (i = 0; i < ctx->of->nb_streams; i++) {
+ if (&ctx->of->streams[i]) {
+ av_freep(&ctx->of->streams[i]);
+ }
+ }
+
+ if (ctx->of->oformat) {
+ if (!(ctx->of->oformat->flags & AVFMT_NOFILE)) {
+ avio_close(ctx->of->pb);
+ }
+ }
+ av_free(ctx->of);
+
+ MEM_freeN(ctx->video_buffer);
+
+ if (ctx->sws_ctx) {
+ sws_freeContext(ctx->sws_ctx);
+
+ MEM_freeN(ctx->frame->data[0]);
+ av_free(ctx->frame);
+ }
+
+ get_proxy_filename(ctx->anim, ctx->proxy_size,
+ fname_tmp, TRUE);
+
+ if (rollback) {
+ unlink(fname_tmp);
+ } else {
+ get_proxy_filename(ctx->anim, ctx->proxy_size,
+ fname, FALSE);
+ rename(fname_tmp, fname);
+ }
+
+ MEM_freeN(ctx);
+}
+
+
+static int index_rebuild_ffmpeg(struct anim * anim,
+ IMB_Timecode_Type tcs_in_use,
+ IMB_Proxy_Size proxy_sizes_in_use,
+ int quality,
+ short *stop, short *do_update,
+ float *progress)
+{
+ int i, videoStream;
+ unsigned long long seek_pos = 0;
+ unsigned long long last_seek_pos = 0;
+ unsigned long long seek_pos_dts = 0;
+ unsigned long long seek_pos_pts = 0;
+ unsigned long long last_seek_pos_dts = 0;
+ unsigned long long start_pts = 0;
+ double frame_rate;
+ double pts_time_base;
+ int frameno = 0;
+ int start_pts_set = FALSE;
+
+ AVFormatContext *iFormatCtx;
+ AVCodecContext *iCodecCtx;
+ AVCodec *iCodec;
+ AVStream *iStream;
+ AVFrame* in_frame = 0;
+ AVPacket next_packet;
+ int streamcount;
+
+ struct proxy_output_ctx * proxy_ctx[IMB_PROXY_MAX_SLOT];
+ anim_index_builder * indexer [IMB_TC_MAX_SLOT];
+
+ int num_proxy_sizes = IMB_PROXY_MAX_SLOT;
+ int num_indexers = IMB_TC_MAX_SLOT;
+ uint64_t stream_size;
+
+ memset(proxy_ctx, 0, sizeof(proxy_ctx));
+ memset(indexer, 0, sizeof(indexer));
+
+ if(av_open_input_file(&iFormatCtx, anim->name, NULL, 0, NULL) != 0) {
+ return 0;
+ }
+
+ if (av_find_stream_info(iFormatCtx) < 0) {
+ av_close_input_file(iFormatCtx);
+ return 0;
+ }
+
+ streamcount = anim->streamindex;
+
+ /* Find the video stream */
+ videoStream = -1;
+ for (i = 0; i < iFormatCtx->nb_streams; i++)
+ if(iFormatCtx->streams[i]->codec->codec_type
+ == AVMEDIA_TYPE_VIDEO) {
+ if (streamcount > 0) {
+ streamcount--;
+ continue;
+ }
+ videoStream = i;
+ break;
+ }
+
+ if (videoStream == -1) {
+ av_close_input_file(iFormatCtx);
+ return 0;
+ }
+
+ iStream = iFormatCtx->streams[videoStream];
+ iCodecCtx = iStream->codec;
+
+ iCodec = avcodec_find_decoder(iCodecCtx->codec_id);
+
+ if (iCodec == NULL) {
+ av_close_input_file(iFormatCtx);
+ return 0;
+ }
+
+ iCodecCtx->workaround_bugs = 1;
+
+ if (avcodec_open(iCodecCtx, iCodec) < 0) {
+ av_close_input_file(iFormatCtx);
+ return 0;
+ }
+
+ in_frame = avcodec_alloc_frame();
+
+ stream_size = avio_size(iFormatCtx->pb);
+
+ for (i = 0; i < num_proxy_sizes; i++) {
+ if (proxy_sizes_in_use & proxy_sizes[i]) {
+ proxy_ctx[i] = alloc_proxy_output_ffmpeg(
+ anim, iStream, proxy_sizes[i],
+ iCodecCtx->width * proxy_fac[i],
+ iCodecCtx->height * proxy_fac[i],
+ quality);
+ if (!proxy_ctx[i]) {
+ proxy_sizes_in_use &= ~proxy_sizes[i];
+ }
+ }
+ }
+
+ for (i = 0; i < num_indexers; i++) {
+ if (tcs_in_use & tc_types[i]) {
+ char fname[FILE_MAXDIR+FILE_MAXFILE];
+
+ get_tc_filename(anim, tc_types[i], fname);
+
+ indexer[i] = IMB_index_builder_create(fname);
+ if (!indexer[i]) {
+ tcs_in_use &= ~tc_types[i];
+ }
+ }
+ }
+
+ frame_rate = av_q2d(iStream->r_frame_rate);
+ pts_time_base = av_q2d(iStream->time_base);
+
+ while(av_read_frame(iFormatCtx, &next_packet) >= 0) {
+ int frame_finished = 0;
+ float next_progress = ((int)floor(((double) next_packet.pos) * 100 /
+ ((double) stream_size)+0.5)) / 100;
+
+ if (*progress != next_progress) {
+ *progress = next_progress;
+ *do_update = 1;
+ }
+
+ if (*stop) {
+ av_free_packet(&next_packet);
+ break;
+ }
+
+ if (next_packet.stream_index == videoStream) {
+ if (next_packet.flags & AV_PKT_FLAG_KEY) {
+ last_seek_pos = seek_pos;
+ last_seek_pos_dts = seek_pos_dts;
+ seek_pos = next_packet.pos;
+ seek_pos_dts = next_packet.dts;
+ seek_pos_pts = next_packet.pts;
+ }
+
+ avcodec_decode_video2(
+ iCodecCtx, in_frame, &frame_finished,
+ &next_packet);
+ }
+
+ if (frame_finished) {
+ unsigned long long s_pos = seek_pos;
+ unsigned long long s_dts = seek_pos_dts;
+ unsigned long long pts
+ = av_get_pts_from_frame(iFormatCtx, in_frame);
+
+ for (i = 0; i < num_proxy_sizes; i++) {
+ add_to_proxy_output_ffmpeg(
+ proxy_ctx[i], in_frame);
+ }
+
+ if (!start_pts_set) {
+ start_pts = pts;
+ start_pts_set = TRUE;
+ }
+
+ frameno = (pts - start_pts)
+ * pts_time_base * frame_rate;
+
+ /* decoding starts *always* on I-Frames,
+ so: P-Frames won't work, even if all the
+ information is in place, when we seek
+ to the I-Frame presented *after* the P-Frame,
+ but located before the P-Frame within
+ the stream */
+
+ if (pts < seek_pos_pts) {
+ s_pos = last_seek_pos;
+ s_dts = last_seek_pos_dts;
+ }
+
+ for (i = 0; i < num_indexers; i++) {
+ if (tcs_in_use & tc_types[i]) {
+ IMB_index_builder_proc_frame(
+ indexer[i],
+ next_packet.data,
+ next_packet.size,
+ frameno, s_pos, s_dts, pts);
+ }
+ }
+ }
+ av_free_packet(&next_packet);
+ }
+
+ for (i = 0; i < num_indexers; i++) {
+ if (tcs_in_use & tc_types[i]) {
+ IMB_index_builder_finish(indexer[i], *stop);
+ }
+ }
+
+ for (i = 0; i < num_proxy_sizes; i++) {
+ if (proxy_sizes_in_use & proxy_sizes[i]) {
+ free_proxy_output_ffmpeg(proxy_ctx[i], *stop);
+ }
+ }
+
+ av_free(in_frame);
+
+ return 1;
+}
+
+#endif
+
+/* ----------------------------------------------------------------------
+ - internal AVI (fallback) rebuilder
+ ---------------------------------------------------------------------- */
+
+static AviMovie * alloc_proxy_output_avi(
+ struct anim * anim, char * filename, int width, int height,
+ int quality)
+{
+ int x, y;
+ AviFormat format;
+ double framerate;
+ AviMovie * avi;
+ short frs_sec = 25; /* it doesn't really matter for proxies,
+ but sane defaults help anyways...*/
+ float frs_sec_base = 1.0;
+
+ IMB_anim_get_fps(anim, &frs_sec, &frs_sec_base);
+
+ x = width;
+ y = height;
+
+ framerate= (double) frs_sec / (double) frs_sec_base;
+
+ avi = MEM_mallocN (sizeof(AviMovie), "avimovie");
+
+ format = AVI_FORMAT_MJPEG;
+
+ if (AVI_open_compress (filename, avi, 1, format) != AVI_ERROR_NONE) {
+ MEM_freeN(avi);
+ return 0;
+ }
+
+ AVI_set_compress_option (avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_WIDTH, &x);
+ AVI_set_compress_option (avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_HEIGHT, &y);
+ AVI_set_compress_option (avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_QUALITY, &quality);
+ AVI_set_compress_option (avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_FRAMERATE, &framerate);
+
+ avi->interlace= 0;
+ avi->odd_fields= 0;
+
+ return avi;
+}
+
+static void index_rebuild_fallback(struct anim * anim,
+ IMB_Timecode_Type tcs_in_use,
+ IMB_Proxy_Size proxy_sizes_in_use,
+ int quality,
+ short *stop, short *do_update,
+ float *progress)
+{
+ int cnt = IMB_anim_get_duration(anim, IMB_TC_NONE);
+ int i, pos;
+ AviMovie * proxy_ctx[IMB_PROXY_MAX_SLOT];
+ char fname[FILE_MAXDIR+FILE_MAXFILE];
+ char fname_tmp[FILE_MAXDIR+FILE_MAXFILE];
+
+ memset(proxy_ctx, 0, sizeof(proxy_ctx));
+
+ /* since timecode indices only work with ffmpeg right now,
+ don't know a sensible fallback here...
+
+ so no proxies, no game to play...
+ */
+ if (proxy_sizes_in_use == IMB_PROXY_NONE) {
+ return;
+ }
+
+ for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
+ if (proxy_sizes_in_use & proxy_sizes[i]) {
+ char fname[FILE_MAXDIR+FILE_MAXFILE];
+
+ get_proxy_filename(anim, proxy_sizes[i], fname, TRUE);
+ BLI_make_existing_file(fname);
+
+ proxy_ctx[i] = alloc_proxy_output_avi(
+ anim, fname,
+ anim->x * proxy_fac[i],
+ anim->y * proxy_fac[i],
+ quality);
+ }
+ }
+
+ for (pos = 0; pos < cnt; pos++) {
+ struct ImBuf * ibuf = IMB_anim_absolute(
+ anim, pos, IMB_TC_NONE, IMB_PROXY_NONE);
+ int next_progress = (int) ((double) pos / (double) cnt);
+
+ if (*progress != next_progress) {
+ *progress = next_progress;
+ *do_update = 1;
+ }
+
+ if (*stop) {
+ break;
+ }
+
+ IMB_flipy(ibuf);
+
+ for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
+ if (proxy_sizes_in_use & proxy_sizes[i]) {
+ int x = anim->x * proxy_fac[i];
+ int y = anim->y * proxy_fac[i];
+
+ struct ImBuf * s_ibuf = IMB_scalefastImBuf(
+ ibuf, x, y);
+
+ IMB_convert_rgba_to_abgr(s_ibuf);
+
+ AVI_write_frame (proxy_ctx[i], pos,
+ AVI_FORMAT_RGB32,
+ s_ibuf->rect, x * y * 4);
+
+ /* note that libavi free's the buffer... */
+ s_ibuf->rect = 0;
+
+ IMB_freeImBuf(s_ibuf);
+ }
+ }
+ }
+
+ for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
+ if (proxy_sizes_in_use & proxy_sizes[i]) {
+ AVI_close_compress (proxy_ctx[i]);
+ MEM_freeN (proxy_ctx[i]);
+
+ get_proxy_filename(anim, proxy_sizes[i],
+ fname_tmp, TRUE);
+ get_proxy_filename(anim, proxy_sizes[i],
+ fname, FALSE);
+
+ if (*stop) {
+ unlink(fname_tmp);
+ } else {
+ rename(fname_tmp, fname);
+ }
+ }
+ }
+}
+
+/* ----------------------------------------------------------------------
+ - public API
+ ---------------------------------------------------------------------- */
+
+void IMB_anim_index_rebuild(struct anim * anim, IMB_Timecode_Type tcs_in_use,
+ IMB_Proxy_Size proxy_sizes_in_use,
+ int quality,
+ short *stop, short *do_update, float *progress)
+{
+ switch (anim->curtype) {
+#ifdef WITH_FFMPEG
+ case ANIM_FFMPEG:
+ index_rebuild_ffmpeg(anim, tcs_in_use, proxy_sizes_in_use,
+ quality, stop, do_update, progress);
+ break;
+#endif
+ default:
+ index_rebuild_fallback(anim, tcs_in_use, proxy_sizes_in_use,
+ quality, stop, do_update, progress);
+ break;
+ }
+}
+
+void IMB_free_indices(struct anim * anim)
+{
+ int i;
+
+ for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
+ if (anim->proxy_anim[i]) {
+ IMB_close_anim(anim->proxy_anim[i]);
+ anim->proxy_anim[i] = 0;
+ }
+ }
+
+ for (i = 0; i < IMB_TC_MAX_SLOT; i++) {
+ if (anim->curr_idx[i]) {
+ IMB_indexer_close(anim->curr_idx[i]);
+ anim->curr_idx[i] = 0;
+ }
+ }
+
+
+ anim->proxies_tried = 0;
+ anim->indices_tried = 0;
+}
+
+void IMB_anim_set_index_dir(struct anim * anim, const char * dir)
+{
+ if (strcmp(anim->index_dir, dir) == 0) {
+ return;
+ }
+ BLI_strncpy(anim->index_dir, dir, sizeof(anim->index_dir));
+
+ IMB_free_indices(anim);
+}
+
+struct anim * IMB_anim_open_proxy(
+ struct anim * anim, IMB_Proxy_Size preview_size)
+{
+ char fname[FILE_MAXDIR+FILE_MAXFILE];
+ int i = IMB_proxy_size_to_array_index(preview_size);
+
+ if (anim->proxy_anim[i]) {
+ return anim->proxy_anim[i];
+ }
+
+ if (anim->proxies_tried & preview_size) {
+ return NULL;
+ }
+
+ get_proxy_filename(anim, preview_size, fname, FALSE);
+
+ anim->proxy_anim[i] = IMB_open_anim(fname, 0, 0);
+
+ anim->proxies_tried |= preview_size;
+
+ return anim->proxy_anim[i];
+}
+
+struct anim_index * IMB_anim_open_index(
+ struct anim * anim, IMB_Timecode_Type tc)
+{
+ char fname[FILE_MAXDIR+FILE_MAXFILE];
+ int i = IMB_timecode_to_array_index(tc);
+
+ if (anim->curr_idx[i]) {
+ return anim->curr_idx[i];
+ }
+
+ if (anim->indices_tried & tc) {
+ return 0;
+ }
+
+ get_tc_filename(anim, tc, fname);
+
+ anim->curr_idx[i] = IMB_indexer_open(fname);
+
+ anim->indices_tried |= tc;
+
+ return anim->curr_idx[i];
+}
+
+int IMB_anim_index_get_frame_index(struct anim * anim, IMB_Timecode_Type tc,
+ int position)
+{
+ struct anim_index * idx = IMB_anim_open_index(anim, tc);
+
+ if (!idx) {
+ return position;
+ }
+
+ return IMB_indexer_get_frame_index(idx, position);
+}
+
diff --git a/source/blender/imbuf/intern/indexer_dv.c b/source/blender/imbuf/intern/indexer_dv.c
new file mode 100644
index 00000000000..0961af10f69
--- /dev/null
+++ b/source/blender/imbuf/intern/indexer_dv.c
@@ -0,0 +1,391 @@
+/*
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Peter Schlaile <peter [at] schlaile [dot] de> 2011
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+*/
+
+#include "IMB_indexer.h"
+#include "MEM_guardedalloc.h"
+#include <time.h>
+
+typedef struct indexer_dv_bitstream {
+ unsigned char* buffer;
+ int bit_pos;
+} indexer_dv_bitstream;
+
+static indexer_dv_bitstream bitstream_new(unsigned char* buffer_)
+{
+ indexer_dv_bitstream rv;
+
+ rv.buffer = buffer_;
+ rv.bit_pos = 0;
+
+ return rv;
+}
+
+static unsigned long bitstream_get_bits(indexer_dv_bitstream * This, int num)
+{
+ int byte_pos = This->bit_pos >> 3;
+ unsigned long i =
+ This->buffer[byte_pos] | (This->buffer[byte_pos + 1] << 8) |
+ (This->buffer[byte_pos + 2] << 16) |
+ (This->buffer[byte_pos + 3] << 24);
+ int rval = (i >> (This->bit_pos & 0x7)) & ((1 << num) - 1);
+ This->bit_pos += num;
+ return rval;
+}
+
+static int parse_num(indexer_dv_bitstream * b, int numbits) {
+ return bitstream_get_bits(b, numbits);
+}
+
+static int parse_bcd(indexer_dv_bitstream * b, int n)
+{
+ char s[256];
+ char * p = s + (n+3)/4;
+
+ *p-- = 0;
+
+ while (n > 4) {
+ char a;
+ int v = bitstream_get_bits(b, 4);
+
+ n -= 4;
+ a = '0' + v;
+
+ if (a > '9') {
+ bitstream_get_bits(b, n);
+ return -1;
+ }
+
+ *p-- = a;
+ }
+ if (n) {
+ char a;
+ int v = bitstream_get_bits(b, n);
+ a = '0' + v;
+ if (a > '9') {
+ return -1;
+ }
+ *p-- = a;
+ }
+
+ return atol(s);
+}
+
+typedef struct indexer_dv_context
+{
+ int rec_curr_frame;
+ int rec_curr_second;
+ int rec_curr_minute;
+ int rec_curr_hour;
+
+ int rec_curr_day;
+ int rec_curr_month;
+ int rec_curr_year;
+
+ char got_record_date;
+ char got_record_time;
+
+ time_t ref_time_read;
+ time_t ref_time_read_new;
+ int curr_frame;
+
+ time_t gap_start;
+ int gap_frame;
+
+ int frameno_offset;
+
+ anim_index_entry backbuffer[31];
+ int fsize;
+
+ anim_index_builder * idx;
+} indexer_dv_context;
+
+static void parse_packet(indexer_dv_context * This, unsigned char * p)
+{
+ indexer_dv_bitstream b;
+ int type = p[0];
+
+ b = bitstream_new(p + 1);
+
+ switch (type) {
+ case 0x62: // Record date
+ parse_num(&b, 8);
+ This->rec_curr_day = parse_bcd(&b, 6);
+ parse_num(&b, 2);
+ This->rec_curr_month = parse_bcd(&b, 5);
+ parse_num(&b, 3);
+ This->rec_curr_year = parse_bcd(&b, 8);
+ if (This->rec_curr_year < 25) {
+ This->rec_curr_year += 2000;
+ } else {
+ This->rec_curr_year += 1900;
+ }
+ This->got_record_date = 1;
+ break;
+ case 0x63: // Record time
+ This->rec_curr_frame = parse_bcd(&b, 6);
+ parse_num(&b, 2);
+ This->rec_curr_second = parse_bcd(&b, 7);
+ parse_num(&b, 1);
+ This->rec_curr_minute = parse_bcd(&b, 7);
+ parse_num(&b, 1);
+ This->rec_curr_hour = parse_bcd(&b, 6);
+ This->got_record_time = 1;
+ break;
+ }
+}
+
+static void parse_header_block(indexer_dv_context * This, unsigned char* target)
+{
+ int i;
+ for (i = 3; i < 80; i += 5) {
+ if (target[i] != 0xff) {
+ parse_packet(This, target + i);
+ }
+ }
+}
+
+static void parse_subcode_blocks(
+ indexer_dv_context * This, unsigned char* target)
+{
+ int i,j;
+
+ for (j = 0; j < 2; j++) {
+ for (i = 3; i < 80; i += 5) {
+ if (target[i] != 0xff) {
+ parse_packet(This, target + i);
+ }
+ }
+ }
+}
+
+static void parse_vaux_blocks(
+ indexer_dv_context * This, unsigned char* target)
+{
+ int i,j;
+
+ for (j = 0; j < 3; j++) {
+ for (i = 3; i < 80; i += 5) {
+ if (target[i] != 0xff) {
+ parse_packet(This, target + i);
+ }
+ }
+ target += 80;
+ }
+}
+
+static void parse_audio_headers(
+ indexer_dv_context * This, unsigned char* target)
+{
+ int i;
+
+ for(i = 0; i < 9; i++) {
+ if (target[3] != 0xff) {
+ parse_packet(This, target + 3);
+ }
+ target += 16 * 80;
+ }
+}
+
+static void parse_frame(indexer_dv_context * This,
+ unsigned char * framebuffer, int isPAL)
+{
+ int numDIFseq = isPAL ? 12 : 10;
+ unsigned char* target = framebuffer;
+ int ds;
+
+ for (ds = 0; ds < numDIFseq; ds++) {
+ parse_header_block(This, target);
+ target += 1 * 80;
+ parse_subcode_blocks(This, target);
+ target += 2 * 80;
+ parse_vaux_blocks(This, target);
+ target += 3 * 80;
+ parse_audio_headers(This, target);
+ target += 144 * 80;
+ }
+}
+
+static void inc_frame(int * frame, time_t * t, int isPAL)
+{
+ if ((isPAL && *frame >= 25) || (!isPAL && *frame >= 30)) {
+ fprintf(stderr, "Ouchie: inc_frame: invalid_frameno: %d\n",
+ *frame);
+ }
+ (*frame)++;
+ if (isPAL && *frame >= 25) {
+ (*t)++;
+ *frame = 0;
+ } else if (!isPAL && *frame >= 30) {
+ (*t)++;
+ *frame = 0;
+ }
+}
+
+static void write_index(indexer_dv_context * This, anim_index_entry * entry)
+{
+ IMB_index_builder_add_entry(
+ This->idx, entry->frameno + This->frameno_offset,
+ entry->seek_pos, entry->seek_pos_dts, entry->pts);
+}
+
+static void fill_gap(indexer_dv_context * This, int isPAL)
+{
+ int i;
+
+ for (i = 0; i < This->fsize; i++) {
+ if (This->gap_start == This->ref_time_read &&
+ This->gap_frame == This->curr_frame) {
+ fprintf(stderr,
+ "indexer_dv::fill_gap: "
+ "can't seek backwards !\n");
+ break;
+ }
+ inc_frame(&This->gap_frame, &This->gap_start, isPAL);
+ }
+
+ while (This->gap_start != This->ref_time_read ||
+ This->gap_frame != This->curr_frame) {
+ inc_frame(&This->gap_frame, &This->gap_start, isPAL);
+ This->frameno_offset++;
+ }
+
+ for (i = 0; i < This->fsize; i++) {
+ write_index(This, This->backbuffer + i);
+ }
+ This->fsize = 0;
+}
+
+static void proc_frame(indexer_dv_context * This,
+ unsigned char* framebuffer, int isPAL)
+{
+ struct tm recDate;
+ time_t t;
+
+ if (!This->got_record_date || !This->got_record_time) {
+ return;
+ }
+
+ recDate.tm_sec = This->rec_curr_second;
+ recDate.tm_min = This->rec_curr_minute;
+ recDate.tm_hour = This->rec_curr_hour;
+ recDate.tm_mday = This->rec_curr_day;
+ recDate.tm_mon = This->rec_curr_month - 1;
+ recDate.tm_year = This->rec_curr_year - 1900;
+ recDate.tm_wday = -1;
+ recDate.tm_yday = -1;
+ recDate.tm_isdst = -1;
+
+ t = mktime(&recDate);
+ if (t == -1) {
+ return;
+ }
+
+ This->ref_time_read_new = t;
+
+ if (This->ref_time_read < 0) {
+ This->ref_time_read = This->ref_time_read_new;
+ This->curr_frame = 0;
+ } else {
+ if (This->ref_time_read_new - This->ref_time_read == 1) {
+ This->curr_frame = 0;
+ This->ref_time_read = This->ref_time_read_new;
+ if (This->gap_frame >= 0) {
+ fill_gap(This, isPAL);
+ This->gap_frame = -1;
+ }
+ } else if (This->ref_time_read_new == This->ref_time_read) {
+ // do nothing
+ } else {
+ This->gap_start = This->ref_time_read;
+ This->gap_frame = This->curr_frame;
+ This->ref_time_read = This->ref_time_read_new;
+ This->curr_frame = -1;
+ }
+ }
+}
+
+static void indexer_dv_proc_frame(anim_index_builder * idx,
+ unsigned char * buffer,
+ int data_size,
+ struct anim_index_entry * entry)
+{
+ int isPAL;
+
+ indexer_dv_context * This = (indexer_dv_context *) idx->private_data;
+
+ isPAL = (buffer[3] & 0x80);
+
+ This->got_record_date = FALSE;
+ This->got_record_time = FALSE;
+
+ parse_frame(This, buffer, isPAL);
+ proc_frame(This, buffer, isPAL);
+
+ if (This->curr_frame >= 0) {
+ write_index(This, entry);
+ inc_frame(&This->curr_frame, &This->ref_time_read, isPAL);
+ } else {
+ This->backbuffer[This->fsize++] = *entry;
+ if (This->fsize >= 31) {
+ int i;
+
+ fprintf(stderr, "indexer_dv::indexer_dv_proc_frame: "
+ "backbuffer overrun, emergency flush");
+
+ for (i = 0; i < This->fsize; i++) {
+ write_index(This, This->backbuffer+i);
+ }
+ This->fsize = 0;
+ }
+ }
+}
+
+static void indexer_dv_delete(anim_index_builder * idx)
+{
+ int i = 0;
+ indexer_dv_context * This = (indexer_dv_context *) idx->private_data;
+
+ for (i = 0; i < This->fsize; i++) {
+ write_index(This, This->backbuffer+i);
+ }
+
+ MEM_freeN(This);
+}
+
+void IMB_indexer_dv_new(anim_index_builder * idx)
+{
+ indexer_dv_context * rv = MEM_callocN(
+ sizeof(indexer_dv_context), "index_dv builder context");
+
+ rv->ref_time_read = -1;
+ rv->curr_frame = -1;
+ rv->gap_frame = -1;
+ rv->idx = idx;
+
+ idx->private_data = rv;
+ idx->proc_frame = indexer_dv_proc_frame;
+ idx->delete_priv_data = indexer_dv_delete;
+}
diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c
index 1d91f34f4fa..2ab7e55d1f8 100644
--- a/source/blender/imbuf/intern/thumbs.c
+++ b/source/blender/imbuf/intern/thumbs.c
@@ -317,9 +317,9 @@ ImBuf* IMB_thumb_create(const char* path, ThumbSize size, ThumbSource source, Im
}
} else if (THB_SOURCE_MOVIE == source) {
struct anim * anim = NULL;
- anim = IMB_open_anim(path, IB_rect | IB_metadata);
+ anim = IMB_open_anim(path, IB_rect | IB_metadata, 0);
if (anim != NULL) {
- img = IMB_anim_absolute(anim, 0);
+ img = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
if (img == NULL) {
printf("not an anim; %s\n", path);
} else {
diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c
index 6db8dcc06cf..85d31f18a03 100644
--- a/source/blender/imbuf/intern/util.c
+++ b/source/blender/imbuf/intern/util.c
@@ -221,7 +221,7 @@ void silence_log_ffmpeg(int quiet)
}
else
{
- av_log_set_level(AV_LOG_INFO);
+ av_log_set_level(AV_LOG_DEBUG);
}
}
@@ -234,9 +234,10 @@ void do_init_ffmpeg(void)
av_register_all();
avdevice_register_all();
- if ((G.f & G_DEBUG) == 0)
- {
+ if ((G.f & G_DEBUG) == 0) {
silence_log_ffmpeg(1);
+ } else {
+ silence_log_ffmpeg(0);
}
}
}
diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h
index 0dd0b9790ab..cd3afbf3553 100644
--- a/source/blender/makesdna/DNA_sequence_types.h
+++ b/source/blender/makesdna/DNA_sequence_types.h
@@ -71,12 +71,19 @@ typedef struct StripColorBalance {
} StripColorBalance;
typedef struct StripProxy {
- char dir[160];
- char file[80];
- struct anim *anim;
- short size;
- short quality;
- int pad;
+ char dir[160]; // custom directory for index and proxy files
+ // (defaults to BL_proxy)
+
+ char file[80]; // custom file
+ struct anim *anim; // custom proxy anim file
+
+ short tc; // time code in use
+
+ short quality; // proxy build quality
+ short build_size_flags;// size flags (see below) of all proxies
+ // to build
+ short build_tc_flags; // time code flags (see below) of all tc indices
+ // to build
} StripProxy;
typedef struct Strip {
@@ -128,11 +135,12 @@ typedef struct Sequence {
int startstill, endstill;
int machine, depth; /*machine - the strip channel, depth - the depth in the sequence when dealing with metastrips */
int startdisp, enddisp; /*starting and ending points in the sequence*/
- float sat, pad;
+ float sat;
float mul, handsize;
/* is sfra needed anymore? - it looks like its only used in one place */
int sfra; /* starting frame according to the timeline of the scene. */
int anim_preseek;
+ int streamindex; /* streamindex for movie or sound files with several streams */
Strip *strip;
@@ -283,6 +291,19 @@ typedef struct SpeedControlVars {
#define SEQ_COLOR_BALANCE_INVERSE_GAMMA 2
#define SEQ_COLOR_BALANCE_INVERSE_LIFT 4
+/* !!! has to be same as IMB_imbuf.h IMB_PROXY_... and IMB_TC_... */
+
+#define SEQ_PROXY_IMAGE_SIZE_25 1
+#define SEQ_PROXY_IMAGE_SIZE_50 2
+#define SEQ_PROXY_IMAGE_SIZE_75 4
+#define SEQ_PROXY_IMAGE_SIZE_100 8
+
+#define SEQ_PROXY_TC_NONE 0
+#define SEQ_PROXY_TC_RECORD_RUN 1
+#define SEQ_PROXY_TC_FREE_RUN 2
+#define SEQ_PROXY_TC_INTERP_REC_DATE_FREE_RUN 4
+#define SEQ_PROXY_TC_ALL 7
+
/* seq->type WATCH IT: SEQ_EFFECT BIT is used to determine if this is an effect strip!!! */
#define SEQ_IMAGE 0
#define SEQ_META 1
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index 67899db5538..66b10bcbf21 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -930,6 +930,7 @@ enum {
#define SEQ_PROXY_RENDER_SIZE_25 25
#define SEQ_PROXY_RENDER_SIZE_50 50
#define SEQ_PROXY_RENDER_SIZE_75 75
+#define SEQ_PROXY_RENDER_SIZE_100 99
#define SEQ_PROXY_RENDER_SIZE_FULL 100
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index 476ac325848..b6e2117956d 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -245,6 +245,10 @@ static void rna_Sequence_use_proxy_set(PointerRNA *ptr, int value)
seq->flag |= SEQ_USE_PROXY;
if(seq->strip->proxy == NULL) {
seq->strip->proxy = MEM_callocN(sizeof(struct StripProxy), "StripProxy");
+ seq->strip->proxy->quality = 90;
+ seq->strip->proxy->build_tc_flags = SEQ_PROXY_TC_ALL;
+ seq->strip->proxy->build_size_flags
+ = SEQ_PROXY_IMAGE_SIZE_25;
}
} else {
seq->flag ^= SEQ_USE_PROXY;
@@ -587,6 +591,34 @@ static void rna_Sequence_filepath_update(Main *bmain, Scene *scene, PointerRNA *
rna_Sequence_update(bmain, scene, ptr);
}
+static int seqproxy_seq_cmp_cb(Sequence *seq, void *arg_pt)
+{
+ struct { Sequence *seq; void *seq_proxy; } *data= arg_pt;
+
+ if(seq->strip && seq->strip->proxy == data->seq_proxy) {
+ data->seq= seq;
+ return -1; /* done so bail out */
+ }
+ return 1;
+}
+
+static void rna_Sequence_tcindex_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ Editing *ed= seq_give_editing(scene, FALSE);
+ Sequence *seq;
+
+ struct { Sequence *seq; void *seq_proxy; } data;
+
+ data.seq= NULL;
+ data.seq_proxy = ptr->data;
+
+ seqbase_recursive_apply(&ed->seqbase, seqproxy_seq_cmp_cb, &data);
+ seq= data.seq;
+
+ reload_sequence_new_file(scene, seq, FALSE);
+ rna_Sequence_frame_change_update(scene, seq);
+}
+
/* do_versions? */
static float rna_Sequence_opacity_get(PointerRNA *ptr)
{
@@ -789,6 +821,19 @@ static void rna_def_strip_proxy(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
+
+ static const EnumPropertyItem seq_tc_items[]= {
+ {SEQ_PROXY_TC_NONE, "NONE", 0, "No TC in use", ""},
+ {SEQ_PROXY_TC_RECORD_RUN, "RECORD_RUN", 0, "Record Run",
+ "use images in the order as they are recorded"},
+ {SEQ_PROXY_TC_FREE_RUN, "FREE_RUN", 0, "Free Run",
+ "use global timestamp written by recording device"},
+ {SEQ_PROXY_TC_INTERP_REC_DATE_FREE_RUN, "FREE_RUN_REC_DATE",
+ 0, "Free Run (rec date)",
+ "interpolate a global timestamp using the "
+ "record date and time written by recording "
+ "device"},
+ {0, NULL, 0, NULL, NULL}};
srna = RNA_def_struct(brna, "SequenceProxy", NULL);
RNA_def_struct_ui_text(srna, "Sequence Proxy", "Proxy parameters for a sequence strip");
@@ -804,6 +849,46 @@ static void rna_def_strip_proxy(BlenderRNA *brna)
RNA_def_property_string_funcs(prop, "rna_Sequence_proxy_filepath_get", "rna_Sequence_proxy_filepath_length", "rna_Sequence_proxy_filepath_set");
RNA_def_property_update(prop, NC_SCENE|ND_SEQUENCER, "rna_Sequence_update");
+
+ prop= RNA_def_property(srna, "build_25", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "build_size_flags", SEQ_PROXY_IMAGE_SIZE_25);
+ RNA_def_property_ui_text(prop, "25%", "Build 25% proxy resolution");
+
+ prop= RNA_def_property(srna, "build_50", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "build_size_flags", SEQ_PROXY_IMAGE_SIZE_50);
+ RNA_def_property_ui_text(prop, "50%", "Build 50% proxy resolution");
+
+ prop= RNA_def_property(srna, "build_75", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "build_size_flags", SEQ_PROXY_IMAGE_SIZE_75);
+ RNA_def_property_ui_text(prop, "75%", "Build 75% proxy resolution");
+
+ prop= RNA_def_property(srna, "build_100", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "build_size_flags", SEQ_PROXY_IMAGE_SIZE_100);
+ RNA_def_property_ui_text(prop, "100%", "Build 100% proxy resolution");
+
+ prop= RNA_def_property(srna, "build_record_run", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "build_tc_flags", SEQ_PROXY_TC_RECORD_RUN);
+ RNA_def_property_ui_text(prop, "Rec Run", "Build record run time code index");
+
+ prop= RNA_def_property(srna, "build_free_run", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "build_tc_flags", SEQ_PROXY_TC_FREE_RUN);
+ RNA_def_property_ui_text(prop, "Free Run", "Build free run time code index");
+
+ prop= RNA_def_property(srna, "build_free_run_rec_date", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "build_tc_flags", SEQ_PROXY_TC_INTERP_REC_DATE_FREE_RUN);
+ RNA_def_property_ui_text(prop, "Free Run (Rec Date)", "Build free run time code index using Record Date/Time");
+
+ prop= RNA_def_property(srna, "quality", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_sdna(prop, NULL, "quality");
+ RNA_def_property_ui_text(prop, "Quality", "JPEG Quality of proxies to build");
+ RNA_def_property_ui_range(prop, 1, 100, 1, 0);
+
+ prop= RNA_def_property(srna, "timecode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "tc");
+ RNA_def_property_enum_items(prop, seq_tc_items);
+ RNA_def_property_ui_text(prop, "Timecode", "");
+ RNA_def_property_update(prop, NC_SCENE|ND_SEQUENCER, "rna_Sequence_tcindex_update");
+
}
static void rna_def_strip_color_balance(BlenderRNA *brna)
@@ -1208,7 +1293,7 @@ static void rna_def_proxy(StructRNA *srna)
prop= RNA_def_property(srna, "use_proxy", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_PROXY);
- RNA_def_property_ui_text(prop, "Use Proxy", "Use a preview proxy for this strip");
+ RNA_def_property_ui_text(prop, "Use Proxy / Timecode", "Use a preview proxy and/or timecode index for this strip");
RNA_def_property_boolean_funcs(prop, NULL, "rna_Sequence_use_proxy_set");
prop= RNA_def_property(srna, "proxy", PROP_POINTER, PROP_NONE);
@@ -1330,6 +1415,12 @@ static void rna_def_movie(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "MPEG Preseek", "For MPEG movies, preseek this many frames");
RNA_def_property_update(prop, NC_SCENE|ND_SEQUENCER, "rna_Sequence_update");
+ prop= RNA_def_property(srna, "streamindex", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "streamindex");
+ RNA_def_property_range(prop, 0, 20);
+ RNA_def_property_ui_text(prop, "Streamindex", "For files with several movie streams, use the stream with the given index");
+ RNA_def_property_update(prop, NC_SCENE|ND_SEQUENCER, "rna_Sequence_update_reopen_files");
+
prop= RNA_def_property(srna, "elements", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "strip->stripdata", NULL);
RNA_def_property_struct_type(prop, "SequenceElement");
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index b79d5395eec..0166baa8443 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -1674,6 +1674,7 @@ static void rna_def_space_sequencer(BlenderRNA *brna)
{SEQ_PROXY_RENDER_SIZE_25, "PROXY_25", 0, "Proxy size 25%", ""},
{SEQ_PROXY_RENDER_SIZE_50, "PROXY_50", 0, "Proxy size 50%", ""},
{SEQ_PROXY_RENDER_SIZE_75, "PROXY_75", 0, "Proxy size 75%", ""},
+ {SEQ_PROXY_RENDER_SIZE_100, "PROXY_100", 0, "Proxy size 100%", ""},
{SEQ_PROXY_RENDER_SIZE_FULL, "FULL", 0, "No proxy, full render", ""},
{0, NULL, 0, NULL, NULL}};
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index e1b8cefca4b..5bdf1ec2787 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -300,6 +300,8 @@ int WM_jobs_test(struct wmWindowManager *wm, void *owner);
float WM_jobs_progress(struct wmWindowManager *wm, void *owner);
char *WM_jobs_name(struct wmWindowManager *wm, void *owner);
+int WM_jobs_is_running(struct wmJob *);
+void* WM_jobs_get_customdata(struct wmJob *);
void WM_jobs_customdata(struct wmJob *, void *customdata, void (*free)(void *));
void WM_jobs_timer(struct wmJob *, double timestep, unsigned int note, unsigned int endnote);
void WM_jobs_callbacks(struct wmJob *,
diff --git a/source/blender/windowmanager/intern/wm_jobs.c b/source/blender/windowmanager/intern/wm_jobs.c
index 4ab4eebdfe1..f4e0b4ef06c 100644
--- a/source/blender/windowmanager/intern/wm_jobs.c
+++ b/source/blender/windowmanager/intern/wm_jobs.c
@@ -202,6 +202,20 @@ char *WM_jobs_name(wmWindowManager *wm, void *owner)
return NULL;
}
+int WM_jobs_is_running(wmJob *steve)
+{
+ return steve->running;
+}
+
+void* WM_jobs_get_customdata(wmJob * steve)
+{
+ if (!steve->customdata) {
+ return steve->run_customdata;
+ } else {
+ return steve->customdata;
+ }
+}
+
void WM_jobs_customdata(wmJob *steve, void *customdata, void (*free)(void *))
{
/* pending job? just free */