== Sequencer ==
authorPeter Schlaile <peter@schlaile.de>
Sun, 28 Aug 2011 14:46:03 +0000 (14:46 +0000)
committerPeter Schlaile <peter@schlaile.de>
Sun, 28 Aug 2011 14:46:03 +0000 (14:46 +0000)
This patch adds:

* support for proxy building again (missing feature from Blender 2.49)
  additionally to the way, Blender 2.49 worked, you can select several
  strips at once and make Blender build proxies in the background (using
  the job system)
  Also a new thing: movie proxies are now build into AVI files, and
  the proxy system is moved into ImBuf-library, so that other parts
  of blender can also benefit from it.

* Timecode support: to fix seeking issues with files, that have
  a) varying frame rates
  b) very large GOP lengths
  c) are broken inbetween
  d) use different time code tracks

  the proxy builder can now also build timecode indices, which are
  used (optionally) for seeking.

  For the first time, it is possible, to do frame exact seeking on
  all file types.

* Support for different video-streams in one video file (can be
  selected in sequencer, other parts of blender can also use it,
  but UI has to be added accordingly)

* IMPORTANT: this patch *requires* ffmpeg 0.7 or newer, since
  older versions don't support the pkt_pts field, that is essential
  for building timecode indices.

  Windows and Mac libs are already updated, Linux-users have to build
  their own ffmpeg verions until distros keep up.

30 files changed:
intern/ffmpeg/ffmpeg_compat.h
release/scripts/startup/bl_ui/space_sequencer.py
source/blender/blenkernel/BKE_image.h
source/blender/blenkernel/BKE_sequencer.h
source/blender/blenkernel/intern/image.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/SConscript
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/filter.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/makesdna/DNA_space_types.h
source/blender/makesrna/intern/rna_sequencer.c
source/blender/makesrna/intern/rna_space.c
source/blender/windowmanager/WM_api.h
source/blender/windowmanager/intern/wm_jobs.c

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 84cc365425ebe59d4e7aec3e819d5ac132dcc797..1902e9345d6f0f9206e394fa56308fc0743e5566 100644 (file)
@@ -60,6 +60,7 @@ class SEQUENCER_HT_header(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")
@@ -241,6 +242,7 @@ class SEQUENCER_MT_strip(Menu):
         layout.operator("sequencer.images_separate")
         layout.operator("sequencer.offset_clear")
         layout.operator("sequencer.deinterlace_selected_movies")
+        layout.operator("sequencer.rebuild_proxy")
         layout.separator()
 
         layout.operator("sequencer.duplicate")
@@ -578,6 +580,7 @@ class SEQUENCER_PT_input(SequencerButtonsPanel, Panel):
             col = split.column()
             col.prop(strip, "filepath", text="")
             col.prop(strip, "mpeg_preseek", text="MPEG Preseek")
+            col.prop(strip, "streamindex", text="Stream Index")
 
         # TODO, sound???
         # end drawing filename
@@ -746,7 +749,7 @@ class SEQUENCER_PT_filter(SequencerButtonsPanel, Panel):
 
 
 class SEQUENCER_PT_proxy(SequencerButtonsPanel, Panel):
-    bl_label = "Proxy"
+    bl_label = "Proxy / Timecode"
 
     @classmethod
     def poll(cls, context):
@@ -772,12 +775,29 @@ class SEQUENCER_PT_proxy(SequencerButtonsPanel, 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.prop(strip.proxy, "build_25")
+            row.prop(strip.proxy, "build_50")
+            row.prop(strip.proxy, "build_75")
+            row.prop(strip.proxy, "build_100")
+
+            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, Panel):
     bl_label = "Scene Preview/Render"
index 10910493ec94d467633adce0268ed8ae6dd11d15..0c31083a266b5b892fc4b21cb158b4937398deb7 100644 (file)
@@ -60,7 +60,7 @@ int           BKE_ftype_to_imtype(int ftype);
 int            BKE_imtype_to_ftype(int imtype);
 int            BKE_imtype_is_movie(int imtype);
 
-struct anim *openanim(char * name, int flags);
+struct anim *openanim(char * name, int flags, int streamindex);
 
 void   image_de_interlace(struct Image *ima, int odd);
        
index bedd58876bc1926e87384146449944ec332cd515..b20811724f46f5234bd1603d271311a9226c0b40 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..4ce5de78895766cfef7a9910c6257bb30acf483c 100644 (file)
@@ -1371,15 +1371,15 @@ void BKE_makepicstring(char *string, const char *base, int frame, int imtype, co
 }
 
 /* used by sequencer too */
-struct anim *openanim(char *name, int flags)
+struct anim *openanim(char *name, int flags, int streamindex)
 {
        struct anim *anim;
        struct ImBuf *ibuf;
        
-       anim = IMB_open_anim(name, flags);
+       anim = IMB_open_anim(name, flags, streamindex);
        if (anim == NULL) return NULL;
 
-       ibuf = IMB_anim_absolute(anim, 0);
+       ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
        if (ibuf == NULL) {
                if(BLI_exists(name))
                        printf("not an anim: %s\n", name);
@@ -1773,20 +1773,26 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame)
                else
                        BLI_path_abs(str, G.main->name);
                
-               ima->anim = openanim(str, IB_rect);
+               /* FIXME: make several stream accessible in image editor, too*/
+               ima->anim = openanim(str, IB_rect, 0);
                
                /* let's initialize this user */
                if(ima->anim && iuser && iuser->frames==0)
-                       iuser->frames= IMB_anim_get_duration(ima->anim);
+                       iuser->frames= IMB_anim_get_duration(ima->anim,
+                                                            IMB_TC_RECORD_RUN);
        }
        
        if(ima->anim) {
-               int dur = IMB_anim_get_duration(ima->anim);
+               int dur = IMB_anim_get_duration(ima->anim,
+                                               IMB_TC_RECORD_RUN);
                int fra= frame-1;
                
                if(fra<0) fra = 0;
                if(fra>(dur-1)) fra= dur-1;
-               ibuf = IMB_anim_absolute(ima->anim, fra);
+               ibuf = IMB_makeSingleUser(
+                       IMB_anim_absolute(ima->anim, fra,
+                                         IMB_TC_RECORD_RUN,
+                                         IMB_PROXY_NONE));
                
                if(ibuf) {
                        image_initialize_after_load(ima, ibuf);
index 3aebbea789f34673a91e2b8f6aa624aafba434a6..60479da64a952946de5d7a0c62203569667cc2c0 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) {
@@ -683,13 +695,16 @@ void reload_sequence_new_file(Scene *scene, Sequence * seq, int lock_range)
        }
        case SEQ_MOVIE:
                if(seq->anim) IMB_free_anim(seq->anim);
-               seq->anim = openanim(str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0));
+               seq->anim = openanim(str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), seq->streamindex);
 
                if (!seq->anim) {
                        return;
                }
        
-               seq->len = IMB_anim_get_duration(seq->anim);
+               seq->len = IMB_anim_get_duration(seq->anim,
+                                                seq->strip->proxy ?
+                                                seq->strip->proxy->tc :
+                                                IMB_TC_RECORD_RUN);
                
                seq->anim_preseek = IMB_anim_get_preseek(seq->anim);
 
@@ -1117,7 +1132,7 @@ static int get_shown_sequences(   ListBase * seqbasep, int cfra, int chanshown, Se
 
        return cnt;
 }
+
 
 /* **********************************************************************
    proxy management
@@ -1125,48 +1140,105 @@ static int get_shown_sequences(        ListBase * seqbasep, int cfra, int chanshown, Se
 
 #define PROXY_MAXFILE (2*FILE_MAXDIR+FILE_MAXFILE)
 
+static IMB_Proxy_Size seq_rendersize_to_proxysize(int size)
+{
+       if (size >= 100) {
+               return IMB_PROXY_NONE;
+       }
+       if (size >= 99) {
+               return IMB_PROXY_100;
+       }
+       if (size >= 75) {
+               return IMB_PROXY_75;
+       }
+       if (size >= 50) {
+               return IMB_PROXY_50;
+       }
+       return IMB_PROXY_25;
+}
+
+static void seq_open_anim_file(Sequence * seq)
+{
+       char name[FILE_MAXDIR+FILE_MAXFILE];
+       StripProxy * proxy;
+
+       if(seq->anim != NULL) {
+               return;
+       }
+
+       BLI_join_dirfile(name, sizeof(name), 
+                        seq->strip->dir, seq->strip->stripdata->name);
+       BLI_path_abs(name, G.main->name);
+       
+       seq->anim = openanim(name, IB_rect |
+                            ((seq->flag & SEQ_FILTERY) ? 
+                             IB_animdeinterlace : 0), seq->streamindex);
+
+       if (seq->anim == NULL) {
+               return;
+       }
+
+       proxy = seq->strip->proxy;
+
+       if (proxy == NULL) {
+               return;
+       }
+
+       if (seq->flag & SEQ_USE_PROXY_CUSTOM_DIR) {
+               IMB_anim_set_index_dir(seq->anim, seq->strip->proxy->dir);
+       }
+}
+
+
 static int seq_proxy_get_fname(SeqRenderData context, Sequence * seq, int cfra, char * name)
 {
        int frameno;
        char dir[FILE_MAXDIR];
+       int render_size = context.preview_render_size;
 
        if (!seq->strip->proxy) {
                return FALSE;
        }
 
+       /* MOVIE tracks (only exception: custom files) are now handled 
+          internally by ImBuf module for various reasons: proper time code 
+          support, quicker index build, using one file instead 
+          of a full directory of jpeg files, etc. Trying to support old
+          and new method at once could lead to funny effects, if people
+          have both, a directory full of jpeg files and proxy avis, so
+          sorry folks, please rebuild your proxies... */
+
        if (seq->flag & (SEQ_USE_PROXY_CUSTOM_DIR|SEQ_USE_PROXY_CUSTOM_FILE)) {
                strcpy(dir, seq->strip->proxy->dir);
+       } else if (seq->type == SEQ_IMAGE) {
+               snprintf(dir, PROXY_MAXFILE, "%s/BL_proxy", seq->strip->dir);
        } else {
-               if (ELEM(seq->type, SEQ_IMAGE, SEQ_MOVIE)) {
-                       snprintf(dir, FILE_MAXDIR, "%s/BL_proxy", seq->strip->dir);
-               } else {
-                       return FALSE;
-               }
+               return FALSE;
        }
 
        if (seq->flag & SEQ_USE_PROXY_CUSTOM_FILE) {
-               BLI_join_dirfile(name, FILE_MAX, dir, seq->strip->proxy->file); /* XXX, not real length */
+               BLI_join_dirfile(name, PROXY_MAXFILE, 
+                                dir, seq->strip->proxy->file);
                BLI_path_abs(name, G.main->name);
 
                return TRUE;
        }
 
+       /* dirty hack to distinguish 100% render size from PROXY_100 */
+       if (render_size == 99) {
+               render_size = 100;
+       }
+
        /* generate a separate proxy directory for each preview size */
 
-       switch(seq->type) {
-       case SEQ_IMAGE:
+       if (seq->type == SEQ_IMAGE) {
                snprintf(name, PROXY_MAXFILE, "%s/images/%d/%s_proxy", dir,
                         context.preview_render_size, 
                         give_stripelem(seq, cfra)->name);
                frameno = 1;
-               break;
-       case SEQ_MOVIE:
-               frameno = (int) give_stripelem_index(seq, cfra) + seq->anim_startofs;
-               snprintf(name, PROXY_MAXFILE, "%s/%s/%d/####", dir,
-                        seq->strip->stripdata->name, context.preview_render_size);
-               break;
-       default:
-               frameno = (int) give_stripelem_index(seq, cfra) + seq->anim_startofs;
+       } else {
+               frameno = (int) give_stripelem_index(seq, cfra) 
+                       + seq->anim_startofs;
                snprintf(name, PROXY_MAXFILE, "%s/proxy_misc/%d/####", dir, 
                         context.preview_render_size);
        }
@@ -1182,13 +1254,18 @@ static int seq_proxy_get_fname(SeqRenderData context, Sequence * seq, int cfra,
 static struct ImBuf * seq_proxy_fetch(SeqRenderData context, Sequence * seq, int cfra)
 {
        char name[PROXY_MAXFILE];
+       IMB_Proxy_Size psize = seq_rendersize_to_proxysize(
+               context.preview_render_size);
+       int size_flags;
 
        if (!(seq->flag & SEQ_USE_PROXY)) {
                return NULL;
        }
 
-       /* rendering at 100% ? No real sense in proxy-ing, right? */
-       if (context.preview_render_size == 100) {
+       size_flags = seq->strip->proxy->build_size_flags;
+
+       /* only use proxies, if they are enabled (even if present!) */
+       if (psize != IMB_PROXY_NONE && ((size_flags & psize) != psize)) {
                return NULL;
        }
 
@@ -1199,13 +1276,19 @@ static struct ImBuf * seq_proxy_fetch(SeqRenderData context, Sequence * seq, int
                                return NULL;
                        }
  
-                       seq->strip->proxy->anim = openanim(name, IB_rect);
+                       seq->strip->proxy->anim = openanim(name, IB_rect, 0);
                }
                if (seq->strip->proxy->anim==NULL) {
                        return NULL;
                }
  
-               return IMB_anim_absolute(seq->strip->proxy->anim, frameno);
+               seq_open_anim_file(seq);
+
+               frameno = IMB_anim_index_get_frame_index(
+                       seq->anim, seq->strip->proxy->tc, frameno);
+
+               return IMB_anim_absolute(seq->strip->proxy->anim, frameno,
+                                        IMB_TC_NONE, IMB_PROXY_NONE);
        }
  
        if (seq_proxy_get_fname(context, seq, cfra, name) == 0) {
@@ -1219,67 +1302,30 @@ static struct ImBuf * seq_proxy_fetch(SeqRenderData context, Sequence * seq, int
        }
 }
 
-#if 0
-static void do_build_seq_ibuf(Scene *scene, Sequence * seq, TStripElem *se, int cfra,
-                                 int build_proxy_run, int preview_render_size);
-
-static void seq_proxy_build_frame(Scene *scene, Sequence * seq, int cfra, int preview_render_size, int seqrectx, int seqrecty)
+static void seq_proxy_build_frame(SeqRenderData context,
+                                 Sequence* seq, int cfra,
+                                 int proxy_render_size)
 {
        char name[PROXY_MAXFILE];
        int quality;
-       TStripElem * se;
-       int ok;
        int rectx, recty;
+       int ok;
        struct ImBuf * ibuf;
 
-       if (!(seq->flag & SEQ_USE_PROXY)) {
-               return;
-       }
-
-       /* rendering at 100% ? No real sense in proxy-ing, right? */
-       if (preview_render_size == 100) {
+       if (!seq_proxy_get_fname(context, seq, cfra, name)) {
                return;
        }
 
-       /* that's why it is called custom... */
-       if (seq->flag & SEQ_USE_PROXY_CUSTOM_FILE) {
-               return;
-       }
+       ibuf = seq_render_strip(context, seq, cfra);
 
-       if (!seq_proxy_get_fname(scene, seq, cfra, name, preview_render_size)) {
-               return;
-       }
-
-       se = give_tstripelem(seq, cfra);
-       if (!se) {
-               return;
-       }
-
-       if(se->ibuf) {
-               IMB_freeImBuf(se->ibuf);
-               se->ibuf = 0;
-       }
-       
-       do_build_seq_ibuf(scene, seq, se, cfra, TRUE, preview_render_size,
-                         seqrectx, seqrecty);
-
-       if (!se->ibuf) {
-               return;
-       }
-
-       rectx= (preview_render_size*scene->r.xsch)/100;
-       recty= (preview_render_size*scene->r.ysch)/100;
-
-       ibuf = se->ibuf;
+       rectx = (proxy_render_size * context.scene->r.xsch) / 100;
+       recty = (proxy_render_size * context.scene->r.ysch) / 100;
 
        if (ibuf->x != rectx || ibuf->y != recty) {
                IMB_scalefastImBuf(ibuf, (short)rectx, (short)recty);
        }
 
-       /* quality is fixed, otherwise one has to generate separate
-          directories for every quality...
-
-          depth = 32 is intentionally left in, otherwise ALPHA channels
+       /* depth = 32 is intentionally left in, otherwise ALPHA channels
           won't work... */
        quality = seq->strip->proxy->quality;
        ibuf->ftype= JPG | quality;
@@ -1292,69 +1338,80 @@ static void seq_proxy_build_frame(Scene *scene, Sequence * seq, int cfra, int pr
        }
 
        IMB_freeImBuf(ibuf);
-       se->ibuf = 0;
 }
 
-static void seq_proxy_rebuild(Scene *scene, Sequence * seq, int seqrectx,
-                             int seqrecty)
+void seq_proxy_rebuild(struct Main * bmain, Scene *scene, Sequence * seq,
+                      short *stop, short *do_update, float *progress)
 {
+       SeqRenderData context;
        int cfra;
-       float rsize = seq->strip->proxy->size;
+       int tc_flags;
+       int size_flags;
+       int quality;
+
+       if (!seq->strip || !seq->strip->proxy) {
+               return;
+       }
+
+       if (!(seq->flag & SEQ_USE_PROXY)) {
+               return;
+       }
 
-       waitcursor(1);
+       tc_flags   = seq->strip->proxy->build_tc_flags;
+       size_flags = seq->strip->proxy->build_size_flags;
+       quality    = seq->strip->proxy->quality;
 
-       G.afbreek = 0;
+       if (seq->type == SEQ_MOVIE) {
+               seq_open_anim_file(seq);
 
-       /* flag management tries to account for strobe and 
-          other "non-linearities", that might come in the future...
-          better way would be to "touch" the files, so that _really_
-          no one is rebuild twice.
-        */
+               if (seq->anim) {
+                       IMB_anim_index_rebuild(
+                               seq->anim, tc_flags, size_flags, quality,
+                               stop, do_update, progress);
+               }
+               return;
+       }
 
-       for (cfra = seq->startdisp; cfra < seq->enddisp; cfra++) {
-               TStripElem * tse = give_tstripelem(seq, cfra);
+       if (!(seq->flag & SEQ_USE_PROXY)) {
+               return;
+       }
 
-               tse->flag &= ~STRIPELEM_PREVIEW_DONE;
+       /* that's why it is called custom... */
+       if (seq->flag & SEQ_USE_PROXY_CUSTOM_FILE) {
+               return;
        }
 
-       
+       /* fail safe code */
 
-       /* a _lot_ faster for movie files, if we read frames in
-          sequential order */
-       if (seq->flag & SEQ_REVERSE_FRAMES) {
-               for (cfra = seq->enddisp-seq->endstill-1; 
-                        cfra >= seq->startdisp + seq->startstill; cfra--) {
-                       TStripElem * tse = give_tstripelem(seq, cfra);
-
-                       if (!(tse->flag & STRIPELEM_PREVIEW_DONE)) {
-//XXX                          set_timecursor(cfra);
-                               seq_proxy_build_frame(scene, seq, cfra, rsize,
-                                                     seqrectx, seqrecty);
-                               tse->flag |= STRIPELEM_PREVIEW_DONE;
-                       }
-                       if (blender_test_break()) {
-                               break;
-                       }
+       context = seq_new_render_data(
+               bmain, scene, 
+               (scene->r.size * (float) scene->r.xsch) / 100.0f + 0.5f, 
+               (scene->r.size * (float) scene->r.ysch) / 100.0f + 0.5f, 
+               100);
+
+       for (cfra = seq->startdisp + seq->startstill; 
+            cfra < seq->enddisp - seq->endstill; cfra++) {
+               if (size_flags & IMB_PROXY_25) {
+                       seq_proxy_build_frame(context, seq, cfra, 25);
                }
-       } else {
-               for (cfra = seq->startdisp + seq->startstill; 
-                        cfra < seq->enddisp - seq->endstill; cfra++) {
-                       TStripElem * tse = give_tstripelem(seq, cfra);
-
-                       if (!(tse->flag & STRIPELEM_PREVIEW_DONE)) {
-//XXX                          set_timecursor(cfra);
-                               seq_proxy_build_frame(scene, seq, cfra, rsize,
-                                                     seqrectx, seqrecty);
-                               tse->flag |= STRIPELEM_PREVIEW_DONE;
-                       }
-                       if (blender_test_break()) {
-                               break;
-                       }
+               if (size_flags & IMB_PROXY_50) {
+                       seq_proxy_build_frame(context, seq, cfra, 50);
+               }
+               if (size_flags & IMB_PROXY_75) {
+                       seq_proxy_build_frame(context, seq, cfra, 75);
                }
+               if (size_flags & IMB_PROXY_100) {
+                       seq_proxy_build_frame(context, seq, cfra, 100);
+               }
+
+               *progress= (float)cfra/(seq->enddisp - seq->endstill 
+                                       - seq->startdisp + seq->startstill);
+               *do_update= 1;
+
+               if(*stop || G.afbreek)
+                       break;
        }
-       waitcursor(0);
 }
-#endif
 
 
 /* **********************************************************************
@@ -1571,6 +1628,8 @@ static ImBuf * input_preprocess(
 {
        float mul;
 
+       ibuf = IMB_makeSingleUser(ibuf);
+
        if((seq->flag & SEQ_FILTERY) && seq->type != SEQ_MOVIE) {
                IMB_filtery(ibuf);
        }
@@ -2096,17 +2155,20 @@ static ImBuf * seq_render_strip(SeqRenderData context, Sequence * seq, float cfr
                }
                case SEQ_MOVIE:
                {
-                       if(seq->anim==NULL) {
-                               BLI_join_dirfile(name, sizeof(name), seq->strip->dir, seq->strip->stripdata->name);
-                               BLI_path_abs(name, G.main->name);
-                                       
-                               seq->anim = openanim(name, IB_rect |
-                                                    ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0));
-                       }
+                       seq_open_anim_file(seq);
 
                        if(seq->anim) {
-                               IMB_anim_set_preseek(seq->anim, seq->anim_preseek);
-                               ibuf = IMB_anim_absolute(seq->anim, nr + seq->anim_startofs);
+                               IMB_anim_set_preseek(seq->anim,
+                                                    seq->anim_preseek);
+
+                               ibuf = IMB_anim_absolute(
+                                       seq->anim, nr + seq->anim_startofs, 
+                                       seq->strip->proxy ? 
+                                       seq->strip->proxy->tc
+                                       : IMB_TC_RECORD_RUN, 
+                                       seq_rendersize_to_proxysize(
+                                               context.preview_render_size));
+
                                /* we don't need both (speed reasons)! */
                                if (ibuf && ibuf->rect_float && ibuf->rect)
                                        imb_freerectImBuf(ibuf);
@@ -3584,7 +3646,7 @@ Sequence *sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoadInfo
        BLI_strncpy(path, seq_load->path, sizeof(path));
        BLI_path_abs(path, G.main->name);
 
-       an = openanim(path, IB_rect);
+       an = openanim(path, IB_rect, 0);
 
        if(an==NULL)
                return NULL;
@@ -3600,7 +3662,7 @@ Sequence *sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoadInfo
 
        /* basic defaults */
        seq->strip= strip= MEM_callocN(sizeof(Strip), "strip");
-       strip->len = seq->len = IMB_anim_get_duration( an );
+       strip->len = seq->len = IMB_anim_get_duration( an, IMB_TC_RECORD_RUN );
        strip->us= 1;
 
        /* we only need 1 element for MOVIE strips */
index f3b478b90f98c10ba0d82f80e43b1a662d6c861f..df7e1f80cbd262f91e1af308d18a8fcc59f9236c 100644 (file)
@@ -9951,12 +9951,6 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
                        if(ed) {
                                SEQP_BEGIN(ed, seq) {
                                        if (seq->strip && seq->strip->proxy){
-                                               if (sce->r.size != 100.0) {
-                                                       seq->strip->proxy->size
-                                                               = sce->r.size;
-                                               } else {
-                                                       seq->strip->proxy->size = 25;
-                                               }
                                                seq->strip->proxy->quality =90;
                                        }
                                }
index 2faac24fd783643c81c6a7daf9282de57bb3e227..67123476f063be2048d0b16509de43eb200a9edf 100644 (file)
@@ -2369,6 +2369,7 @@ void uiTemplateOperatorSearch(uiLayout *layout)
 #define B_STOPCAST             2
 #define B_STOPANIM             3
 #define B_STOPCOMPO            4
+#define B_STOPSEQ              5
 
 static void do_running_jobs(bContext *C, void *UNUSED(arg), int event)
 {
@@ -2385,6 +2386,9 @@ static void do_running_jobs(bContext *C, void *UNUSED(arg), int event)
                case B_STOPCOMPO:
                        WM_jobs_stop(CTX_wm_manager(C), CTX_wm_area(C), NULL);
                        break;
+               case B_STOPSEQ:
+                       WM_jobs_stop(CTX_wm_manager(C), CTX_wm_area(C), NULL);
+                       break;
        }
 }
 
@@ -2406,8 +2410,11 @@ void uiTemplateRunningJobs(uiLayout *layout, bContext *C)
                if(WM_jobs_test(wm, sa))
                   owner = sa;
                handle_event= B_STOPCOMPO;
-       } 
-       else {
+       } else if (sa->spacetype==SPACE_SEQ) {
+               if(WM_jobs_test(wm, sa))
+                       owner = sa;
+               handle_event = B_STOPSEQ;
+       } else {
                Scene *scene;
                /* another scene can be rendering too, for example via compositor */
                for(scene= CTX_data_main(C)->scene.first; scene; scene= scene->id.next)
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 65bbf90055667436dca5ddd2b5d302d6f094ad65..3430c10b7667eb0784ef17700f4fa83af35eb7f6 100644 (file)
@@ -8,4 +8,7 @@ incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include'
 incs += ' ../../makesrna ../../blenloader'
 incs += ' #/intern/audaspace/intern'
 
+if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'):
+    incs += ' ' + env['BF_PTHREADS_INC']
+
 env.BlenderLib ( 'bf_editors_space_sequencer', sources, Split(incs), [], libtype=['core'], priority=[100] )
index e876da41bd9c363b66f6cf952a23e7900b6bef56..18ff33fd8a9a9513c234533ab7b4b10d2058f334 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"
@@ -125,6 +126,111 @@ typedef struct TransSeq {
        int len;
 } TransSeq;
 
+/* ********************************************************************** */
+
+/* ***************** proxy job manager ********************** */
+
+typedef struct ProxyBuildJob {
+       Scene *scene; 
+       struct Main * main;
+       ListBase queue;
+       ThreadMutex queue_lock;
+} ProxyJob;
+
+static void proxy_freejob(void *pjv)
+{
+       ProxyJob *pj= pjv;
+       Sequence * seq;
+
+       for (seq = pj->queue.first; seq; seq = seq->next) {
+               BLI_remlink(&pj->queue, seq);
+               seq_free_sequence_recurse(pj->scene, seq);
+       }
+
+       BLI_mutex_end(&pj->queue_lock);
+
+       MEM_freeN(pj);
+}
+
+/* only this runs inside thread */
+static void proxy_startjob(void *pjv, short *stop, short *do_update, float *progress)
+{
+       ProxyJob *pj = pjv;
+
+       while (!*stop) {
+               Sequence * seq;
+
+               BLI_mutex_lock(&pj->queue_lock);
+               
+               if (!pj->queue.first) {
+                       BLI_mutex_unlock(&pj->queue_lock);
+                       break;
+               }
+
+               seq = pj->queue.first;
+
+               BLI_remlink(&pj->queue, seq);
+               BLI_mutex_unlock(&pj->queue_lock);
+
+               seq_proxy_rebuild(pj->main, pj->scene, seq, 
+                                 stop, do_update, progress);
+               seq_free_sequence_recurse(pj->scene, seq);
+       }
+
+       if (*stop) {
+               fprintf(stderr, 
+                       "Canceling proxy rebuild on users request...\n");
+       }
+}
+
+static void proxy_endjob(void *UNUSED(customdata))
+{
+
+}
+
+void seq_proxy_build_job(const bContext *C, Sequence * seq)
+{
+       wmJob * steve;
+       ProxyJob *pj;
+       Scene *scene= CTX_data_scene(C);
+       ScrArea * sa= CTX_wm_area(C);
+
+       seq = seq_dupli_recursive(scene, scene, seq, 0);
+
+       steve = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), 
+                           sa, "Building Proxies", WM_JOB_PROGRESS);
+
+       pj = WM_jobs_get_customdata(steve);
+
+       if (!pj) {
+               pj = MEM_callocN(sizeof(ProxyJob), "proxy rebuild job");
+       
+               pj->scene= scene;
+               pj->main = CTX_data_main(C);
+
+               BLI_mutex_init(&pj->queue_lock);
+
+               WM_jobs_customdata(steve, pj, proxy_freejob);
+               WM_jobs_timer(steve, 0.1, NC_SCENE|ND_SEQUENCER,
+                             NC_SCENE|ND_SEQUENCER);
+               WM_jobs_callbacks(steve, proxy_startjob, NULL, NULL, 
+                                 proxy_endjob);
+       }
+
+       BLI_mutex_lock(&pj->queue_lock);
+       BLI_addtail(&pj->queue, seq);
+       BLI_mutex_unlock(&pj->queue_lock);
+
+       if (!WM_jobs_is_running(steve)) {
+               G.afbreek = 0;
+               WM_jobs_start(CTX_wm_manager(C), steve);
+       }
+
+       ED_area_tag_redraw(CTX_wm_area(C));
+}
+
+/* ********************************************************************** */
+
 void seq_rectf(Sequence *seq, rctf *rectf)
 {
        if(seq->startstill) rectf->xmin= seq->start;
@@ -2690,6 +2796,37 @@ void SEQUENCER_OT_view_ghost_border(wmOperatorType *ot)
        WM_operator_properties_gesture_border(ot, FALSE);
 }
 
+/* rebuild_proxy operator */
+static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
+{
+       Scene *scene = CTX_data_scene(C);
+       Editing *ed = seq_give_editing(scene, FALSE);
+       Sequence * seq;
+
+       SEQP_BEGIN(ed, seq) {
+               if ((seq->flag & SELECT)) {
+                       seq_proxy_build_job(C, seq);
+               }
+       }
+       SEQ_END
+               
+       return OPERATOR_FINISHED;
+}
+
+void SEQUENCER_OT_rebuild_proxy(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Rebuild Proxy and Timecode Indices";
+       ot->idname= "SEQUENCER_OT_rebuild_proxy";
+       ot->description="Rebuild all selected proxies and timecode indeces using the job system";
+       
+       /* api callbacks */
+       ot->exec= sequencer_rebuild_proxy_exec;
+       ot->poll= ED_operator_sequencer_active;
+       
+       /* flags */
+       ot->flag= OPTYPE_REGISTER;
+}
 
 /* change ops */
 
index 7ab76f9b6d7e6043b2907009e52d6c1392dbfcdc..89e9a22c9a1cea7e08ca7612c40f3358060f61c1 100644 (file)
@@ -118,6 +118,8 @@ void SEQUENCER_OT_change_path(struct wmOperatorType *ot);
 void SEQUENCER_OT_copy(struct wmOperatorType *ot);
 void SEQUENCER_OT_paste(struct wmOperatorType *ot);
 
+void SEQUENCER_OT_rebuild_proxy(struct wmOperatorType *ot);
+
 /* preview specific operators */
 void SEQUENCER_OT_view_all_preview(struct wmOperatorType *ot);
 
index df33ce73b9c3879f0792768834a78eb9c4eb534f..5c13b57cca8fcc1cf9d37dacf8546f69bcb7c3b9 100644 (file)
@@ -87,6 +87,7 @@ void sequencer_operatortypes(void)
        WM_operatortype_append(SEQUENCER_OT_view_zoom_ratio);
        WM_operatortype_append(SEQUENCER_OT_view_ghost_border);
 
+       WM_operatortype_append(SEQUENCER_OT_rebuild_proxy);
        WM_operatortype_append(SEQUENCER_OT_change_effect_input);
        WM_operatortype_append(SEQUENCER_OT_change_effect_type);
        WM_operatortype_append(SEQUENCER_OT_change_path);
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..1fbe8e01fd4137d5128e05132c8076585947531c 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,18 +193,71 @@ 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_100 = 8,
+       IMB_PROXY_MAX_SLOT = 4
+} IMB_Proxy_Size;
+
+/* defaults to BL_proxy within the directory of the animation */
+void IMB_anim_set_index_dir(struct anim * anim, const char * dir);
+
+int IMB_anim_index_get_frame_index(struct anim * anim, IMB_Timecode_Type tc,
+                                  int position);
+
+/* will rebuild all used indices and proxies at once */
+void IMB_anim_index_rebuild(struct anim * anim, 
+                           IMB_Timecode_Type build_tcs,
+                           IMB_Proxy_Size build_preview_sizes, 
+                           int build_quality,
+                           short *stop, short *do_update, float *progress);
+
 /**
  * Return the length (in frames) of the given @a anim.
  */
-int IMB_anim_get_duration(struct anim *anim);
+int IMB_anim_get_duration(struct anim *anim, IMB_Timecode_Type tc);
+
+
+/**
+ * Return the fps contained in movie files (function rval is FALSE,
+ * and frs_sec and frs_sec_base untouched if none available!)
+ */
+int IMB_anim_get_fps(struct anim * anim, 
+                    short * frs_sec, float * frs_sec_base);
 
 /**
  *
  * @attention Defined in anim.c
  */
-struct anim *IMB_open_anim(const char *name, int ib_flags);
+struct anim *IMB_open_anim(const char *name, int ib_flags, int streamindex);
 void IMB_close_anim(struct anim *anim);
 
+
 /**
  *
  * @attention Defined in anim.c
@@ -218,7 +272,10 @@ int IMB_anim_get_preseek(struct anim *anim);
  * @attention Defined in anim.c
  */
 
-struct ImBuf *IMB_anim_absolute(struct anim *anim, int position);
+struct ImBuf *IMB_anim_absolute(
+       struct anim *anim, int position,
+       IMB_Timecode_Type tc        /* = 1 = IMB_TC_RECORD_RUN */, 
+       IMB_Proxy_Size preview_size /* = 0 = IMB_PROXY_NONE */);
 
 /**
  *
@@ -227,12 +284,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..8436846bf2e091606fea85a15a11e035e74e8642 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;
@@ -148,9 +151,7 @@ struct anim {
        size_t framesize;
        int interlacing;
        int preseek;
-       
-               /* data */
-       struct ImBuf * ibuf1, * ibuf2;
+       int streamindex;
        
                /* avi */
        struct _AviMovie *avi;
@@ -179,11 +180,26 @@ struct anim {
        AVFrame *pFrameDeinterlaced;
        struct SwsContext *img_convert_ctx;
        int videoStream;
+
+       struct ImBuf * last_frame;
+       int64_t last_pts;
+       int64_t next_pts;
+       int64_t next_undecoded_pts;
+       AVPacket next_packet;
 #endif
 
 #ifdef WITH_REDCODE
        struct redcode_handle * redcodeCtx;
 #endif
+
+       char index_dir[256];
+
+       int proxies_tried;
+       int indices_tried;
+       
+       struct anim * proxy_anim[IMB_PROXY_MAX_SLOT];
+       struct anim_index * curr_idx[IMB_TC_MAX_SLOT];
+
 };
 
 #endif
diff --git a/source/blender/imbuf/intern/IMB_indexer.h b/source/blender/imbuf/intern/IMB_indexer.h
new file mode 100644 (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 59772771f3b3359c61899c07a9401f60d4ce39fe..6ce6c9409d1b4e386f3342f322656ca94a2a0e97 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 8b0104fcdca56994ab5f99a824e554bac6020948..7b172008bee7282e153050529c889a0780c566e8 100644 (file)
@@ -57,6 +57,7 @@
 #include <ctype.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <math.h>
 #ifndef _WIN32
 #include <dirent.h>
 #else
@@ -66,6 +67,7 @@
 #include "BLI_blenlib.h" /* BLI_remlink BLI_filesize BLI_addtail
                                                        BLI_countlist BLI_stringdec */
 #include "BLI_utildefines.h"
+#include "BLI_math_base.h"
 
 #include "MEM_guardedalloc.h"
 
@@ -90,6 +92,7 @@
 
 #include "IMB_allocimbuf.h"
 #include "IMB_anim.h"
+#include "IMB_indexer.h"
 
 #ifdef WITH_FFMPEG
 #include <libavformat/avformat.h>
@@ -304,15 +307,6 @@ static void free_anim_avi (struct anim *anim) {
        anim->duration = 0;
 }
 
-void IMB_free_anim_ibuf(struct anim * anim) {
-       if (anim == NULL) return;
-
-       if (anim->ibuf1) IMB_freeImBuf(anim->ibuf1);
-       if (anim->ibuf2) IMB_freeImBuf(anim->ibuf2);
-
-       anim->ibuf1 = anim->ibuf2 = NULL;
-}
-
 #ifdef WITH_FFMPEG
 static void free_anim_ffmpeg(struct anim * anim);
 #endif
@@ -326,7 +320,6 @@ void IMB_free_anim(struct anim * anim) {
                return;
        }
 
-       IMB_free_anim_ibuf(anim);
        free_anim_movie(anim);
        free_anim_avi(anim);
 
@@ -339,6 +332,7 @@ void IMB_free_anim(struct anim * anim) {
 #ifdef WITH_REDCODE
        free_anim_redcode(anim);
 #endif
+       IMB_free_indices(anim);
 
        MEM_freeN(anim);
 }
@@ -350,13 +344,14 @@ void IMB_close_anim(struct anim * anim) {
 }
 
 
-struct anim * IMB_open_anim( const char * name, int ib_flags) {
+struct anim * IMB_open_anim( const char * name, int ib_flags, int streamindex) {
        struct anim * anim;
 
        anim = (struct anim*)MEM_callocN(sizeof(struct anim), "anim struct");
        if (anim != NULL) {
                BLI_strncpy(anim->name, name, sizeof(anim->name));
                anim->ib_flags = ib_flags;
+               anim->streamindex = streamindex;
        }
        return(anim);
 }
@@ -368,10 +363,13 @@ static int startavi (struct anim *anim) {
 #if defined(_WIN32) && !defined(FREE_WINDOWS)
        HRESULT hr;
        int i, firstvideo = -1;
+       int streamcount;
        BYTE abFormat[1024];
        LONG l;
        LPBITMAPINFOHEADER lpbi;
        AVISTREAMINFO avis;
+
+       streamcount = anim->streamindex;
 #endif
 
        anim->avi = MEM_callocN (sizeof(AviMovie),"animavi");
@@ -396,6 +394,10 @@ static int startavi (struct anim *anim) {
                                
                                AVIStreamInfo(anim->pavi[i], &avis, sizeof(avis));
                                if ((avis.fccType == streamtypeVIDEO) && (firstvideo == -1)) {
+                                       if (streamcount > 0) {
+                                               streamcount--;
+                                               continue;
+                                       }
                                        anim->pgf = AVIStreamGetFrameOpen(anim->pavi[i], NULL);
                                        if (anim->pgf) {
                                                firstvideo = i;
@@ -496,14 +498,14 @@ static ImBuf * avi_fetchibuf (struct anim *anim, int position) {
 
                for (y=0; y < anim->y; y++) {
                        memcpy (&(ibuf->rect)[((anim->y-y)-1)*anim->x],  &tmp[y*anim->x],  
-                                       anim->x * 4);
+                               anim->x * 4);
                }
                
                MEM_freeN (tmp);
        }
-
+       
        ibuf->profile = IB_PROFILE_SRGB;
-               
+       
        return ibuf;
 }
 
@@ -517,6 +519,9 @@ static int startffmpeg(struct anim * anim) {
        AVCodec *pCodec;
        AVFormatContext *pFormatCtx;
        AVCodecContext *pCodecCtx;
+       int frs_num;
+       double frs_den;
+       int streamcount;
 
 #ifdef FFMPEG_SWSCALE_COLOR_SPACE_SUPPORT
        /* The following for color space determination */
@@ -527,6 +532,8 @@ static int startffmpeg(struct anim * anim) {
 
        if (anim == 0) return(-1);
 
+       streamcount = anim->streamindex;
+
        do_init_ffmpeg();
 
        if(av_open_input_file(&pFormatCtx, anim->name, NULL, 0, NULL)!=0) {
@@ -541,12 +548,17 @@ static int startffmpeg(struct anim * anim) {
        av_dump_format(pFormatCtx, 0, anim->name, 0);
 
 
-               /* Find the first video stream */
-       videoStream=-1;
-       for(i=0; i<pFormatCtx->nb_streams; i++)
-               if(pFormatCtx->streams[i]->codec->codec_type
+       /* Find the video stream */
+       videoStream = -1;
+
+       for(i = 0; i < pFormatCtx->nb_streams; i++)
+               if (pFormatCtx->streams[i]->codec->codec_type
                   == AVMEDIA_TYPE_VIDEO) {
-                       videoStream=i;
+                       if (streamcount > 0) {
+                               streamcount--;
+                               continue;
+                       }
+                       videoStream = i;
                        break;
                }
 
@@ -557,16 +569,16 @@ static int startffmpeg(struct anim * anim) {
 
        pCodecCtx = pFormatCtx->streams[videoStream]->codec;
 
-               /* Find the decoder for the video stream */
-       pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
-       if(pCodec==NULL) {
+       /* Find the decoder for the video stream */
+       pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
+       if(pCodec == NULL) {
                av_close_input_file(pFormatCtx);
                return -1;
        }
 
        pCodecCtx->workaround_bugs = 1;
 
-       if(avcodec_open(pCodecCtx, pCodec)<0) {
+       if(avcodec_open(pCodecCtx, pCodec) < 0) {
                av_close_input_file(pFormatCtx);
                return -1;
        }
@@ -575,6 +587,19 @@ static int startffmpeg(struct anim * anim) {
                * av_q2d(pFormatCtx->streams[videoStream]->r_frame_rate) 
                / AV_TIME_BASE);
 
+       frs_num = pFormatCtx->streams[videoStream]->r_frame_rate.num;
+       frs_den = pFormatCtx->streams[videoStream]->r_frame_rate.den;
+
+       frs_den *= AV_TIME_BASE;
+
+       while (frs_num % 10 == 0 && frs_den >= 2.0 && frs_num > 10) {
+               frs_num /= 10;
+               frs_den /= 10;
+       }
+
+       anim->frs_sec = frs_num;
+       anim->frs_sec_base = frs_den;
+
        anim->params = 0;
 
        anim->x = pCodecCtx->width;
@@ -584,6 +609,11 @@ static int startffmpeg(struct anim * anim) {
        anim->framesize = anim->x * anim->y * 4;
 
        anim->curposition = -1;
+       anim->last_frame = 0;
+       anim->last_pts = -1;
+       anim->next_pts = -1;
+       anim->next_undecoded_pts = -1;
+       anim->next_packet.stream_index = -1;
 
        anim->pFormatCtx = pFormatCtx;
        anim->pCodecCtx = pCodecCtx;
@@ -666,10 +696,19 @@ static int startffmpeg(struct anim * anim) {
        return (0);
 }
 
-static void ffmpeg_postprocess(struct anim * anim, ImBuf * ibuf,
-                              int * filter_y)
+/* postprocess the image in anim->pFrame and do color conversion
+   and deinterlacing stuff.
+
+   Output is anim->last_frame
+*/
+
+static void ffmpeg_postprocess(struct anim * anim)
 {
        AVFrame * input = anim->pFrame;
+       ImBuf * ibuf = anim->last_frame;
+       int filter_y = 0;
+
+       ibuf->profile = IB_PROFILE_SRGB;
 
        /* This means the data wasnt read properly, 
           this check stops crashing */
@@ -690,12 +729,16 @@ static void ffmpeg_postprocess(struct anim * anim, ImBuf * ibuf,
                            anim->pCodecCtx->width,
                            anim->pCodecCtx->height)
                    < 0) {
-                       *filter_y = 1;
+                       filter_y = TRUE;
                } else {
                        input = anim->pFrameDeinterlaced;
                }
        }
        
+       avpicture_fill((AVPicture*) anim->pFrameRGB, 
+                      (unsigned char*) ibuf->rect, 
+                      PIX_FMT_RGBA, anim->x, anim->y);
+
        if (ENDIAN_ORDER == B_ENDIAN) {
                int * dstStride   = anim->pFrameRGB->linesize;
                uint8_t** dst     = anim->pFrameRGB->data;
@@ -774,150 +817,359 @@ static void ffmpeg_postprocess(struct anim * anim, ImBuf * ibuf,
                        }
                }
        }
+
+       if (filter_y) {
+               IMB_filtery(ibuf);
+       }
 }
 
-static ImBuf * ffmpeg_fetchibuf(struct anim * anim, int position) {
-       ImBuf * ibuf;
-       int frameFinished;
-       AVPacket packet;
+/* decode one video frame and load the next packet into anim->packet,
+   so that we can obtain next_pts and next undecoded pts */
+
+static int ffmpeg_decode_video_frame(struct anim * anim)
+{
+       int frameFinished = 0;
+       int rval = 0;
+
+       av_log(anim->pFormatCtx, AV_LOG_DEBUG, "  DECODE VIDEO FRAME\n");
+
+       anim->next_undecoded_pts = -1;
+
+       if (anim->next_packet.stream_index == anim->videoStream) {
+               av_log(anim->pFormatCtx, AV_LOG_DEBUG, 
+                      "  DECODE: cached next packet\n");
+
+               avcodec_decode_video2(anim->pCodecCtx, 
+                                     anim->pFrame, &frameFinished, 
+                                     &anim->next_packet);
+
+               if (frameFinished) {
+                       av_log(anim->pFormatCtx, 
+                              AV_LOG_DEBUG, 
+                              "  FRAME DONE: "
+                               "next_pts=%lld pkt_pts=%lld\n",
+                               (anim->pFrame->pts == AV_NOPTS_VALUE) ? 
+                               -1 : anim->pFrame->pts, 
+                               (anim->pFrame->pkt_pts == AV_NOPTS_VALUE) ?
+                               -1 : anim->pFrame->pkt_pts);
+                       anim->next_pts = 
+                               av_get_pts_from_frame(anim->pFormatCtx,
+                                                     anim->pFrame);
+               }
+
+               av_free_packet(&anim->next_packet);
+               anim->next_packet.stream_index = -1;
+       }
+       
+       while((rval = av_read_frame(anim->pFormatCtx, &anim->next_packet)) >= 0) {
+               av_log(anim->pFormatCtx, 
+                      AV_LOG_DEBUG, 
+                      "%sREAD: strID=%d (VID: %d) dts=%lld pts=%lld "
+                      "%s\n",
+                      (anim->next_packet.stream_index == anim->videoStream)
+                      ? "->" : "  ",
+                      anim->next_packet.stream_index, 
+                      anim->videoStream,
+                      (anim->next_packet.dts == AV_NOPTS_VALUE) ? -1:
+                      anim->next_packet.dts,
+                      (anim->next_packet.pts == AV_NOPTS_VALUE) ? -1:
+                      anim->next_packet.pts,
+                      (anim->next_packet.flags & AV_PKT_FLAG_KEY) ? 
+                      " KEY" : "");
+               if (anim->next_packet.stream_index == anim->videoStream) {
+                       if (frameFinished) {
+                               av_log(anim->pFormatCtx,
+                                      AV_LOG_DEBUG,
+                                      "  FRAME finished, we leave\n");
+                               anim->next_undecoded_pts 
+                                       = anim->next_packet.dts;
+                               break;
+                       }
+
+                       avcodec_decode_video2(
+                               anim->pCodecCtx, 
+                               anim->pFrame, &frameFinished, 
+                               &anim->next_packet);
+
+                       if (frameFinished) {
+                               anim->next_pts = av_get_pts_from_frame(
+                                       anim->pFormatCtx, anim->pFrame);
+
+                               av_log(anim->pFormatCtx,
+                                      AV_LOG_DEBUG,
+                                      "  FRAME DONE: next_pts=%lld "
+                                      "pkt_pts=%lld, guessed_pts=%lld\n",
+                                      (anim->pFrame->pts == AV_NOPTS_VALUE) ?
+                                      -1 : anim->pFrame->pts, 
+                                      (anim->pFrame->pkt_pts 
+                                       == AV_NOPTS_VALUE) ?
+                                      -1 : anim->pFrame->pkt_pts,
+                                       anim->next_pts);
+                       }
+               }
+               av_free_packet(&anim->next_packet);
+               anim->next_packet.stream_index = -1;
+       }
+       
+       if (rval < 0) {
+               av_log(anim->pFormatCtx,
+                      AV_LOG_ERROR, "  DECODE READ FAILED: av_read_frame() "
+                      "returned error: %d\n",  rval);
+       }
+       return (rval >= 0);
+}
+
+static void ffmpeg_decode_video_frame_scan(
+       struct anim * anim, int64_t pts_to_search)
+{
+        /* there seem to exist *very* silly GOP lengths out in the wild... */
+       int count = 1000;
+
+       av_log(anim->pFormatCtx,
+              AV_LOG_DEBUG, 
+              "SCAN start: considering pts=%lld in search of %lld\n", 
+              anim->next_pts, pts_to_search);
+
+       while (count > 0 && anim->next_pts < pts_to_search) {
+               av_log(anim->pFormatCtx,
+                      AV_LOG_DEBUG, 
+                      "  WHILE: pts=%lld in search of %lld\n", 
+                      anim->next_pts, pts_to_search);
+               if (!ffmpeg_decode_video_frame(anim)) {
+                       break;
+               }
+               count--;
+       }
+       if (count == 0) {
+               av_log(anim->pFormatCtx,
+                      AV_LOG_ERROR, 
+                      "SCAN failed: completely lost in stream, "
+                      "bailing out at PTS=%lld, searching for PTS=%lld\n", 
+                      anim->next_pts, pts_to_search);
+       }
+       if (anim->next_pts == pts_to_search) {
+               av_log(anim->pFormatCtx,
+                      AV_LOG_DEBUG, "SCAN HAPPY: we found our PTS!\n");
+       } else {
+               av_log(anim->pFormatCtx,
+                      AV_LOG_ERROR, "SCAN UNHAPPY: PTS not matched!\n");
+       }
+}
+
+static int match_format(const char *name, AVFormatContext * pFormatCtx)
+{
+       const char *p;
+       int len, namelen;
+
+       const char *names = pFormatCtx->iformat->name;
+
+       if (!name || !names)
+               return 0;
+
+       namelen = strlen(name);
+       while ((p = strchr(names, ','))) {
+               len = MAX2(p - names, namelen);
+               if (!BLI_strncasecmp(name, names, len))
+                       return 1;
+               names = p+1;
+       }
+       return !BLI_strcasecmp(name, names);
+}
+
+static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx)
+{
+       static const char * byte_seek_list [] = { "dv", "mpegts", 0 };
+       const char ** p;
+
+       if (pFormatCtx->iformat->flags & AVFMT_TS_DISCONT) {
+               return TRUE;
+       }
+
+       p = byte_seek_list;
+
+       while (*p) {
+               if (match_format(*p++, pFormatCtx)) {
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+static ImBuf * ffmpeg_fetchibuf(struct anim * anim, int position,
+                               IMB_Timecode_Type tc) {
        int64_t pts_to_search = 0;
-       int pos_found = 1;
-       int filter_y = 0;
-       int seek_by_bytes= 0;
-       int preseek_count = 0;
+       double frame_rate;
+       double pts_time_base;
+       long long st_time; 
+       struct anim_index * tc_index = 0;
+       AVStream * v_st;
+       int new_frame_index;
+       int old_frame_index;
 
        if (anim == 0) return (0);
 
-       ibuf = IMB_allocImBuf(anim->x, anim->y, 32, IB_rect);
+       av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: pos=%d\n", position);
 
-       avpicture_fill((AVPicture*) anim->pFrameRGB, 
-                          (unsigned char*) ibuf->rect, 
-                          PIX_FMT_RGBA, anim->x, anim->y);
-
-       if (position != anim->curposition + 1) { 
-               if (position > anim->curposition + 1 
-                       && anim->preseek 
-                       && position - (anim->curposition + 1) < anim->preseek) {
-                       while(av_read_frame(anim->pFormatCtx, &packet)>=0) {
-                               if (packet.stream_index == anim->videoStream) {
-                                       avcodec_decode_video2(
-                                               anim->pCodecCtx, 
-                                               anim->pFrame, &frameFinished, 
-                                               &packet);
-
-                                       if (frameFinished) {
-                                               anim->curposition++;
-                                       }
-                               }
-                               av_free_packet(&packet);
-                               if (position == anim->curposition+1) {
-                                       break;
-                               }
-                       }
+       if (tc != IMB_TC_NONE) {
+               tc_index = IMB_anim_open_index(anim, tc);
+       }
+
+       v_st = anim->pFormatCtx->streams[anim->videoStream];
+
+       frame_rate = av_q2d(v_st->r_frame_rate);
+
+       st_time = anim->pFormatCtx->start_time;
+       pts_time_base = av_q2d(v_st->time_base);
+
+       if (tc_index) {
+               new_frame_index = IMB_indexer_get_frame_index(
+                       tc_index, position);
+               old_frame_index = IMB_indexer_get_frame_index(
+                       tc_index, anim->curposition);
+               pts_to_search = IMB_indexer_get_pts(
+                       tc_index, new_frame_index);
+       } else {
+               pts_to_search = (long long) 
+                       floor(((double) position) / pts_time_base / frame_rate + 0.5);
+
+               if (st_time != AV_NOPTS_VALUE) {
+                       pts_to_search += st_time / pts_time_base 
+                               / AV_TIME_BASE;
                }
        }
 
-/* disable seek_by_bytes for now, since bitrates are guessed wrong!
-   also: MPEG2TS-seeking was fixed in later versions of ffmpeg, so problem
-   is somewhat fixed by now (until we add correct timecode management code...)
-*/
-#if 0
-       seek_by_bytes = !!(anim->pFormatCtx->iformat->flags & AVFMT_TS_DISCONT);
-#else
-       seek_by_bytes = FALSE;
-#endif
+       av_log(anim->pFormatCtx, AV_LOG_DEBUG, 
+              "FETCH: looking for PTS=%lld "
+              "(pts_timebase=%g, frame_rate=%g, st_time=%lld)\n", 
+              pts_to_search, pts_time_base, frame_rate, st_time);
+
+       if (anim->last_frame && 
+           anim->last_pts <= pts_to_search && anim->next_pts > pts_to_search){
+               av_log(anim->pFormatCtx, AV_LOG_DEBUG, 
+                      "FETCH: frame repeat: last: %lld next: %lld\n",
+                      anim->last_pts, anim->next_pts);
+               IMB_refImBuf(anim->last_frame);
+               anim->curposition = position;
+               return anim->last_frame;
+       }
+        
+       IMB_freeImBuf(anim->last_frame);
+
+       if (anim->next_pts <= pts_to_search && 
+           anim->next_undecoded_pts > pts_to_search) {
+               av_log(anim->pFormatCtx, AV_LOG_DEBUG, 
+                      "FETCH: no seek necessary: "
+                       "next: %lld next undecoded: %lld\n",
+                       anim->next_pts, anim->next_undecoded_pts);
+
+               /* we are already done :) */
+
+       } else if (position > anim->curposition + 1 
+                  && anim->preseek 
+                  && !tc_index
+                  && position - (anim->curposition + 1) < anim->preseek) {
+
+               av_log(anim->pFormatCtx, AV_LOG_DEBUG, 
+                      "FETCH: within preseek interval (no index)\n");
+
+               ffmpeg_decode_video_frame_scan(anim, pts_to_search);
+       } else if (tc_index && 
+                  IMB_indexer_can_scan(tc_index, old_frame_index,
+                                       new_frame_index)) {
+
+               av_log(anim->pFormatCtx, AV_LOG_DEBUG, 
+                      "FETCH: within preseek interval "
+                      "(index tells us)\n");
 
-       if (position != anim->curposition + 1) { 
-               double frame_rate = 
-                       av_q2d(anim->pFormatCtx->streams[anim->videoStream]
-                                  ->r_frame_rate);
-               double pts_time_base = av_q2d(anim->pFormatCtx->streams[anim->videoStream]->time_base);
+               ffmpeg_decode_video_frame_scan(anim, pts_to_search);
+       } else if (position != anim->curposition + 1) { 
                long long pos;
-               long long st_time = anim->pFormatCtx->start_time;
                int ret;
 
-               if (seek_by_bytes) {
-                       pos = position - anim->preseek;
-                       if (pos < 0) {
-                               pos = 0;
-                       }
-                       preseek_count = position - pos;
+               if (tc_index) {
+                       unsigned long long dts;
+
+                       pos = IMB_indexer_get_seek_pos(
+                               tc_index, new_frame_index);
+                       dts = IMB_indexer_get_seek_pos_dts(
+                               tc_index, new_frame_index);
+
+                       av_log(anim->pFormatCtx, AV_LOG_DEBUG, 
+                              "TC INDEX seek pos = %lld\n", pos);
+                       av_log(anim->pFormatCtx, AV_LOG_DEBUG, 
+                              "TC INDEX seek dts = %lld\n", dts);
 
-                       pos *= anim->pFormatCtx->bit_rate / frame_rate;
-                       pos /= 8;
+                       if (ffmpeg_seek_by_byte(anim->pFormatCtx)) {
+                               av_log(anim->pFormatCtx, AV_LOG_DEBUG, 
+                                      "... using BYTE pos\n");
+
+                               ret = av_seek_frame(anim->pFormatCtx, 
+                                                   -1,
+                                                   pos, AVSEEK_FLAG_BYTE);
+                               av_update_cur_dts(anim->pFormatCtx, v_st, dts);
+                       } else {
+                               av_log(anim->pFormatCtx, AV_LOG_DEBUG, 
+                                      "... using DTS pos\n");
+                               ret = av_seek_frame(anim->pFormatCtx, 
+                                                   anim->videoStream, 
+                                                   dts, AVSEEK_FLAG_BACKWARD);
+                       }
                } else {
                        pos = (long long) (position - anim->preseek) 
                                * AV_TIME_BASE / frame_rate;
                        if (pos < 0) {
                                pos = 0;
                        }
-
+               
                        if (st_time != AV_NOPTS_VALUE) {
                                pos += st_time;
                        }
-               }
 
-               ret = av_seek_frame(anim->pFormatCtx, -1, 
-                                   pos, 
-                                   AVSEEK_FLAG_BACKWARD | (
-                                           seek_by_bytes 
-                                           ? AVSEEK_FLAG_ANY 
-                                           | AVSEEK_FLAG_BYTE : 0));
-               if (ret < 0) {
-                       fprintf(stderr, "error while seeking: %d\n", ret);
+                       ret = av_seek_frame(anim->pFormatCtx, -1, 
+                                           pos, AVSEEK_FLAG_BACKWARD);
                }
 
-               pts_to_search = (long long) 
-                       (((double) position) / pts_time_base / frame_rate);
-               if (st_time != AV_NOPTS_VALUE) {
-                       pts_to_search += st_time / pts_time_base/ AV_TIME_BASE;
+               if (ret < 0) {
+                       av_log(anim->pFormatCtx, AV_LOG_ERROR, 
+                              "FETCH: "
+                              "error while seeking to DTS = %lld "
+                              "(frameno = %d, PTS = %lld): errcode = %d\n", 
+                              pos, position, pts_to_search, ret);
                }
 
-               pos_found = 0;
                avcodec_flush_buffers(anim->pCodecCtx);
-       }
 
-       while(av_read_frame(anim->pFormatCtx, &packet)>=0) {
-               if(packet.stream_index == anim->videoStream) {
-                       avcodec_decode_video2(anim->pCodecCtx, 
-                                             anim->pFrame, &frameFinished, 
-                                             &packet);
+               anim->next_pts = -1;
 
-                       if (seek_by_bytes && preseek_count > 0) {
-                               preseek_count--;
-                       }
+               if (anim->next_packet.stream_index == anim->videoStream) {
+                       av_free_packet(&anim->next_packet);
+                       anim->next_packet.stream_index = -1;
+               }
 
-                       if (frameFinished && !pos_found) {
-                               if (seek_by_bytes) {
-                                       if (!preseek_count) {
-                                               pos_found = 1;
-                                               anim->curposition = position;
-                                       }
-                               } else {
-                                       if (packet.dts >= pts_to_search) {
-                                               pos_found = 1;
-                                               anim->curposition = position;
-                                       }
-                               }
-                       } 
+               /* memset(anim->pFrame,...) ?? */
 
-                       if(frameFinished && pos_found == 1) {
-                               ffmpeg_postprocess(anim, ibuf, &filter_y);
-                               av_free_packet(&packet);
-                               break;
-                       }
+               if (ret >= 0) {
+                       ffmpeg_decode_video_frame_scan(anim, pts_to_search);
                }
-
-               av_free_packet(&packet);
+       } else if (position == 0 && anim->curposition == -1) {
+               /* first frame without seeking special case... */
+               ffmpeg_decode_video_frame(anim);
        }
 
-       if (filter_y && ibuf) {
-               IMB_filtery(ibuf);
-       }
+       anim->last_frame = IMB_allocImBuf(anim->x, anim->y, 32, IB_rect);
 
-       ibuf->profile = IB_PROFILE_SRGB;
+       ffmpeg_postprocess(anim);
        
-       return(ibuf);
+       anim->last_pts = anim->next_pts;
+       
+       ffmpeg_decode_video_frame(anim);
+       
+       anim->curposition = position;
+       
+       IMB_refImBuf(anim->last_frame);
+       
+       return anim->last_frame;
 }
 
 static void free_anim_ffmpeg(struct anim * anim) {
@@ -934,6 +1186,10 @@ static void free_anim_ffmpeg(struct anim * anim) {
                }
                av_free(anim->pFrameDeinterlaced);
                sws_freeContext(anim->img_convert_ctx);
+               IMB_freeImBuf(anim->last_frame);
+               if (anim->next_packet.stream_index != -1) {
+                       av_free_packet(&anim->next_packet);
+               }
        }
        anim->duration = 0;
 }
@@ -1063,16 +1319,19 @@ struct ImBuf * IMB_anim_previewframe(struct anim * anim) {
        struct ImBuf * ibuf = NULL;
        int position = 0;
        
-       ibuf = IMB_anim_absolute(anim, 0);
+       ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
        if (ibuf) {
                IMB_freeImBuf(ibuf);
                position = anim->duration / 2;
-               ibuf = IMB_anim_absolute(anim, position);
+               ibuf = IMB_anim_absolute(anim, position, IMB_TC_NONE,
+                                        IMB_PROXY_NONE);
        }
        return ibuf;
 }
 
-struct ImBuf * IMB_anim_absolute(struct anim * anim, int position) {
+struct ImBuf * IMB_anim_absolute(struct anim * anim, int position,
+                                IMB_Timecode_Type tc,
+                                IMB_Proxy_Size preview_size) {
        struct ImBuf * ibuf = NULL;
        char head[256], tail[256];
        unsigned short digits;
@@ -1095,6 +1354,18 @@ struct ImBuf * IMB_anim_absolute(struct anim * anim, int position) {
        if (position < 0) return(NULL);
        if (position >= anim->duration) return(NULL);
 
+       if (preview_size != IMB_PROXY_NONE) {
+               struct anim * proxy = IMB_anim_open_proxy(anim, preview_size);
+
+               if (proxy) {
+                       position = IMB_anim_index_get_frame_index(
+                               anim, tc, position);
+                       return IMB_anim_absolute(
+                               proxy, position,
+                               IMB_TC_NONE, IMB_PROXY_NONE);
+               }
+       }
+
        switch(anim->curtype) {
        case ANIM_SEQUENCE:
                pic = an_stringdec(anim->first, head, tail, &digits);
@@ -1127,7 +1398,7 @@ struct ImBuf * IMB_anim_absolute(struct anim * anim, int position) {
 #endif
 #ifdef WITH_FFMPEG
        case ANIM_FFMPEG:
-               ibuf = ffmpeg_fetchibuf(anim, position);
+               ibuf = ffmpeg_fetchibuf(anim, position, tc);
                if (ibuf)
                        anim->curposition = position;
                filter_y = 0; /* done internally */
@@ -1151,8 +1422,29 @@ struct ImBuf * IMB_anim_absolute(struct anim * anim, int position) {
 
 /***/
 
-int IMB_anim_get_duration(struct anim *anim) {
-       return anim->duration;
+int IMB_anim_get_duration(struct anim *anim, IMB_Timecode_Type tc) {
+       struct anim_index * idx;
+       if (tc == IMB_TC_NONE) {
+               return anim->duration;
+       }
+       
+       idx = IMB_anim_open_index(anim, tc);
+       if (!idx) {
+               return anim->duration;
+       }
+
+       return IMB_indexer_get_duration(idx);
+}
+
+int IMB_anim_get_fps(struct anim * anim, 
+                    short * frs_sec, float * frs_sec_base)
+{
+       if (anim->frs_sec) {
+               *frs_sec = anim->frs_sec;
+               *frs_sec_base = anim->frs_sec_base;
+               return TRUE;
+       }
+       return FALSE;
 }
 
 void IMB_anim_set_preseek(struct anim * anim, int preseek)
index 7f1eef803186dc04bb83b4862a4cb97919c1240d..2677913caed5e6e0200f3af9d0f7c68f6e08644f 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ *
+ * $Id$
  *
  * ***** BEGIN GPL LICENSE BLOCK *****
  *
diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c
new file mode 100644 (file)
index 0000000..f624694
--- /dev/null
@@ -0,0 +1,1135 @@
+/*
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+  * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Peter Schlaile <peter [at] schlaile [dot] de> 2011
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+*/
+
+#include "IMB_indexer.h"
+#include "IMB_anim.h"
+#include "AVI_avi.h"
+#include "imbuf.h"
+#include "MEM_guardedalloc.h"
+#include "BLI_utildefines.h"
+#include "BLI_blenlib.h"
+#include "BLI_math_base.h"
+#include "BLI_string.h"
+#include "MEM_guardedalloc.h"
+#include "DNA_userdef_types.h"
+#include "BKE_global.h"
+#include <stdlib.h>
+
+#ifdef WITH_FFMPEG
+
+#include "ffmpeg_compat.h"
+
+#endif //WITH_FFMPEG
+
+
+static char magic[] = "BlenMIdx";
+static char temp_ext [] = "_part";
+
+static int proxy_sizes[] = { IMB_PROXY_25, IMB_PROXY_50, IMB_PROXY_75,
+                            IMB_PROXY_100 };
+static float proxy_fac[] = { 0.25, 0.50, 0.75, 1.00 };
+static int tc_types[] = { IMB_TC_RECORD_RUN, IMB_TC_FREE_RUN,
+                         IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN };
+
+#define INDEX_FILE_VERSION 1
+
+/* ---------------------------------------------------------------------- 
+   - special indexers
+   ---------------------------------------------------------------------- 
+ */
+
+extern void IMB_indexer_dv_new(anim_index_builder * idx);
+
+
+/* ----------------------------------------------------------------------
+   - time code index functions
+   ---------------------------------------------------------------------- */
+
+anim_index_builder * IMB_index_builder_create(const char * name)
+{
+
+       anim_index_builder * rv 
+               = MEM_callocN( sizeof(struct anim_index_builder), 
+                              "index builder");
+
+       fprintf(stderr, "Starting work on index: %s\n", name);
+
+       BLI_strncpy(rv->name, name, sizeof(rv->name));
+       BLI_strncpy(rv->temp_name, name, sizeof(rv->temp_name));
+
+       strcat(rv->temp_name, temp_ext);
+
+       BLI_make_existing_file(rv->temp_name);
+
+       rv->fp = fopen(rv->temp_name, "w");
+
+       if (!rv->fp) {
+               fprintf(stderr, "Couldn't open index target: %s! "
+                       "Index build broken!\n", rv->temp_name);
+               MEM_freeN(rv);
+               return NULL;
+       }
+
+       fprintf(rv->fp, "%s%c%.3d", magic, (ENDIAN_ORDER==B_ENDIAN)?'V':'v',
+               INDEX_FILE_VERSION);
+
+       return rv;
+}
+
+void IMB_index_builder_add_entry(anim_index_builder * fp, 
+                                int frameno,unsigned long long seek_pos,
+                                unsigned long long seek_pos_dts,
+                                unsigned long long pts)
+{
+       fwrite(&frameno, sizeof(int), 1, fp->fp);
+       fwrite(&seek_pos, sizeof(unsigned long long), 1, fp->fp);
+       fwrite(&seek_pos_dts, sizeof(unsigned long long), 1, fp->fp);
+       fwrite(&pts, sizeof(unsigned long long), 1, fp->fp);
+}
+
+void IMB_index_builder_proc_frame(anim_index_builder * fp, 
+                                 unsigned char * buffer,
+                                 int data_size,
+                                 int frameno, unsigned long long seek_pos,
+                                 unsigned long long seek_pos_dts,
+                                 unsigned long long pts)
+{
+       if (fp->proc_frame) {
+               anim_index_entry e;
+               e.frameno = frameno;
+               e.seek_pos = seek_pos;
+               e.seek_pos_dts = seek_pos_dts;
+               e.pts = pts;
+
+               fp->proc_frame(fp, buffer, data_size, &e);
+       } else {
+               IMB_index_builder_add_entry(fp, frameno, seek_pos,
+                                           seek_pos_dts, pts);
+       }
+}
+
+void IMB_index_builder_finish(anim_index_builder * fp, int rollback)
+{
+       if (fp->delete_priv_data) {
+               fp->delete_priv_data(fp);
+       }
+
+       fclose(fp->fp);
+       
+       if (rollback) {
+               unlink(fp->temp_name);
+       } else {
+               rename(fp->temp_name, fp->name);
+       }
+
+       MEM_freeN(fp);
+}
+
+struct anim_index * IMB_indexer_open(const char * name)
+{
+       char header[13];
+       struct anim_index * idx;
+       FILE * fp = fopen(name, "rb");
+       int i;
+
+       if (!fp) {
+               return 0;
+       }
+
+       if (fread(header, 12, 1, fp) != 1) {
+               fclose(fp);
+               return 0;
+       }
+
+       header[12] = 0;
+
+       if (memcmp(header, magic, 8) != 0) {
+               fclose(fp);
+               return 0;
+       }
+
+       if (atoi(header+9) != INDEX_FILE_VERSION) {
+               fclose(fp);
+               return 0;
+       }
+
+       idx = MEM_callocN( sizeof(struct anim_index), "anim_index");
+
+       BLI_strncpy(idx->name, name, sizeof(idx->name));
+       
+               fseek(fp, 0, SEEK_END);
+
+       idx->num_entries = (ftell(fp) - 12) 
+               / (sizeof(int) // framepos
+                  + sizeof(unsigned long long) // seek_pos
+                  + sizeof(unsigned long long) // seek_pos_dts
+                  + sizeof(unsigned long long) // pts
+                       );
+       
+       fseek(fp, 12, SEEK_SET);
+
+       idx->entries = MEM_callocN( sizeof(struct anim_index_entry) 
+                                   * idx->num_entries, "anim_index_entries");
+
+       for (i = 0; i < idx->num_entries; i++) {
+               fread(&idx->entries[i].frameno, 
+                     sizeof(int), 1, fp);
+               fread(&idx->entries[i].seek_pos, 
+                     sizeof(unsigned long long), 1, fp);
+               fread(&idx->entries[i].seek_pos_dts, 
+                     sizeof(unsigned long long), 1, fp);
+               fread(&idx->entries[i].pts, 
+                     sizeof(unsigned long long), 1, fp);
+       }
+
+       if (((ENDIAN_ORDER == B_ENDIAN) != (header[8] == 'V'))) {
+               for (i = 0; i < idx->num_entries; i++) {
+                       SWITCH_INT(idx->entries[i].frameno);
+                       SWITCH_INT64(idx->entries[i].seek_pos);
+                       SWITCH_INT64(idx->entries[i].seek_pos_dts);
+                       SWITCH_INT64(idx->entries[i].pts);
+               }
+       }
+
+       fclose(fp);
+
+       return idx;
+}
+
+unsigned long long IMB_indexer_get_seek_pos(
+       struct anim_index * idx, int frame_index)
+{
+       if (frame_index < 0) {
+               frame_index = 0;
+       }
+       if (frame_index >= idx->num_entries) {
+               frame_index = idx->num_entries - 1;
+       }
+       return idx->entries[frame_index].seek_pos;
+}
+
+unsigned long long IMB_indexer_get_seek_pos_dts(
+       struct anim_index * idx, int frame_index)
+{
+       if (frame_index < 0) {
+               frame_index = 0;
+       }
+       if (frame_index >= idx->num_entries) {
+               frame_index = idx->num_entries - 1;
+       }
+       return idx->entries[frame_index].seek_pos_dts;
+}
+
+int IMB_indexer_get_frame_index(struct anim_index * idx, int frameno)
+{
+       int len = idx->num_entries;
+       int half;
+       int middle;
+       int first = 0;
+
+       /* bsearch (lower bound) the right index */
+       
+       while (len > 0) {
+               half = len >> 1;
+               middle = first;
+
+               middle += half;
+
+               if (idx->entries[middle].frameno < frameno) {
+                       first = middle;
+                       ++first;
+                       len = len - half - 1;
+               } else {
+                       len = half;
+               }
+       }
+
+       if (first == idx->num_entries) {
+               return idx->num_entries - 1;
+       } else {
+               return first;
+       }
+}
+
+unsigned long long IMB_indexer_get_pts(struct anim_index * idx, 
+                                      int frame_index)
+{
+       if (frame_index < 0) {
+               frame_index = 0;
+       }
+       if (frame_index >= idx->num_entries) {
+               frame_index = idx->num_entries - 1;
+       }
+       return idx->entries[frame_index].pts;
+}
+
+int IMB_indexer_get_duration(struct anim_index * idx)
+{
+       if (idx->num_entries == 0) {
+               return 0;
+       }
+       return idx->entries[idx->num_entries-1].frameno + 1;
+}
+
+int IMB_indexer_can_scan(struct anim_index * idx, 
+                        int old_frame_index, int new_frame_index)
+{
+       /* makes only sense, if it is the same I-Frame and we are not
+          trying to run backwards in time... */
+       return (IMB_indexer_get_seek_pos(idx, old_frame_index)
+               == IMB_indexer_get_seek_pos(idx, new_frame_index) && 
+               old_frame_index < new_frame_index);
+}
+
+void IMB_indexer_close(struct anim_index * idx)
+{
+       MEM_freeN(idx->entries);
+       MEM_freeN(idx);
+}
+
+int IMB_proxy_size_to_array_index(IMB_Proxy_Size pr_size)
+{
+       switch (pr_size) {
+       case IMB_PROXY_NONE: /* if we got here, something is broken anyways,
+                               so sane defaults... */
+               return 0;
+       case IMB_PROXY_25:
+               return 0;
+       case IMB_PROXY_50:
+               return 1;
+       case IMB_PROXY_75:
+               return 2;
+       case IMB_PROXY_100:
+               return 3;
+       default:
+               return 0;
+       };
+       return 0;
+}
+
+int IMB_timecode_to_array_index(IMB_Timecode_Type tc)
+{
+       switch (tc) {
+       case IMB_TC_NONE: /* if we got here, something is broken anyways,
+                               so sane defaults... */
+               return 0;
+       case IMB_TC_RECORD_RUN:
+               return 0;
+       case IMB_TC_FREE_RUN:
+               return 1;
+       case IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN:
+               return 2;
+       default:
+               return 0;
+       };
+       return 0;
+}
+
+
+/* ----------------------------------------------------------------------
+   - rebuild helper functions
+   ---------------------------------------------------------------------- */
+
+static void get_index_dir(struct anim * anim, char * index_dir)
+{
+       if (!anim->index_dir[0]) {
+               char fname[FILE_MAXFILE];
+               BLI_strncpy(index_dir, anim->name, FILE_MAXDIR);
+               BLI_splitdirstring(index_dir, fname);
+               BLI_join_dirfile(index_dir, FILE_MAXDIR, index_dir, "BL_proxy");
+               BLI_join_dirfile(index_dir, FILE_MAXDIR, index_dir, fname);
+       } else {
+               BLI_strncpy(index_dir, anim->index_dir, FILE_MAXDIR);
+       }
+}
+
+static void get_proxy_filename(struct anim * anim, IMB_Proxy_Size preview_size,
+                              char * fname, int temp)
+{
+       char index_dir[FILE_MAXDIR];
+       int i = IMB_proxy_size_to_array_index(preview_size);
+
+       char proxy_name[256];
+       char proxy_temp_name[256];
+       char stream_suffix[20];
+       
+       stream_suffix[0] = 0;
+
+       if (anim->streamindex > 0) {
+               BLI_snprintf(stream_suffix, 20, "_st%d", anim->streamindex);
+       }
+
+       BLI_snprintf(proxy_name, 256, "proxy_%d%s.avi", 
+                    (int) (proxy_fac[i] * 100), stream_suffix);
+       BLI_snprintf(proxy_temp_name, 256, "proxy_%d%s_part.avi", 
+                    (int) (proxy_fac[i] * 100), stream_suffix);
+
+       get_index_dir(anim, index_dir);
+
+       BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR, index_dir, 
+                        temp ? proxy_temp_name : proxy_name);
+}
+
+static void get_tc_filename(struct anim * anim, IMB_Timecode_Type tc,
+                           char * fname)
+{
+       char index_dir[FILE_MAXDIR];
+       int i = IMB_timecode_to_array_index(tc);
+       char * index_names[] = {
+               "record_run%s.blen_tc", "free_run%s.blen_tc",
+               "interp_free_run%s.blen_tc" };
+
+       char stream_suffix[20];
+       char index_name[256];
+       
+       stream_suffix[0] = 0;
+
+       if (anim->streamindex > 0) {
+               BLI_snprintf(stream_suffix, 20, "_st%d", anim->streamindex);
+       }
+       
+       BLI_snprintf(index_name, 256, index_names[i], stream_suffix);
+
+       get_index_dir(anim, index_dir);
+       
+       BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR, 
+                        index_dir, index_name);
+}
+
+/* ----------------------------------------------------------------------
+   - ffmpeg rebuilder
+   ---------------------------------------------------------------------- */
+
+#ifdef WITH_FFMPEG
+
+struct proxy_output_ctx {
+       AVFormatContext* of;
+       AVStream* st;
+       AVCodecContext* c;
+       AVCodec* codec;
+       struct SwsContext * sws_ctx;
+       AVFrame* frame;
+       uint8_t* video_buffer;
+       int video_buffersize;
+       int cfra;
+       int proxy_size;
+       int orig_height;
+       struct anim * anim;
+};
+
+// work around stupid swscaler 16 bytes alignment bug...
+
+static int round_up(int x, int mod)
+{
+       return x + ((mod - (x % mod)) % mod);
+}
+
+static struct proxy_output_ctx * alloc_proxy_output_ffmpeg(
+       struct anim * anim,
+       AVStream * st, int proxy_size, int width, int height,
+       int quality)
+{
+       struct proxy_output_ctx * rv = MEM_callocN(
+               sizeof(struct proxy_output_ctx), "alloc_proxy_output");
+       
+       char fname[FILE_MAXDIR+FILE_MAXFILE];
+
+       // JPEG requires this
+       width = round_up(width, 8);
+       height = round_up(height, 8);
+
+       rv->proxy_size = proxy_size;
+       rv->anim = anim;
+
+       get_proxy_filename(rv->anim, rv->proxy_size, fname, TRUE);
+       BLI_make_existing_file(fname);
+
+       rv->of = avformat_alloc_context();
+       rv->of->oformat = av_guess_format("avi", NULL, NULL);
+       
+       BLI_snprintf(rv->of->filename, sizeof(rv->of->filename), "%s", fname);
+
+       fprintf(stderr, "Starting work on proxy: %s\n", rv->of->filename);
+
+       rv->st = av_new_stream(rv->of, 0);
+       rv->c = rv->st->codec;
+       rv->c->codec_type = AVMEDIA_TYPE_VIDEO;
+       rv->c->codec_id = CODEC_ID_MJPEG;
+       rv->c->width = width;
+       rv->c->height = height;
+
+       rv->of->oformat->video_codec = rv->c->codec_id;
+       rv->codec = avcodec_find_encoder(rv->c->codec_id);
+
+       if (!rv->codec) {
+               fprintf(stderr, "No ffmpeg MJPEG encoder available? "
+                       "Proxy not built!\n");
+               av_free(rv->of);
+               return NULL;
+       }
+
+       if (rv->codec->pix_fmts) {
+               rv->c->pix_fmt = rv->codec->pix_fmts[0];
+       } else {
+               rv->c->pix_fmt = PIX_FMT_YUVJ420P;
+       }
+
+       rv->c->sample_aspect_ratio 
+               = rv->st->sample_aspect_ratio 
+               = st->codec->sample_aspect_ratio;
+
+       rv->c->time_base.den = 25;
+       rv->c->time_base.num = 1;
+       rv->st->time_base = rv->c->time_base;
+
+       if (rv->of->flags & AVFMT_GLOBALHEADER) {
+               rv->c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+       }
+
+       if (av_set_parameters(rv->of, NULL) < 0) {
+               fprintf(stderr, "Couldn't set output parameters? "
+                       "Proxy not built!\n");
+               av_free(rv->of);
+               return 0;
+       }
+
+       if (avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE) < 0) {
+               fprintf(stderr, "Couldn't open outputfile! "
+                       "Proxy not built!\n");
+               av_free(rv->of);
+               return 0;
+       }
+
+       avcodec_open(rv->c, rv->codec);
+
+       rv->video_buffersize = 2000000;
+       rv->video_buffer = (uint8_t*)MEM_mallocN(
+               rv->video_buffersize, "FFMPEG video buffer");
+
+       rv->orig_height = st->codec->height;
+
+       if (st->codec->width != width || st->codec->height != height
+           || st->codec->pix_fmt != rv->c->pix_fmt) {
+               rv->frame = avcodec_alloc_frame();
+               avpicture_fill((AVPicture*) rv->frame, 
+                              MEM_mallocN(avpicture_get_size(
+                                                  rv->c->pix_fmt, 
+                                                  round_up(width, 16), height),
+                                          "alloc proxy output frame"),
+                              rv->c->pix_fmt, round_up(width, 16), height);
+
+               rv->sws_ctx = sws_getContext(
+                       st->codec->width,
+                       st->codec->height,
+                       st->codec->pix_fmt,
+                       width, height,
+                       rv->c->pix_fmt,
+                       SWS_FAST_BILINEAR | SWS_PRINT_INFO,
+                       NULL, NULL, NULL);
+       }
+
+       av_write_header(rv->of);
+
+       return rv;
+}
+
+static int add_to_proxy_output_ffmpeg(
+       struct proxy_output_ctx * ctx, AVFrame * frame)
+{
+       int outsize = 0;
+
+       if (!ctx) {
+               return 0;
+       }
+
+       if (ctx->sws_ctx && frame && 
+           (frame->data[0] || frame->data[1] ||
+            frame->data[2] || frame->data[3])) {
+               sws_scale(ctx->sws_ctx, (const uint8_t * const*) frame->data,
+                         frame->linesize, 0, ctx->orig_height, 
+                         ctx->frame->data, ctx->frame->linesize);
+       }
+
+       ctx->frame->pts = ctx->cfra++;
+
+       outsize = avcodec_encode_video(
+               ctx->c, ctx->video_buffer, ctx->video_buffersize, 
+               ctx->sws_ctx ? (frame ? ctx->frame : 0) : frame);
+
+       if (outsize < 0) {
+               fprintf(stderr, "Error encoding proxy frame %d for '%s'\n", 
+                       ctx->cfra - 1, ctx->of->filename);
+               return 0;
+       }
+
+       if (outsize != 0) {
+               AVPacket packet;
+               av_init_packet(&packet);
+
+               if (ctx->c->coded_frame->pts != AV_NOPTS_VALUE) {
+                       packet.pts = av_rescale_q(ctx->c->coded_frame->pts,
+                                                 ctx->c->time_base,
+                                                 ctx->st->time_base);
+               }
+               if (ctx->c->coded_frame->key_frame)
+                       packet.flags |= AV_PKT_FLAG_KEY;
+
+               packet.stream_index = ctx->st->index;
+               packet.data = ctx->video_buffer;
+               packet.size = outsize;
+
+               if (av_interleaved_write_frame(ctx->of, &packet) != 0) {
+                       fprintf(stderr, "Error writing proxy frame %d "
+                               "into '%s'\n", ctx->cfra - 1, 
+                               ctx->of->filename);
+                       return 0;
+               }
+
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
+static void free_proxy_output_ffmpeg(struct proxy_output_ctx * ctx,
+                                    int rollback)
+{
+       int i;
+       char fname[FILE_MAXDIR+FILE_MAXFILE];
+       char fname_tmp[FILE_MAXDIR+FILE_MAXFILE];
+
+       if (!ctx) {
+               return;
+       }
+
+       if (!rollback) {
+               while (add_to_proxy_output_ffmpeg(ctx, NULL)) ;
+       }
+
+       avcodec_flush_buffers(ctx->c);
+
+       av_write_trailer(ctx->of);
+       
+       avcodec_close(ctx->c);
+       
+       for (i = 0; i < ctx->of->nb_streams; i++) {
+               if (&ctx->of->streams[i]) {
+                       av_freep(&ctx->of->streams[i]);
+               }
+       }
+
+       if (ctx->of->oformat) {
+               if (!(ctx->of->oformat->flags & AVFMT_NOFILE)) {
+                       avio_close(ctx->of->pb);
+               }
+       }
+       av_free(ctx->of);
+
+       MEM_freeN(ctx->video_buffer);
+
+       if (ctx->sws_ctx) {
+               sws_freeContext(ctx->sws_ctx);
+
+               MEM_freeN(ctx->frame->data[0]);
+               av_free(ctx->frame);
+       }
+
+       get_proxy_filename(ctx->anim, ctx->proxy_size, 
+                          fname_tmp, TRUE);
+
+       if (rollback) {
+               unlink(fname_tmp);
+       } else {
+               get_proxy_filename(ctx->anim, ctx->proxy_size, 
+                                  fname, FALSE);
+               rename(fname_tmp, fname);
+       }
+       
+       MEM_freeN(ctx);
+}
+
+
+static int index_rebuild_ffmpeg(struct anim * anim, 
+                               IMB_Timecode_Type tcs_in_use,
+                               IMB_Proxy_Size proxy_sizes_in_use,
+                               int quality,
+                               short *stop, short *do_update, 
+                               float *progress)
+{
+       int i, videoStream;
+       unsigned long long seek_pos = 0;
+       unsigned long long last_seek_pos = 0;
+       unsigned long long seek_pos_dts = 0;
+       unsigned long long seek_pos_pts = 0;
+       unsigned long long last_seek_pos_dts = 0;
+       unsigned long long start_pts = 0;
+       double frame_rate;
+       double pts_time_base;
+       int frameno = 0;
+       int start_pts_set = FALSE;
+
+       AVFormatContext *iFormatCtx;
+       AVCodecContext *iCodecCtx;
+       AVCodec *iCodec;
+       AVStream *iStream;
+       AVFrame* in_frame = 0;
+       AVPacket next_packet;
+       int streamcount;
+
+       struct proxy_output_ctx * proxy_ctx[IMB_PROXY_MAX_SLOT];
+       anim_index_builder * indexer [IMB_TC_MAX_SLOT];
+
+       int num_proxy_sizes = IMB_PROXY_MAX_SLOT;
+       int num_indexers = IMB_TC_MAX_SLOT;
+       uint64_t stream_size;
+
+       memset(proxy_ctx, 0, sizeof(proxy_ctx));
+       memset(indexer, 0, sizeof(indexer));
+
+       if(av_open_input_file(&iFormatCtx, anim->name, NULL, 0, NULL) != 0) {
+               return 0;
+       }
+
+       if (av_find_stream_info(iFormatCtx) < 0) {
+               av_close_input_file(iFormatCtx);
+               return 0;
+       }
+
+       streamcount = anim->streamindex;
+
+       /* Find the video stream */
+       videoStream = -1;
+       for (i = 0; i < iFormatCtx->nb_streams; i++)
+               if(iFormatCtx->streams[i]->codec->codec_type
+                  == AVMEDIA_TYPE_VIDEO) {
+                       if (streamcount > 0) {
+                               streamcount--;
+                               continue;
+                       }
+                       videoStream = i;
+                       break;
+               }
+
+       if (videoStream == -1) {
+               av_close_input_file(iFormatCtx);
+               return 0;
+       }
+
+       iStream = iFormatCtx->streams[videoStream];
+       iCodecCtx = iStream->codec;
+
+       iCodec = avcodec_find_decoder(iCodecCtx->codec_id);
+       
+       if (iCodec == NULL) {
+               av_close_input_file(iFormatCtx);
+               return 0;
+       }
+
+       iCodecCtx->workaround_bugs = 1;
+
+       if (avcodec_open(iCodecCtx, iCodec) < 0) {
+               av_close_input_file(iFormatCtx);
+               return 0;
+       }
+
+       in_frame = avcodec_alloc_frame();
+
+       stream_size = avio_size(iFormatCtx->pb);
+
+       for (i = 0; i < num_proxy_sizes; i++) {
+               if (proxy_sizes_in_use & proxy_sizes[i]) {
+                       proxy_ctx[i] = alloc_proxy_output_ffmpeg(
+                               anim, iStream, proxy_sizes[i],
+                               iCodecCtx->width * proxy_fac[i],
+                               iCodecCtx->height * proxy_fac[i],
+                               quality);
+                       if (!proxy_ctx[i]) {
+                               proxy_sizes_in_use &= ~proxy_sizes[i];
+                       }
+               }
+       }
+
+       for (i = 0; i < num_indexers; i++) {
+               if (tcs_in_use & tc_types[i]) {
+                       char fname[FILE_MAXDIR+FILE_MAXFILE];
+
+                       get_tc_filename(anim, tc_types[i], fname);
+
+                       indexer[i] = IMB_index_builder_create(fname);
+                       if (!indexer[i]) {
+                               tcs_in_use &= ~tc_types[i];
+                       }
+               }
+       }
+
+       frame_rate = av_q2d(iStream->r_frame_rate);
+       pts_time_base = av_q2d(iStream->time_base);
+
+       while(av_read_frame(iFormatCtx, &next_packet) >= 0) {
+               int frame_finished = 0;
+               float next_progress =  ((int)floor(((double) next_packet.pos) * 100 /
+                                                  ((double) stream_size)+0.5)) / 100;
+
+               if (*progress != next_progress) {
+                       *progress = next_progress;
+                       *do_update = 1;
+               }
+
+               if (*stop) {
+                       av_free_packet(&next_packet);
+                       break;
+               }
+
+               if (next_packet.stream_index == videoStream) {
+                       if (next_packet.flags & AV_PKT_FLAG_KEY) {
+                               last_seek_pos = seek_pos;
+                               last_seek_pos_dts = seek_pos_dts;
+                               seek_pos = next_packet.pos;
+                               seek_pos_dts = next_packet.dts;
+                               seek_pos_pts = next_packet.pts;
+                       }
+
+                       avcodec_decode_video2(
+                               iCodecCtx, in_frame, &frame_finished, 
+                               &next_packet);
+               }
+
+               if (frame_finished) {
+                       unsigned long long s_pos = seek_pos;
+                       unsigned long long s_dts = seek_pos_dts;
+                       unsigned long long pts 
+                               = av_get_pts_from_frame(iFormatCtx, in_frame);
+
+                       for (i = 0; i < num_proxy_sizes; i++) {
+                               add_to_proxy_output_ffmpeg(
+                                       proxy_ctx[i], in_frame);
+                       }
+
+                       if (!start_pts_set) {
+                               start_pts = pts;
+                               start_pts_set = TRUE;
+                       }
+
+                       frameno = (pts - start_pts) 
+                               * pts_time_base * frame_rate; 
+
+                       /* decoding starts *always* on I-Frames,
+                          so: P-Frames won't work, even if all the
+                          information is in place, when we seek
+                          to the I-Frame presented *after* the P-Frame,
+                          but located before the P-Frame within
+                          the stream */
+
+                       if (pts < seek_pos_pts) {
+                               s_pos = last_seek_pos;
+                               s_dts = last_seek_pos_dts;
+                       }
+
+                       for (i = 0; i < num_indexers; i++) {
+                               if (tcs_in_use & tc_types[i]) {
+                                       IMB_index_builder_proc_frame(
+                                               indexer[i], 
+                                               next_packet.data, 
+                                               next_packet.size,
+                                               frameno, s_pos, s_dts, pts);
+                               }
+                       }
+               }
+               av_free_packet(&next_packet);
+       }
+
+       for (i = 0; i < num_indexers; i++) {
+               if (tcs_in_use & tc_types[i]) {
+                       IMB_index_builder_finish(indexer[i], *stop);
+               }
+       }
+
+       for (i = 0; i < num_proxy_sizes; i++) {
+               if (proxy_sizes_in_use & proxy_sizes[i]) {
+                       free_proxy_output_ffmpeg(proxy_ctx[i], *stop);
+               }
+       }
+
+       av_free(in_frame);
+
+       return 1;
+}
+
+#endif
+
+/* ----------------------------------------------------------------------
+   - internal AVI (fallback) rebuilder
+   ---------------------------------------------------------------------- */
+
+static AviMovie * alloc_proxy_output_avi(
+       struct anim * anim, char * filename, int width, int height,
+       int quality)
+{
+       int x, y;
+       AviFormat format;
+       double framerate;
+       AviMovie * avi;
+       short frs_sec = 25;      /* it doesn't really matter for proxies,
+                                   but sane defaults help anyways...*/
+       float frs_sec_base = 1.0;
+
+       IMB_anim_get_fps(anim, &frs_sec, &frs_sec_base);
+       
+       x = width;
+       y = height;
+
+       framerate= (double) frs_sec / (double) frs_sec_base;
+       
+       avi = MEM_mallocN (sizeof(AviMovie), "avimovie");
+
+       format = AVI_FORMAT_MJPEG;
+
+       if (AVI_open_compress (filename, avi, 1, format) != AVI_ERROR_NONE) {
+               MEM_freeN(avi);
+               return 0;
+       }
+                       
+       AVI_set_compress_option (avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_WIDTH, &x);
+       AVI_set_compress_option (avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_HEIGHT, &y);
+       AVI_set_compress_option (avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_QUALITY, &quality);           
+       AVI_set_compress_option (avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_FRAMERATE, &framerate);
+
+       avi->interlace= 0;
+       avi->odd_fields= 0;
+
+       return avi;
+}
+
+static void index_rebuild_fallback(struct anim * anim,
+                                  IMB_Timecode_Type tcs_in_use,
+                                  IMB_Proxy_Size proxy_sizes_in_use,
+                                  int quality,
+                                  short *stop, short *do_update, 
+                                  float *progress)
+{
+       int cnt = IMB_anim_get_duration(anim, IMB_TC_NONE);
+       int i, pos;
+       AviMovie * proxy_ctx[IMB_PROXY_MAX_SLOT];
+       char fname[FILE_MAXDIR+FILE_MAXFILE];
+       char fname_tmp[FILE_MAXDIR+FILE_MAXFILE];
+       
+       memset(proxy_ctx, 0, sizeof(proxy_ctx));
+
+       /* since timecode indices only work with ffmpeg right now,
+          don't know a sensible fallback here...
+
+          so no proxies, no game to play...
+       */
+       if (proxy_sizes_in_use == IMB_PROXY_NONE) {
+               return;
+       }
+
+       for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
+               if (proxy_sizes_in_use & proxy_sizes[i]) {
+                       char fname[FILE_MAXDIR+FILE_MAXFILE];
+
+                       get_proxy_filename(anim, proxy_sizes[i], fname, TRUE);
+                       BLI_make_existing_file(fname);
+
+                       proxy_ctx[i] = alloc_proxy_output_avi(
+                               anim, fname,
+                               anim->x * proxy_fac[i],
+                               anim->y * proxy_fac[i],
+                               quality);
+               }
+       }
+
+       for (pos = 0; pos < cnt; pos++) {
+               struct ImBuf * ibuf = IMB_anim_absolute(
+                       anim, pos, IMB_TC_NONE, IMB_PROXY_NONE);
+               int next_progress = (int) ((double) pos / (double) cnt);
+
+               if (*progress != next_progress) {
+                       *progress = next_progress;
+                       *do_update = 1;
+               }
+               
+               if (*stop) {
+                       break;
+               }
+
+               IMB_flipy(ibuf);
+
+               for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
+                       if (proxy_sizes_in_use & proxy_sizes[i]) {
+                               int x = anim->x * proxy_fac[i];
+                               int y = anim->y * proxy_fac[i];
+
+                               struct ImBuf * s_ibuf = IMB_scalefastImBuf(
+                                       ibuf, x, y);
+
+                               IMB_convert_rgba_to_abgr(s_ibuf);
+       
+                               AVI_write_frame (proxy_ctx[i], pos, 
+                                                AVI_FORMAT_RGB32, 
+                                                s_ibuf->rect, x * y * 4);
+
+                               /* note that libavi free's the buffer... */
+                               s_ibuf->rect = 0;
+
+                               IMB_freeImBuf(s_ibuf);
+                       }
+               }
+       }
+
+       for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
+               if (proxy_sizes_in_use & proxy_sizes[i]) {
+                       AVI_close_compress (proxy_ctx[i]);
+                       MEM_freeN (proxy_ctx[i]);
+
+                       get_proxy_filename(anim, proxy_sizes[i], 
+                                          fname_tmp, TRUE);
+                       get_proxy_filename(anim, proxy_sizes[i], 
+                                          fname, FALSE);
+
+                       if (*stop) {
+                               unlink(fname_tmp);
+                       } else {
+                               rename(fname_tmp, fname);
+                       }
+               }
+       }
+}
+
+/* ----------------------------------------------------------------------
+   - public API
+   ---------------------------------------------------------------------- */
+
+void IMB_anim_index_rebuild(struct anim * anim, IMB_Timecode_Type tcs_in_use,
+                           IMB_Proxy_Size proxy_sizes_in_use,
+                           int quality,
+                           short *stop, short *do_update, float *progress)
+{
+       switch (anim->curtype) {
+#ifdef WITH_FFMPEG
+       case ANIM_FFMPEG:
+               index_rebuild_ffmpeg(anim, tcs_in_use, proxy_sizes_in_use,
+                                    quality, stop, do_update, progress);
+               break;
+#endif
+       default:
+               index_rebuild_fallback(anim, tcs_in_use, proxy_sizes_in_use,
+                                      quality, stop, do_update, progress);
+               break;
+       }
+}
+
+void IMB_free_indices(struct anim * anim)
+{
+       int i;
+
+       for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
+               if (anim->proxy_anim[i]) {
+                       IMB_close_anim(anim->proxy_anim[i]);
+                       anim->proxy_anim[i] = 0;
+               }
+       }
+
+       for (i = 0; i < IMB_TC_MAX_SLOT; i++) {
+               if (anim->curr_idx[i]) {
+                       IMB_indexer_close(anim->curr_idx[i]);
+                       anim->curr_idx[i] = 0;
+               }
+       }
+
+
+       anim->proxies_tried = 0;
+       anim->indices_tried = 0;
+}
+
+void IMB_anim_set_index_dir(struct anim * anim, const char * dir)
+{
+       if (strcmp(anim->index_dir, dir) == 0) {
+               return;
+       }
+       BLI_strncpy(anim->index_dir, dir, sizeof(anim->index_dir));
+
+       IMB_free_indices(anim);
+}
+
+struct anim * IMB_anim_open_proxy(
+       struct anim * anim, IMB_Proxy_Size preview_size)
+{
+       char fname[FILE_MAXDIR+FILE_MAXFILE];
+       int i = IMB_proxy_size_to_array_index(preview_size);
+
+       if (anim->proxy_anim[i]) {
+               return anim->proxy_anim[i];
+       }
+
+       if (anim->proxies_tried & preview_size) {
+               return NULL;
+       }
+
+       get_proxy_filename(anim, preview_size, fname, FALSE);
+
+       anim->proxy_anim[i] = IMB_open_anim(fname, 0, 0);
+       
+       anim->proxies_tried |= preview_size;
+
+       return anim->proxy_anim[i];
+}
+
+struct anim_index * IMB_anim_open_index(
+       struct anim * anim, IMB_Timecode_Type tc)
+{
+       char fname[FILE_MAXDIR+FILE_MAXFILE];
+       int i = IMB_timecode_to_array_index(tc);
+
+       if (anim->curr_idx[i]) {
+               return anim->curr_idx[i];
+       }
+
+       if (anim->indices_tried & tc) {
+               return 0;
+       }
+
+       get_tc_filename(anim, tc, fname);
+
+       anim->curr_idx[i] = IMB_indexer_open(fname);
+       
+       anim->indices_tried |= tc;
+
+       return anim->curr_idx[i];
+}
+
+int IMB_anim_index_get_frame_index(struct anim * anim, IMB_Timecode_Type tc,
+                                  int position)
+{
+       struct anim_index * idx = IMB_anim_open_index(anim, tc);
+
+       if (!idx) {
+               return position;
+       }
+
+       return IMB_indexer_get_frame_index(idx, position);
+}
+
diff --git a/source/blender/imbuf/intern/indexer_dv.c b/source/blender/imbuf/intern/indexer_dv.c
new file mode 100644 (file)
index 0000000..0961af1
--- /dev/null
@@ -0,0 +1,391 @@
+/*
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Peter Schlaile <peter [at] schlaile [dot] de> 2011
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+*/
+
+#include "IMB_indexer.h"
+#include "MEM_guardedalloc.h"
+#include <time.h>
+
+typedef struct indexer_dv_bitstream {
+        unsigned char* buffer;
+        int bit_pos;
+} indexer_dv_bitstream;
+
+static indexer_dv_bitstream bitstream_new(unsigned char* buffer_) 
+{
+       indexer_dv_bitstream rv;
+
+       rv.buffer = buffer_;
+       rv.bit_pos = 0;
+
+       return rv;
+}
+
+static unsigned long bitstream_get_bits(indexer_dv_bitstream * This, int num) 
+{
+       int byte_pos = This->bit_pos >> 3;
+       unsigned long i = 
+               This->buffer[byte_pos] | (This->buffer[byte_pos + 1] << 8) |
+               (This->buffer[byte_pos + 2] << 16) | 
+               (This->buffer[byte_pos + 3] << 24);
+       int rval = (i >> (This->bit_pos & 0x7)) & ((1 << num) - 1);
+       This->bit_pos += num;
+       return rval;
+}
+
+static int parse_num(indexer_dv_bitstream * b, int numbits) {
+        return bitstream_get_bits(b, numbits);
+}
+
+static int parse_bcd(indexer_dv_bitstream * b, int n) 
+{
+        char s[256];
+       char * p = s + (n+3)/4;
+
+       *p-- = 0;
+
+        while (n > 4) {
+                char a;
+                int v = bitstream_get_bits(b, 4);
+
+                n -= 4;
+               a = '0' + v;
+
+                if (a > '9') {
+                       bitstream_get_bits(b, n); 
+                       return -1;
+                }
+
+               *p-- = a;
+        }
+        if (n) {
+                char a;
+                int v = bitstream_get_bits(b, n);
+                a = '0' + v;
+                if (a > '9') {
+                       return -1;
+                }
+                *p-- = a;
+        }
+
+        return atol(s);
+}
+
+typedef struct indexer_dv_context
+{
+       int rec_curr_frame;
+       int rec_curr_second;
+       int rec_curr_minute;
+       int rec_curr_hour;
+
+       int rec_curr_day;
+       int rec_curr_month;
+       int rec_curr_year;
+
+       char got_record_date;
+       char got_record_time;
+
+       time_t ref_time_read;
+       time_t ref_time_read_new;
+       int curr_frame;
+
+       time_t gap_start;
+       int gap_frame;
+
+       int frameno_offset;
+
+       anim_index_entry backbuffer[31];
+       int fsize;
+
+       anim_index_builder * idx;
+} indexer_dv_context;
+
+static void parse_packet(indexer_dv_context * This, unsigned char * p)
+{
+        indexer_dv_bitstream b;
+        int type = p[0];
+
+       b = bitstream_new(p + 1);
+
+        switch (type) {
+        case 0x62: // Record date
+                parse_num(&b, 8);
+                This->rec_curr_day = parse_bcd(&b, 6);
+                parse_num(&b, 2);
+                This->rec_curr_month = parse_bcd(&b, 5);
+                parse_num(&b, 3);
+                This->rec_curr_year = parse_bcd(&b, 8);
+                if (This->rec_curr_year < 25) {
+                        This->rec_curr_year += 2000;
+                } else {
+                        This->rec_curr_year += 1900;
+                }
+                This->got_record_date = 1;
+                break;
+        case 0x63: // Record time
+                This->rec_curr_frame = parse_bcd(&b, 6);
+                parse_num(&b, 2);
+                This->rec_curr_second = parse_bcd(&b, 7);
+                parse_num(&b, 1);
+                This->rec_curr_minute = parse_bcd(&b, 7);
+                parse_num(&b, 1);
+                This->rec_curr_hour = parse_bcd(&b, 6);
+                This->got_record_time = 1;
+                break;
+        }
+}
+
+static void parse_header_block(indexer_dv_context * This, unsigned char* target)
+{
+       int i;
+        for (i = 3; i < 80; i += 5) {
+                  if (target[i] != 0xff) {
+                          parse_packet(This, target + i);
+                  }
+        }
+}
+
+static void parse_subcode_blocks(
+       indexer_dv_context * This, unsigned char* target)
+{
+       int i,j;
+
+        for (j = 0; j < 2; j++) {
+                for (i = 3; i < 80; i += 5) {
+                        if (target[i] != 0xff) {
+                                parse_packet(This, target + i);
+                        }
+                }
+        }
+}
+
+static void parse_vaux_blocks(
+       indexer_dv_context * This, unsigned char* target)
+{
+       int i,j;
+
+        for (j = 0; j < 3; j++) {
+                for (i = 3; i < 80; i += 5) {
+                        if (target[i] != 0xff) {
+                                parse_packet(This, target + i);
+                        }
+                }
+                target += 80;
+        }
+}
+
+static void parse_audio_headers(
+       indexer_dv_context * This, unsigned char* target)
+{
+       int i;
+
+        for(i = 0; i < 9; i++) {
+                if (target[3] != 0xff) {
+                        parse_packet(This, target + 3);
+                }
+                target += 16 * 80;
+        }
+}
+
+static void parse_frame(indexer_dv_context * This, 
+                       unsigned char * framebuffer, int isPAL)
+{
+        int numDIFseq = isPAL ? 12 : 10;
+        unsigned char* target = framebuffer;
+       int ds;
+
+        for (ds = 0; ds < numDIFseq; ds++) { 
+                parse_header_block(This, target);
+                target +=   1 * 80;
+                parse_subcode_blocks(This, target);
+                target +=   2 * 80;
+                parse_vaux_blocks(This, target);
+                target +=   3 * 80;
+                parse_audio_headers(This, target);
+                target += 144 * 80;
+        }
+}
+
+static void inc_frame(int * frame, time_t * t, int isPAL)
+{
+        if ((isPAL && *frame >= 25) || (!isPAL && *frame >= 30)) {
+                fprintf(stderr, "Ouchie: inc_frame: invalid_frameno: %d\n",
+                       *frame);
+        }
+        (*frame)++;
+        if (isPAL && *frame >= 25) {
+                (*t)++;
+                *frame = 0;
+        } else if (!isPAL && *frame >= 30) {
+                (*t)++;
+                *frame = 0;
+        }
+}
+
+static void write_index(indexer_dv_context * This, anim_index_entry * entry)
+{
+       IMB_index_builder_add_entry(
+               This->idx, entry->frameno + This->frameno_offset, 
+               entry->seek_pos, entry->seek_pos_dts, entry->pts);
+}
+
+static void fill_gap(indexer_dv_context * This, int isPAL)
+{
+       int i;
+
+        for (i = 0; i < This->fsize; i++) {
+                if (This->gap_start == This->ref_time_read && 
+                   This->gap_frame == This->curr_frame) {
+                        fprintf(stderr, 
+                               "indexer_dv::fill_gap: "
+                               "can't seek backwards !\n");
+                        break;
+                }
+                inc_frame(&This->gap_frame, &This->gap_start, isPAL);
+        }
+
+        while (This->gap_start != This->ref_time_read || 
+              This->gap_frame != This->curr_frame) {
+                inc_frame(&This->gap_frame, &This->gap_start, isPAL);
+               This->frameno_offset++;
+        }
+
+        for (i = 0; i < This->fsize; i++) {
+               write_index(This, This->backbuffer + i);
+        }
+        This->fsize = 0;
+}
+
+static void proc_frame(indexer_dv_context * This,
+                      unsigned char* framebuffer, int isPAL)
+{
+       struct tm recDate;
+       time_t t;
+
+        if (!This->got_record_date || !This->got_record_time) {
+               return;
+       }
+
+       recDate.tm_sec = This->rec_curr_second;
+       recDate.tm_min = This->rec_curr_minute;
+       recDate.tm_hour = This->rec_curr_hour;
+       recDate.tm_mday = This->rec_curr_day;
+       recDate.tm_mon = This->rec_curr_month - 1;
+       recDate.tm_year = This->rec_curr_year - 1900;
+       recDate.tm_wday = -1;
+       recDate.tm_yday = -1;
+       recDate.tm_isdst = -1;
+       
+       t = mktime(&recDate);
+       if (t == -1) {
+               return;
+       }
+
+       This->ref_time_read_new = t;
+
+       if (This->ref_time_read < 0) {
+               This->ref_time_read = This->ref_time_read_new;
+               This->curr_frame = 0;
+       } else {
+               if (This->ref_time_read_new - This->ref_time_read == 1) {
+                       This->curr_frame = 0;
+                       This->ref_time_read = This->ref_time_read_new;
+                       if (This->gap_frame >= 0) {
+                               fill_gap(This, isPAL);
+                               This->gap_frame = -1;
+                       }
+               } else if (This->ref_time_read_new  == This->ref_time_read) {
+                       // do nothing
+               } else {
+                       This->gap_start = This->ref_time_read;
+                       This->gap_frame = This->curr_frame;
+                       This->ref_time_read = This->ref_time_read_new;
+                       This->curr_frame = -1;
+               }
+       }
+}
+
+static void indexer_dv_proc_frame(anim_index_builder * idx, 
+                                 unsigned char * buffer,
+                                 int data_size, 
+                                 struct anim_index_entry * entry)
+{
+       int isPAL;
+       
+       indexer_dv_context * This = (indexer_dv_context *) idx->private_data;
+
+       isPAL = (buffer[3] & 0x80);
+
+       This->got_record_date = FALSE;
+       This->got_record_time = FALSE;
+
+       parse_frame(This, buffer, isPAL);
+       proc_frame(This, buffer, isPAL);
+
+       if (This->curr_frame >= 0) {
+               write_index(This, entry);
+               inc_frame(&This->curr_frame, &This->ref_time_read, isPAL);
+       } else {
+               This->backbuffer[This->fsize++] = *entry;
+               if (This->fsize >= 31) {
+                       int i;
+
+                       fprintf(stderr, "indexer_dv::indexer_dv_proc_frame: "
+                               "backbuffer overrun, emergency flush");
+
+                       for (i = 0; i < This->fsize; i++) {
+                               write_index(This, This->backbuffer+i);
+                       }
+                       This->fsize = 0;
+               }
+       }
+}
+
+static void indexer_dv_delete(anim_index_builder * idx)
+{
+       int i = 0;
+       indexer_dv_context * This = (indexer_dv_context *) idx->private_data;
+
+       for (i = 0; i < This->fsize; i++) {
+               write_index(This, This->backbuffer+i);
+       }
+
+       MEM_freeN(This);
+}
+
+void IMB_indexer_dv_new(anim_index_builder * idx)
+{
+       indexer_dv_context * rv = MEM_callocN( 
+               sizeof(indexer_dv_context), "index_dv builder context");
+
+       rv->ref_time_read = -1;
+       rv->curr_frame = -1;
+       rv->gap_frame = -1;
+       rv->idx = idx;
+       
+       idx->private_data = rv;
+       idx->proc_frame = indexer_dv_proc_frame;
+       idx->delete_priv_data = indexer_dv_delete;
+}
index 1d91f34f4fa34573bd028d929115291028881eea..2ab7e55d1f883cc49a2b2d3f65bdf767dfbdea0a 100644 (file)
@@ -317,9 +317,9 @@ ImBuf* IMB_thumb_create(const char* path, ThumbSize size, ThumbSource source, Im
                                }
                        } else if (THB_SOURCE_MOVIE == source) {
                                struct anim * anim = NULL;
-                               anim = IMB_open_anim(path, IB_rect | IB_metadata);
+                               anim = IMB_open_anim(path, IB_rect | IB_metadata, 0);
                                if (anim != NULL) {
-                                       img = IMB_anim_absolute(anim, 0);
+                                       img = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
                                        if (img == NULL) {
                                                printf("not an anim; %s\n", path);
                                        } else {
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 0dd0b9790ab6f095ee6977bd0808f99eeb5650e2..cd3afbf35533e68b23a81291fa64ead3c20ffcd8 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 {
@@ -128,11 +135,12 @@ typedef struct Sequence {
        int startstill, endstill;
        int machine, depth; /*machine - the strip channel, depth - the depth in the sequence when dealing with metastrips */
        int startdisp, enddisp; /*starting and ending points in the sequence*/
-       float sat, pad;
+       float sat;
        float mul, handsize;
                                        /* is sfra needed anymore? - it looks like its only used in one place */
        int sfra;               /* starting frame according to the timeline of the scene. */
        int anim_preseek;
+       int streamindex;   /* streamindex for movie or sound files with several streams */
 
        Strip *strip;
 
@@ -283,6 +291,19 @@ typedef struct SpeedControlVars {
 #define SEQ_COLOR_BALANCE_INVERSE_GAMMA 2
 #define SEQ_COLOR_BALANCE_INVERSE_LIFT 4
 
+/* !!! has to be same as IMB_imbuf.h IMB_PROXY_... and IMB_TC_... */
+
+#define SEQ_PROXY_IMAGE_SIZE_25                 1
+#define SEQ_PROXY_IMAGE_SIZE_50                 2
+#define SEQ_PROXY_IMAGE_SIZE_75                 4
+#define SEQ_PROXY_IMAGE_SIZE_100                8
+
+#define SEQ_PROXY_TC_NONE                       0
+#define SEQ_PROXY_TC_RECORD_RUN                 1
+#define SEQ_PROXY_TC_FREE_RUN                   2
+#define SEQ_PROXY_TC_INTERP_REC_DATE_FREE_RUN   4
+#define SEQ_PROXY_TC_ALL                        7
+
 /* seq->type WATCH IT: SEQ_EFFECT BIT is used to determine if this is an effect strip!!! */
 #define SEQ_IMAGE              0
 #define SEQ_META               1
index 67899db5538c9bd7bfe8c40a0c25f13b1ee80c65..66b10bcbf2162edbe794b0858b93e26161fd3898 100644 (file)
@@ -930,6 +930,7 @@ enum {
 #define SEQ_PROXY_RENDER_SIZE_25        25
 #define SEQ_PROXY_RENDER_SIZE_50        50
 #define SEQ_PROXY_RENDER_SIZE_75        75
+#define SEQ_PROXY_RENDER_SIZE_100       99
 #define SEQ_PROXY_RENDER_SIZE_FULL      100
 
 
index 476ac325848287403bc9e3de8713ad407d7af52d..b6e2117956d20841adec20021f1f771e8ca43a67 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,46 @@ static void rna_def_strip_proxy(BlenderRNA *brna)
        RNA_def_property_string_funcs(prop, "rna_Sequence_proxy_filepath_get", "rna_Sequence_proxy_filepath_length", "rna_Sequence_proxy_filepath_set");
 
        RNA_def_property_update(prop, NC_SCENE|ND_SEQUENCER, "rna_Sequence_update");
+
+       prop= RNA_def_property(srna, "build_25", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "build_size_flags", SEQ_PROXY_IMAGE_SIZE_25);
+       RNA_def_property_ui_text(prop, "25%", "Build 25% proxy resolution");
+
+       prop= RNA_def_property(srna, "build_50", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "build_size_flags", SEQ_PROXY_IMAGE_SIZE_50);
+       RNA_def_property_ui_text(prop, "50%", "Build 50% proxy resolution");
+
+       prop= RNA_def_property(srna, "build_75", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "build_size_flags", SEQ_PROXY_IMAGE_SIZE_75);
+       RNA_def_property_ui_text(prop, "75%", "Build 75% proxy resolution");
+
+       prop= RNA_def_property(srna, "build_100", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "build_size_flags", SEQ_PROXY_IMAGE_SIZE_100);
+       RNA_def_property_ui_text(prop, "100%", "Build 100% proxy resolution");
+
+       prop= RNA_def_property(srna, "build_record_run", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "build_tc_flags", SEQ_PROXY_TC_RECORD_RUN);
+       RNA_def_property_ui_text(prop, "Rec Run", "Build record run time code index");
+
+       prop= RNA_def_property(srna, "build_free_run", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "build_tc_flags", SEQ_PROXY_TC_FREE_RUN);
+       RNA_def_property_ui_text(prop, "Free Run", "Build free run time code index");
+
+       prop= RNA_def_property(srna, "build_free_run_rec_date", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "build_tc_flags", SEQ_PROXY_TC_INTERP_REC_DATE_FREE_RUN);
+       RNA_def_property_ui_text(prop, "Free Run (Rec Date)", "Build free run time code index using Record Date/Time");
+
+       prop= RNA_def_property(srna, "quality", PROP_INT, PROP_UNSIGNED);
+       RNA_def_property_int_sdna(prop, NULL, "quality");
+       RNA_def_property_ui_text(prop, "Quality", "JPEG Quality of proxies to build");
+       RNA_def_property_ui_range(prop, 1, 100, 1, 0);
+
+       prop= RNA_def_property(srna, "timecode", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_sdna(prop, NULL, "tc");
+       RNA_def_property_enum_items(prop, seq_tc_items);
+       RNA_def_property_ui_text(prop, "Timecode", "");
+       RNA_def_property_update(prop, NC_SCENE|ND_SEQUENCER, "rna_Sequence_tcindex_update");
+
 }
 
 static void rna_def_strip_color_balance(BlenderRNA *brna)
@@ -1208,7 +1293,7 @@ static void rna_def_proxy(StructRNA *srna)
 
        prop= RNA_def_property(srna, "use_proxy", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_PROXY);
-       RNA_def_property_ui_text(prop, "Use Proxy", "Use a preview proxy for this strip");
+       RNA_def_property_ui_text(prop, "Use Proxy / Timecode", "Use a preview proxy and/or timecode index for this strip");
        RNA_def_property_boolean_funcs(prop, NULL, "rna_Sequence_use_proxy_set");
 
        prop= RNA_def_property(srna, "proxy", PROP_POINTER, PROP_NONE);
@@ -1330,6 +1415,12 @@ static void rna_def_movie(BlenderRNA *brna)
        RNA_def_property_ui_text(prop, "MPEG Preseek", "For MPEG movies, preseek this many frames");
        RNA_def_property_update(prop, NC_SCENE|ND_SEQUENCER, "rna_Sequence_update");
 
+       prop= RNA_def_property(srna, "streamindex", PROP_INT, PROP_NONE);
+       RNA_def_property_int_sdna(prop, NULL, "streamindex");
+       RNA_def_property_range(prop, 0, 20);
+       RNA_def_property_ui_text(prop, "Streamindex", "For files with several movie streams, use the stream with the given index");
+       RNA_def_property_update(prop, NC_SCENE|ND_SEQUENCER, "rna_Sequence_update_reopen_files");
+
        prop= RNA_def_property(srna, "elements", PROP_COLLECTION, PROP_NONE);
        RNA_def_property_collection_sdna(prop, NULL, "strip->stripdata", NULL);
        RNA_def_property_struct_type(prop, "SequenceElement");
index b79d5395eec7a668e757a3e68b5cbff9070c4a10..0166baa84434a3de522044508a5997690ff5ed9e 100644 (file)
@@ -1674,6 +1674,7 @@ static void rna_def_space_sequencer(BlenderRNA *brna)
                {SEQ_PROXY_RENDER_SIZE_25, "PROXY_25", 0, "Proxy size 25%", ""},
                {SEQ_PROXY_RENDER_SIZE_50, "PROXY_50", 0, "Proxy size 50%", ""},
                {SEQ_PROXY_RENDER_SIZE_75, "PROXY_75", 0, "Proxy size 75%", ""},
+               {SEQ_PROXY_RENDER_SIZE_100, "PROXY_100", 0, "Proxy size 100%", ""},
                {SEQ_PROXY_RENDER_SIZE_FULL, "FULL", 0, "No proxy, full render", ""},
                {0, NULL, 0, NULL, NULL}};
        
index e1b8cefca4bab27cf1900f3ae68badca67842d6b..5bdf1ec2787427caa9074fb50e37cc5b06a3eed4 100644 (file)
@@ -300,6 +300,8 @@ int                 WM_jobs_test(struct wmWindowManager *wm, void *owner);
 float          WM_jobs_progress(struct wmWindowManager *wm, void *owner);
 char           *WM_jobs_name(struct wmWindowManager *wm, void *owner);
 
+int             WM_jobs_is_running(struct wmJob *);
+void*           WM_jobs_get_customdata(struct wmJob *);
 void           WM_jobs_customdata(struct wmJob *, void *customdata, void (*free)(void *));
 void           WM_jobs_timer(struct wmJob *, double timestep, unsigned int note, unsigned int endnote);
 void           WM_jobs_callbacks(struct wmJob *, 
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 */