Camera tracking integration
authorSergey Sharybin <sergey.vfx@gmail.com>
Mon, 8 Aug 2011 12:18:20 +0000 (12:18 +0000)
committerSergey Sharybin <sergey.vfx@gmail.com>
Mon, 8 Aug 2011 12:18:20 +0000 (12:18 +0000)
===========================

Commiting VSE-proxies patch from Peter Schlaile.

Description is here: http://lists.blender.org/pipermail/bf-committers/2011-May/032152.html
Git commit hash: baaecf244b83c70cb57e1bf6fdd51dd8591bde5b

33 files changed:
CMakeLists.txt
build_files/scons/config/darwin-config.py
build_files/scons/config/linuxcross-config.py
build_files/scons/config/win32-mingw-config.py
build_files/scons/config/win32-vc-config.py
build_files/scons/config/win64-vc-config.py
intern/ffmpeg/ffmpeg_compat.h
release/scripts/startup/bl_ui/space_sequencer.py
source/blender/blenkernel/BKE_sequencer.h
source/blender/blenkernel/intern/image.c
source/blender/blenkernel/intern/movieclip.c
source/blender/blenkernel/intern/sequencer.c
source/blender/blenloader/intern/readfile.c
source/blender/editors/interface/interface_templates.c
source/blender/editors/space_image/image_buttons.c
source/blender/editors/space_sequencer/sequencer_edit.c
source/blender/editors/space_sequencer/sequencer_intern.h
source/blender/editors/space_sequencer/sequencer_ops.c
source/blender/imbuf/CMakeLists.txt
source/blender/imbuf/IMB_imbuf.h
source/blender/imbuf/intern/IMB_anim.h
source/blender/imbuf/intern/IMB_indexer.h [new file with mode: 0644]
source/blender/imbuf/intern/allocimbuf.c
source/blender/imbuf/intern/anim_movie.c
source/blender/imbuf/intern/indexer.c [new file with mode: 0644]
source/blender/imbuf/intern/indexer_dv.c [new file with mode: 0644]
source/blender/imbuf/intern/thumbs.c
source/blender/imbuf/intern/util.c
source/blender/makesdna/DNA_sequence_types.h
source/blender/makesrna/intern/rna_sequencer.c
source/blender/windowmanager/WM_api.h
source/blender/windowmanager/intern/wm_jobs.c
source/creator/CMakeLists.txt

index 641e798b0ef00fde66531f239efb210d58336a95..5c2e45a44ed1981bc3a2356ac095b04de3807bc2 100644 (file)
@@ -694,11 +694,11 @@ elseif(WIN32)
                                ${LIBDIR}/ffmpeg/include/msvc
                        )
                        set(FFMPEG_LIBRARIES
-                               ${LIBDIR}/ffmpeg/lib/avcodec-52.lib
-                               ${LIBDIR}/ffmpeg/lib/avformat-52.lib
-                               ${LIBDIR}/ffmpeg/lib/avdevice-52.lib
-                               ${LIBDIR}/ffmpeg/lib/avutil-50.lib
-                               ${LIBDIR}/ffmpeg/lib/swscale-0.lib
+                               ${LIBDIR}/ffmpeg/lib/avcodec-53.lib
+                               ${LIBDIR}/ffmpeg/lib/avformat-53.lib
+                               ${LIBDIR}/ffmpeg/lib/avdevice-53.lib
+                               ${LIBDIR}/ffmpeg/lib/avutil-51.lib
+                               ${LIBDIR}/ffmpeg/lib/swscale-2.lib
                        )
                endif()
 
@@ -825,7 +825,7 @@ elseif(WIN32)
                if(WITH_CODEC_FFMPEG)
                        set(FFMPEG ${LIBDIR}/ffmpeg)
                        set(FFMPEG_INCLUDE_DIRS ${FFMPEG}/include ${FFMPEG}/include)
-                       set(FFMPEG_LIBRARIES avcodec-52 avformat-52 avdevice-52 avutil-50 swscale-0)
+                       set(FFMPEG_LIBRARIES avcodec-53 avformat-53 avdevice-53 avutil-51 swscale-2)
                        set(FFMPEG_LIBPATH ${FFMPEG}/lib)
                endif()
 
index 29695896921d10bec48aa322bd1723ed963fc2c4..cd9c75237d7460cb350812da98d144ed2fdc392e 100644 (file)
@@ -98,7 +98,7 @@ else:
 
 # enable ffmpeg  support
 WITH_BF_FFMPEG = True  # -DWITH_FFMPEG
-BF_FFMPEG = LIBDIR + '/ffmpeg'
+BF_FFMPEG = LIBDIR + '/ffmpeg-9.8'
 BF_FFMPEG_INC = "${BF_FFMPEG}/include"
 BF_FFMPEG_LIBPATH='${BF_FFMPEG}/lib'
 BF_FFMPEG_LIB = 'avcodec avdevice avformat avutil mp3lame swscale x264 xvidcore theora theoradec theoraenc vorbis vorbisenc vorbisfile ogg bz2'
index 62474527825b40f7493b587455c8b48792da3274..98100e3d5817b1e65a511427e1aa06968ca40bd9 100644 (file)
@@ -125,8 +125,9 @@ WITH_BF_BINRELOC = False
 
 # enable ffmpeg  support
 WITH_BF_FFMPEG = True  # -DWITH_FFMPEG
-BF_FFMPEG = LIBDIR + '/ffmpeg'
-BF_FFMPEG_LIB = 'avformat-52 avcodec-52 avdevice-52 avutil-50 swscale-0'
+BF_FFMPEG = LIBDIR + '/ffmpeg-0.8'
+BF_FFMPEG_LIB = 'avformat-53 avcodec-53 avdevice-53 avutil-51 swscale-2'
+BF_FFMPEG_DLL = '${BF_FFMPEG_LIBPATH}/avformat-53.dll ${BF_FFMPEG_LIBPATH}/avcodec-53.dll ${BF_FFMPEG_LIBPATH}/avdevice-53.dll ${BF_FFMPEG_LIBPATH}/avutil-51.dll ${BF_FFMPEG_LIBPATH}/swscale-2.dll'
 BF_FFMPEG_INC = '${BF_FFMPEG}/include'
 BF_FFMPEG_LIBPATH = '${BF_FFMPEG}/lib'
 
index 6dac29b37f719c832a865bc71c2c6c8e8367bb1d..6134d3c11c45ba25830553308c9774ec8e0d595d 100644 (file)
@@ -18,9 +18,11 @@ BF_OPENAL_LIB = 'wrap_oal'
 BF_OPENAL_LIBPATH = '${BF_OPENAL}/lib'
 
 WITH_BF_FFMPEG = False
-BF_FFMPEG_LIB = 'avformat-52 avcodec-52 avdevice-52 avutil-50 swscale-0'
-BF_FFMPEG_LIBPATH = LIBDIR + '/ffmpeg/lib'
-BF_FFMPEG_INC =  LIBDIR + '/ffmpeg/include'
+BF_FFMPEG_LIB = 'avformat-53 avcodec-53 avdevice-53 avutil-51 swscale-2'
+BF_FFMPEG_LIBPATH = LIBDIR + '/ffmpeg-0.8/lib'
+BF_FFMPEG_INC =  LIBDIR + '/ffmpeg-0.8/include'
+BF_FFMPEG_DLL = '${BF_FFMPEG_LIBPATH}/avformat-53.dll ${BF_FFMPEG_LIBPATH}/avcodec-53.dll ${BF_FFMPEG_LIBPATH}/avdevice-53.dll ${BF_FFMPEG_LIBPATH}/avutil-51.dll ${BF_FFMPEG_LIBPATH}/swscale-2.dll'
+
 
 BF_LIBSAMPLERATE = LIBDIR + '/samplerate'
 BF_LIBSAMPLERATE_INC = '${BF_LIBSAMPLERATE}/include'
index 4baada7f9bf9814c23366a2b4255c9af8ef87596..beee028116560cbaa335868e88c355727c3e6ab0 100644 (file)
@@ -3,11 +3,11 @@ LIBDIR = '${LCGDIR}'
 
 # enable ffmpeg  support
 WITH_BF_FFMPEG = True  # -DWITH_FFMPEG
-BF_FFMPEG = LIBDIR +'/ffmpeg'
+BF_FFMPEG = LIBDIR +'/ffmpeg-0.8'
 BF_FFMPEG_INC = '${BF_FFMPEG}/include ${BF_FFMPEG}/include/msvc'
 BF_FFMPEG_LIBPATH='${BF_FFMPEG}/lib'
-BF_FFMPEG_LIB = 'avformat-52.lib avcodec-52.lib avdevice-52.lib avutil-50.lib swscale-0.lib'
-BF_FFMPEG_DLL = '${BF_FFMPEG_LIBPATH}/avformat-52.dll ${BF_FFMPEG_LIBPATH}/avcodec-52.dll ${BF_FFMPEG_LIBPATH}/avdevice-52.dll ${BF_FFMPEG_LIBPATH}/avutil-50.dll ${BF_FFMPEG_LIBPATH}/swscale-0.dll'
+BF_FFMPEG_LIB = 'avformat-53.lib avcodec-53.lib avdevice-53.lib avutil-51.lib swscale-2.lib'
+BF_FFMPEG_DLL = '${BF_FFMPEG_LIBPATH}/avformat-53.dll ${BF_FFMPEG_LIBPATH}/avcodec-53.dll ${BF_FFMPEG_LIBPATH}/avdevice-53.dll ${BF_FFMPEG_LIBPATH}/avutil-51.dll ${BF_FFMPEG_LIBPATH}/swscale-2.dll'
 
 BF_PYTHON = LIBDIR + '/python'
 BF_PYTHON_VERSION = '3.2'
index db7c8d09af829b62dcf7be8770999608c33fea95..e3eb560628933773d05e98834c5046b6862e2f00 100644 (file)
@@ -3,11 +3,11 @@ LIBDIR = '${LCGDIR}'
 
 # enable ffmpeg  support
 WITH_BF_FFMPEG = True # -DWITH_FFMPEG
-BF_FFMPEG = LIBDIR +'/ffmpeg'
+BF_FFMPEG = LIBDIR +'/ffmpeg-0.8'
 BF_FFMPEG_INC = '${BF_FFMPEG}/include ${BF_FFMPEG}/include/msvc '
 BF_FFMPEG_LIBPATH='${BF_FFMPEG}/lib'
-BF_FFMPEG_LIB = 'avformat-52.lib avcodec-52.lib avdevice-52.lib avutil-50.lib swscale-0.lib'
-BF_FFMPEG_DLL = '${BF_FFMPEG_LIBPATH}/avformat-52.dll ${BF_FFMPEG_LIBPATH}/avcodec-52.dll ${BF_FFMPEG_LIBPATH}/avdevice-52.dll ${BF_FFMPEG_LIBPATH}/avutil-50.dll ${BF_FFMPEG_LIBPATH}/swscale-0.dll'
+BF_FFMPEG_LIB = 'avformat-53.lib avcodec-53.lib avdevice-53.lib avutil-51.lib swscale-2.lib'
+BF_FFMPEG_DLL = '${BF_FFMPEG_LIBPATH}/avformat-53.dll ${BF_FFMPEG_LIBPATH}/avcodec-53.dll ${BF_FFMPEG_LIBPATH}/avdevice-53.dll ${BF_FFMPEG_LIBPATH}/avutil-51.dll ${BF_FFMPEG_LIBPATH}/swscale-2.dll'
 
 BF_PYTHON = LIBDIR + '/python'
 BF_PYTHON_VERSION = '3.2'
index fae8590568d78c7e4dcdaaaa4f421925e5fe54f6..d8172902a4caa89d7c07248a47d97dcd19a75195 100644 (file)
@@ -71,6 +71,7 @@
 #define avio_open url_fopen
 #define avio_tell url_ftell
 #define avio_close url_fclose
+#define avio_size url_fsize
 #endif
 
 /* there are some version inbetween, which have avio_... functions but no
@@ -130,4 +131,19 @@ int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
 }
 #endif
 
+static inline
+int64_t av_get_pts_from_frame(AVFormatContext *avctx, AVFrame * picture)
+{
+       int64_t pts = picture->pkt_pts;
+
+       if (pts == AV_NOPTS_VALUE) {
+               pts = picture->pkt_dts;
+       }
+       if (pts == AV_NOPTS_VALUE) {
+               pts = 0;
+       }
+       
+       return pts;
+}
+
 #endif
index c477a2ff62baf15470c88e529d57644b8eb872dc..695e520b030168356281eab938c1f58611cced41 100644 (file)
@@ -60,6 +60,7 @@ class SEQUENCER_HT_header(bpy.types.Header):
 
             layout.separator()
             layout.operator("sequencer.refresh_all")
+            layout.template_running_jobs()
         elif st.view_type == 'SEQUENCER_PREVIEW':
             layout.separator()
             layout.operator("sequencer.refresh_all")
@@ -234,6 +235,7 @@ class SEQUENCER_MT_strip(bpy.types.Menu):
         layout.operator("sequencer.cut", text="Cut (soft) at frame").type = 'SOFT'
         layout.operator("sequencer.images_separate")
         layout.operator("sequencer.deinterlace_selected_movies")
+        layout.operator("sequencer.rebuild_proxy")
         layout.separator()
 
         layout.operator("sequencer.duplicate")
@@ -733,7 +735,7 @@ class SEQUENCER_PT_filter(SequencerButtonsPanel, bpy.types.Panel):
 
 
 class SEQUENCER_PT_proxy(SequencerButtonsPanel, bpy.types.Panel):
-    bl_label = "Proxy"
+    bl_label = "Proxy / Timecode"
 
     @classmethod
     def poll(cls, context):
@@ -759,12 +761,29 @@ class SEQUENCER_PT_proxy(SequencerButtonsPanel, bpy.types.Panel):
         flow = layout.column_flow()
         flow.prop(strip, "use_proxy_custom_directory")
         flow.prop(strip, "use_proxy_custom_file")
-        if strip.proxy:  # TODO - need to add this somehow
+        if strip.proxy:
             if strip.use_proxy_custom_directory and not strip.use_proxy_custom_file:
                 flow.prop(strip.proxy, "directory")
             if strip.use_proxy_custom_file:
                 flow.prop(strip.proxy, "filepath")
 
+            row = layout.row()
+            row.label(text="Build Sizes:")
+            row.prop(strip.proxy, "build_25")
+            row.prop(strip.proxy, "build_50")
+            row.prop(strip.proxy, "build_75")
+
+            col = layout.column()
+            col.label(text="Build JPEG quality")
+            col.prop(strip.proxy, "quality")
+
+            if strip.type == "MOVIE":
+                col = layout.column()
+                col.label(text="Use timecode index:")
+
+                col.prop(strip.proxy, "timecode")
+
+
 
 class SEQUENCER_PT_preview(SequencerButtonsPanel_Output, bpy.types.Panel):
     bl_label = "Scene Preview/Render"
index 604fd27418520c481ee13ef712540efb5612ddfb..bf33d6f61f1b697897eaaeda551a4497db11a7e5 100644 (file)
@@ -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
 
index ab67d7e3f250fa2a4df6414bd7c4c4e617d8768a..0bf67c846bdf8346092e0eefa1c1a4c02afb48b4 100644 (file)
@@ -1379,7 +1379,7 @@ struct anim *openanim(char *name, int flags)
        anim = IMB_open_anim(name, flags);
        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);
@@ -1777,16 +1777,21 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame)
                
                /* 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);
index 3abc84b6cbb92570bc45989d04a55de0ed3d157b..0da7baea12777cef4be469644f57540e2c61f732 100644 (file)
@@ -134,7 +134,7 @@ static ImBuf *movieclip_load_movie_file(MovieClip *clip, int framenr)
        }
 
        if(clip->anim) {
-               int dur= IMB_anim_get_duration(clip->anim);
+               int dur= IMB_anim_get_duration(clip->anim, IMB_TC_NONE);
                int fra= framenr-1;
 
                if(fra<0)
@@ -143,7 +143,7 @@ static ImBuf *movieclip_load_movie_file(MovieClip *clip, int framenr)
                if(fra>(dur-1))
                        fra= dur-1;
 
-               ibuf= IMB_anim_absolute(clip->anim, fra);
+               ibuf= IMB_anim_absolute(clip->anim, fra, IMB_TC_NONE, IMB_PROXY_NONE);
        }
 
        return ibuf;
index d6a152a52809cb308ea9a59cf6836bbefd23cc73..bc11702ee55050b5f32b00b2a64e2ecd03f7fb2d 100644 (file)
@@ -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) {
@@ -689,7 +701,10 @@ void reload_sequence_new_file(Scene *scene, Sequence * seq, int lock_range)
                        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);
 
@@ -1116,7 +1131,7 @@ static int get_shown_sequences(   ListBase * seqbasep, int cfra, int chanshown, Se
 
        return cnt;
 }
+
 
 /* **********************************************************************
    proxy management
@@ -1124,6 +1139,53 @@ 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 >= 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));
+
+       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;
@@ -1133,18 +1195,25 @@ static int seq_proxy_get_fname(SeqRenderData context, Sequence * seq, int cfra,
                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;
@@ -1152,20 +1221,14 @@ static int seq_proxy_get_fname(SeqRenderData context, Sequence * seq, int cfra,
 
        /* 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);
        }
@@ -1204,7 +1267,13 @@ static struct ImBuf * seq_proxy_fetch(SeqRenderData context, Sequence * seq, int
                        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) {
@@ -1218,58 +1287,24 @@ 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)) {
+       if (!seq_proxy_get_fname(context, seq, cfra, name)) {
                return;
        }
 
-       /* rendering at 100% ? No real sense in proxy-ing, right? */
-       if (preview_render_size == 100) {
-               return;
-       }
-
-       /* that's why it is called custom... */
-       if (seq->flag & SEQ_USE_PROXY_CUSTOM_FILE) {
-               return;
-       }
-
-       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;
-       }
+       ibuf = seq_render_strip(context, seq, cfra);
 
-       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);
@@ -1291,69 +1326,76 @@ 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;
 
-       waitcursor(1);
+       if (!seq->strip || !seq->strip->proxy) {
+               return;
+       }
 
-       G.afbreek = 0;
+       if (!(seq->flag & SEQ_USE_PROXY)) {
+               return;
+       }
 
-       /* 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.
-        */
+       tc_flags   = seq->strip->proxy->build_tc_flags;
+       size_flags = seq->strip->proxy->build_size_flags;
+       quality    = seq->strip->proxy->quality;
 
-       for (cfra = seq->startdisp; cfra < seq->enddisp; cfra++) {
-               TStripElem * tse = give_tstripelem(seq, cfra);
+       if (seq->type == SEQ_MOVIE) {
+               seq_open_anim_file(seq);
 
-               tse->flag &= ~STRIPELEM_PREVIEW_DONE;
+               if (seq->anim) {
+                       IMB_anim_index_rebuild(
+                               seq->anim, tc_flags, size_flags, quality,
+                               stop, do_update, progress);
+               }
+               return;
        }
 
-       
+       if (!(seq->flag & SEQ_USE_PROXY)) {
+               return;
+       }
 
-       /* 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;
-                       }
+       /* that's why it is called custom... */
+       if (seq->flag & SEQ_USE_PROXY_CUSTOM_FILE) {
+               return;
+       }
+
+       /* fail safe code */
+
+       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);
                }
+
+               *progress= (float)cfra/(seq->enddisp - seq->endstill - seq->startdisp + seq->startstill);
+               *do_update= 1;
+
+               if(*stop || G.afbreek)
+                       break;
        }
-       waitcursor(0);
 }
-#endif
 
 
 /* **********************************************************************
@@ -1570,6 +1612,8 @@ static ImBuf * input_preprocess(
 {
        float mul;
 
+       ibuf = IMB_makeSingleUser(ibuf);
+
        if((seq->flag & SEQ_FILTERY) && seq->type != SEQ_MOVIE) {
                IMB_filtery(ibuf);
        }
@@ -2095,17 +2139,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);
@@ -3599,7 +3646,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 */
index a25f2ebda26f1a2f5f36cad9386280e651f039b7..fb90b1e0c9bda79401032ee6f05270099b591788 100644 (file)
@@ -10060,12 +10060,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;
                                        }
                                }
index 07b5bf90cd5bf0df577651ec7eb7bf2d6dbcc9ce..c4fd67cf06fc08e0cb3f62e76e7a02263d91278c 100644 (file)
@@ -2367,6 +2367,7 @@ void uiTemplateOperatorSearch(uiLayout *layout)
 #define B_STOPANIM             3
 #define B_STOPCOMPO            4
 #define B_STOPCLIP             5
+#define B_STOPSEQ              6
 
 static void do_running_jobs(bContext *C, void *UNUSED(arg), int event)
 {
@@ -2386,6 +2387,9 @@ static void do_running_jobs(bContext *C, void *UNUSED(arg), int event)
                case B_STOPCLIP:
                        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;
        }
 }
 
@@ -2412,8 +2416,11 @@ void uiTemplateRunningJobs(uiLayout *layout, bContext *C)
                if(WM_jobs_test(wm, sa))
                   owner = sa;
                handle_event= B_STOPCLIP;
-       }
-       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)
index 66e844e67a8f689bc694b1d83e9df424ea56aad0..4011f038be8fcf1d33a54e6f87437760314b9297 100644 (file)
@@ -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);
        }
 }
index 6a69d32d307fb83b11116a23eebaa7876c848de3..9b938f8b1bd6e68aabe7fb86b449e0524d12cdef 100644 (file)
@@ -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"
@@ -129,6 +130,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;
@@ -2830,3 +2936,35 @@ void SEQUENCER_OT_view_ghost_border(wmOperatorType *ot)
        /* rna */
        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;
+}
index 209b39662aaf894e95bc390e9dd903c8e94a0f02..09a5895d7f02b55230912b92f33619b71cbd6021 100644 (file)
@@ -111,6 +111,8 @@ void SEQUENCER_OT_view_ghost_border(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);
 
index f5c26cb17d3308818fb6a63673db555ae1d22caf..fdb40204d213853b06b6189aed9497ba5c2eee48 100644 (file)
@@ -86,6 +86,8 @@ 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);
+
        /* sequencer_select.c */
        WM_operatortype_append(SEQUENCER_OT_select_all_toggle);
        WM_operatortype_append(SEQUENCER_OT_select_inverse);
index 18b5eff5c7358e47fcdbe74db49e2dda00a165ce..ff13be20d4e10c5d590ea9bee30b6a754d9ea94f 100644 (file)
@@ -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
index 36123592c546e289f519a871c60d99b99b551dc2..cec805c049e8be0e13c6a60ef5d2119c16753d7b 100644 (file)
@@ -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);
 
 /**
  *
@@ -192,10 +193,61 @@ void IMB_rectcpy(struct ImBuf *drect, struct ImBuf *srect, int destx,
 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_MAX_SLOT = 3
+} 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);
 
 /**
  *
@@ -204,6 +256,7 @@ int IMB_anim_get_duration(struct anim *anim);
 struct anim *IMB_open_anim(const char *name, int ib_flags);
 void IMB_close_anim(struct anim *anim);
 
+
 /**
  *
  * @attention Defined in anim.c
@@ -218,7 +271,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 */);
 
 /**
  *
@@ -227,12 +283,6 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, int position);
  */
 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
index fba0772dd93ac1dc637cdcc9a42bd33e36b6dfd4..1cce2a95f65bede15c6125bf99c894479fc9b76f 100644 (file)
 #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;
@@ -149,9 +152,6 @@ struct anim {
        int interlacing;
        int preseek;
        
-               /* data */
-       struct ImBuf * ibuf1, * ibuf2;
-       
                /* avi */
        struct _AviMovie *avi;
 
@@ -179,11 +179,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 (file)
index 0000000..ae3b48f
--- /dev/null
@@ -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
index f71235b0a9de2cbe6af74cdee8a7c3eee808cf77..fdcae9eb8724afa4d21bcb8c8b8190a2db6e1371 100644 (file)
@@ -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;
index 919b0eb0c29e736b20f044988ed91277523d7551..f93dc4bf8736e13cf62eca381416129b1410e945 100644 (file)
@@ -57,6 +57,7 @@
 #include <ctype.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <math.h>
 #ifndef _WIN32
 #include <dirent.h>
 #else
@@ -90,6 +91,7 @@
 
 #include "IMB_allocimbuf.h"
 #include "IMB_anim.h"
+#include "IMB_indexer.h"
 
 #ifdef WITH_FFMPEG
 #include <libavformat/avformat.h>
@@ -304,15 +306,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 +319,6 @@ void IMB_free_anim(struct anim * anim) {
                return;
        }
 
-       IMB_free_anim_ibuf(anim);
        free_anim_movie(anim);
        free_anim_avi(anim);
 
@@ -339,6 +331,7 @@ void IMB_free_anim(struct anim * anim) {
 #ifdef WITH_REDCODE
        free_anim_redcode(anim);
 #endif
+       IMB_free_indices(anim);
 
        MEM_freeN(anim);
 }
@@ -496,14 +489,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 +510,8 @@ static int startffmpeg(struct anim * anim) {
        AVCodec *pCodec;
        AVFormatContext *pFormatCtx;
        AVCodecContext *pCodecCtx;
+       int frs_num;
+       double frs_den;
 
 #ifdef FFMPEG_SWSCALE_COLOR_SPACE_SUPPORT
        /* The following for color space determination */
@@ -575,6 +570,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 +592,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 +679,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 +712,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 +800,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) 
+                       rint(((double) position) / pts_time_base / frame_rate);
+
+               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 (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);
+       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");
+
+               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 *= anim->pFormatCtx->bit_rate / frame_rate;
-                       pos /= 8;
+                       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);
+
+                       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 +1169,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 +1302,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 +1337,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 +1381,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 +1405,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/indexer.c b/source/blender/imbuf/intern/indexer.c
new file mode 100644 (file)
index 0000000..8009bf6
--- /dev/null
@@ -0,0 +1,1071 @@
+#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 "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 };
+static int tc_types[] = { IMB_TC_RECORD_RUN, IMB_TC_FREE_RUN,
+                         IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN };
+static float proxy_fac[] = { 0.25, 0.50, 0.75 };
+
+#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... */
+       case IMB_PROXY_MAX_SLOT:
+               return 0;
+       case IMB_PROXY_25:
+               return 0;
+       case IMB_PROXY_50:
+               return 1;
+       case IMB_PROXY_75:
+               return 2;
+       };
+       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... */
+       case IMB_TC_MAX_SLOT:
+               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;
+       };
+       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_names[] = { 
+               "proxy_25.avi", "proxy_50.avi", "proxy_75.avi" };
+
+       char * proxy_temp_names[] = { 
+               "proxy_25_part.avi", "proxy_50_part.avi", "proxy_75_part.avi" };
+
+       get_index_dir(anim, index_dir);
+
+       BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR, index_dir, 
+                        temp ? proxy_temp_names[i] : proxy_names[i]);
+}
+
+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.blen_tc", "free_run.blen_tc",
+               "interp_free_run.blen_tc" };
+
+       get_index_dir(anim, index_dir);
+       
+       BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR, 
+                        index_dir, index_names[i]);
+}
+
+/* ----------------------------------------------------------------------
+   - 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);
+       
+       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->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->orig_height = st->codec->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 (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, 
+               frame ? ctx->frame : 0);
+
+       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);
+
+       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;
+
+       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;
+       }
+
+       /* Find the first video stream */
+       videoStream = -1;
+       for (i = 0; i < iFormatCtx->nb_streams; i++)
+               if(iFormatCtx->streams[i]->codec->codec_type
+                  == AVMEDIA_TYPE_VIDEO) {
+                       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 =  rint(((double) next_packet.pos) * 100 / 
+                                           ((double) stream_size)) / 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 (in_frame->pkt_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);
+       
+       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 (file)
index 0000000..0d984aa
--- /dev/null
@@ -0,0 +1,365 @@
+#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;
+}
index 1d91f34f4fa34573bd028d929115291028881eea..b08d090dff6471e83921afd45abd9389c851725e 100644 (file)
@@ -319,7 +319,7 @@ ImBuf* IMB_thumb_create(const char* path, ThumbSize size, ThumbSource source, Im
                                struct anim * anim = NULL;
                                anim = IMB_open_anim(path, IB_rect | IB_metadata);
                                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 {
index 6db8dcc06cf1a88f75c75b872b4895db553d0852..85d31f18a0362aaea7dc5fe9e3789a6e143e0f48 100644 (file)
@@ -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);
                }
        }
 }
index 3e7654bcf47d000eda88493ea273eb3a447a74c5..35402022236cd689e8f7f70631c12eacef84ff45 100644 (file)
@@ -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 {
@@ -288,6 +295,18 @@ 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_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
index 8c4e4d9e736aa7af32c20da702f0a06b8b901ffc..9a0e8bb62dad9844ae5cc8810ea28f084bff8c0b 100644 (file)
@@ -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,42 @@ 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_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 +1289,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);
index 42c3096dfc9fed028f7d15497bc062e024ad74b8..29d54d34acee0419080c675f4834b098efbc7f4b 100644 (file)
@@ -298,6 +298,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 *, 
index 4ab4eebdfe15b82d11d6fe20747faf7ef167a4fe..f4e0b4ef06ceed1a43d07d19477d3f65c787a6f6 100644 (file)
@@ -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 */
index c1f0560bc1cb9cb5f993b265c9cc5812a042fec7..52cc8a1b21c89070c566ff9b7bed449b0f592781 100644 (file)
@@ -552,11 +552,11 @@ elseif(WIN32)
        if(WITH_CODEC_FFMPEG)
                install(
                        FILES
-                               ${LIBDIR}/ffmpeg/lib/avcodec-52.dll
-                               ${LIBDIR}/ffmpeg/lib/avformat-52.dll
-                               ${LIBDIR}/ffmpeg/lib/avdevice-52.dll
-                               ${LIBDIR}/ffmpeg/lib/avutil-50.dll
-                               ${LIBDIR}/ffmpeg/lib/swscale-0.dll
+                               ${LIBDIR}/ffmpeg-0.8/lib/avcodec-53.dll
+                               ${LIBDIR}/ffmpeg-0.8/lib/avformat-53.dll
+                               ${LIBDIR}/ffmpeg-0.8/lib/avdevice-53.dll
+                               ${LIBDIR}/ffmpeg-0.8/lib/avutil-51.dll
+                               ${LIBDIR}/ffmpeg-0.8/lib/swscale-2.dll
                        DESTINATION ${TARGETDIR}
                )