use BLI_strncpy and BLI_snprintf when the size of the string is known.
[blender-staging.git] / source / blender / imbuf / intern / anim_movie.c
index 344601d734669bf89444929312128b7654a2f33a..a09033795285aa2dd88833ebad666ab80794833d 100644 (file)
@@ -1,8 +1,4 @@
-/**
- * anim.c
- *
- * $Id: anim_movie.c 34160 2011-01-07 19:18:31Z campbellbarton $
- *
+/*
  * ***** BEGIN GPL LICENSE BLOCK *****
  *
  * This program is free software; you can redistribute it and/or
  * ***** END GPL LICENSE BLOCK *****
  */
 
+/** \file blender/imbuf/intern/anim_movie.c
+ *  \ingroup imbuf
+ */
+
+
 #ifdef _WIN32
 #define INC_OLE2
 #include <windows.h>
@@ -52,6 +53,7 @@
 #include <ctype.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <math.h>
 #ifndef _WIN32
 #include <dirent.h>
 #else
@@ -61,6 +63,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"
 
@@ -85,6 +88,7 @@
 
 #include "IMB_allocimbuf.h"
 #include "IMB_anim.h"
+#include "IMB_indexer.h"
 
 #ifdef WITH_FFMPEG
 #include <libavformat/avformat.h>
 #include <libavutil/rational.h>
 #include <libswscale/swscale.h>
 
-#if LIBAVFORMAT_VERSION_INT < (49 << 16)
-#define FFMPEG_OLD_FRAME_RATE 1
-#else
-#define FFMPEG_CODEC_IS_POINTER 1
-#endif
-
-#if (LIBAVCODEC_VERSION_MAJOR >= 52) && (LIBAVCODEC_VERSION_MINOR >= 29) && \
-        (LIBSWSCALE_VERSION_MAJOR >= 0) && (LIBSWSCALE_VERSION_MINOR >= 10)
-#define FFMPEG_SWSCALE_COLOR_SPACE_SUPPORT
-#endif
+#include "ffmpeg_compat.h"
 
 #endif //WITH_FFMPEG
 
 #endif
 #endif
 
-/****/
-
-#ifdef __sgi
-
-#include <dmedia/moviefile.h>
-
-static void movie_printerror(char * str) {
-       const char * errstr = mvGetErrorStr(mvGetErrno());
-
-       if (str) {
-               if (errstr) printf("%s: %s\n", str, errstr);
-               else printf("%s: returned error\n", str);
-       } else printf("%s\n", errstr);
-}
-
-static int startmovie(struct anim * anim) {
-       if (anim == 0) return(-1);
-
-       if ( mvOpenFile (anim->name, O_BINARY|O_RDONLY, &anim->movie ) != DM_SUCCESS ) {
-               printf("Can't open movie: %s\n", anim->name);
-               return(-1);
-       }
-       if ( mvFindTrackByMedium (anim->movie, DM_IMAGE, &anim->track) != DM_SUCCESS ) {
-               printf("No image track in movie: %s\n", anim->name);
-               mvClose(anim->movie);
-               return(-1);
-       }
-
-       anim->duration = mvGetTrackLength (anim->track);
-       anim->params = mvGetParams( anim->track );
-
-       anim->x = dmParamsGetInt( anim->params, DM_IMAGE_WIDTH);
-       anim->y = dmParamsGetInt( anim->params, DM_IMAGE_HEIGHT);
-       anim->interlacing = dmParamsGetEnum (anim->params, DM_IMAGE_INTERLACING);
-       anim->orientation = dmParamsGetEnum (anim->params, DM_IMAGE_ORIENTATION);
-       anim->framesize = dmImageFrameSize(anim->params);
-
-       anim->curposition = 0;
-       anim->preseek = 0;
-
-       /*printf("x:%d y:%d size:%d interl:%d dur:%d\n", anim->x, anim->y, anim->framesize, anim->interlacing, anim->duration);*/
-       return (0);
-}
-
-static ImBuf * movie_fetchibuf(struct anim * anim, int position) {
-       ImBuf * ibuf;
-/*     extern rectcpy(); */
-       int size;
-       unsigned int *rect1, *rect2;
-
-       if (anim == 0) return (0);
-
-       ibuf = IMB_allocImBuf(anim->x, anim->y, 24, IB_rect);
-
-       if ( mvReadFrames(anim->track, position, 1, ibuf->x * ibuf->y * 
-               sizeof(int), ibuf->rect ) != DM_SUCCESS ) {
-               movie_printerror("mvReadFrames");
-               IMB_freeImBuf(ibuf);
-               return(0);
-       }
-
-/*
-       if (anim->interlacing == DM_IMAGE_INTERLACED_EVEN) {
-               rect1 = ibuf->rect + (ibuf->x * ibuf->y) - 1;
-               rect2 = rect1 - ibuf->x;
-    
-               for (size = ibuf->x * (ibuf->y - 1); size > 0; size--){
-                       *rect1-- = *rect2--;
-               }
-       }
-*/
-
-       if (anim->interlacing == DM_IMAGE_INTERLACED_EVEN)
-       {
-               rect1 = ibuf->rect;
-               rect2 = rect1 + ibuf->x;
-
-               for (size = ibuf->x * (ibuf->y - 1); size > 0; size--){
-                       *rect1++ = *rect2++;
-               }
-       }
-       /*if (anim->orientation == DM_TOP_TO_BOTTOM) IMB_flipy(ibuf);*/
-
-
-       return(ibuf);
-}
-
-static void free_anim_movie(struct anim * anim) {
-       if (anim == NULL) return;
-
-       if (anim->movie) {
-               mvClose(anim->movie);
-               anim->movie = NULL;
-       }
-       anim->duration = 0;
-}
-
-int ismovie(char *name) {
-       return (mvIsMovieFile(name) == DM_TRUE);
-}
-
-#else
-
-int ismovie(const char *UNUSED(name)) {
+int ismovie(const char *UNUSED(filepath))
+{
        return 0;
 }
 
@@ -227,7 +120,6 @@ static int startmovie(struct anim *UNUSED(anim)) { return 1; }
 static ImBuf * movie_fetchibuf(struct anim *UNUSED(anim), int UNUSED(position)) { return NULL; }
 static void free_anim_movie(struct anim *UNUSED(anim)) { ; }
 
-#endif
 
 #if defined(_WIN32)
 # define PATHSEPERATOR '\\'
@@ -235,7 +127,8 @@ static void free_anim_movie(struct anim *UNUSED(anim)) { ; }
 # define PATHSEPERATOR '/'
 #endif
 
-static int an_stringdec(const char *string, char* head, char *tail, unsigned short *numlen) {
+static int an_stringdec(const char *string, char* head, char *tail, unsigned short *numlen)
+{
        unsigned short len,nume,nums=0;
        short i,found=FALSE;
 
@@ -270,11 +163,13 @@ static int an_stringdec(const char *string, char* head, char *tail, unsigned sho
 }
 
 
-static void an_stringenc(char *string, const char *head, const char *tail, unsigned short numlen, int pic) {
+static void an_stringenc(char *string, const char *head, const char *tail, unsigned short numlen, int pic)
+{
        BLI_stringenc(string, head, tail, numlen, pic);
 }
 
-static void free_anim_avi (struct anim *anim) {
+static void free_anim_avi (struct anim *anim)
+{
 #if defined(_WIN32) && !defined(FREE_WINDOWS)
        int i;
 #endif
@@ -308,15 +203,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
@@ -324,13 +210,13 @@ static void free_anim_ffmpeg(struct anim * anim);
 static void free_anim_redcode(struct anim * anim);
 #endif
 
-void IMB_free_anim(struct anim * anim) {
+void IMB_free_anim(struct anim * anim)
+{
        if (anim == NULL) {
                printf("free anim, anim == NULL\n");
                return;
        }
 
-       IMB_free_anim_ibuf(anim);
        free_anim_movie(anim);
        free_anim_avi(anim);
 
@@ -343,39 +229,47 @@ void IMB_free_anim(struct anim * anim) {
 #ifdef WITH_REDCODE
        free_anim_redcode(anim);
 #endif
+       IMB_free_indices(anim);
 
        MEM_freeN(anim);
 }
 
-void IMB_close_anim(struct anim * anim) {
-       if (anim == 0) return;
+void IMB_close_anim(struct anim * anim)
+{
+       if (anim == NULL) return;
 
        IMB_free_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) {
-               strcpy(anim->name, name);  /* fixme: possible buffer overflow here? */
+               BLI_strncpy(anim->name, name, sizeof(anim->name));
                anim->ib_flags = ib_flags;
+               anim->streamindex = streamindex;
        }
        return(anim);
 }
 
 
-static int startavi (struct anim *anim) {
+static int startavi (struct anim *anim)
+{
 
        AviError avierror;
 #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");
@@ -400,6 +294,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;
@@ -449,7 +347,7 @@ static int startavi (struct anim *anim) {
        }
        
        anim->duration = anim->avi->header->TotalFrames;
-       anim->params = 0;
+       anim->params = NULL;
 
        anim->x = anim->avi->header->Width;
        anim->y = anim->avi->header->Height;
@@ -465,7 +363,8 @@ static int startavi (struct anim *anim) {
        return 0;
 }
 
-static ImBuf * avi_fetchibuf (struct anim *anim, int position) {
+static ImBuf * avi_fetchibuf (struct anim *anim, int position)
+{
        ImBuf *ibuf = NULL;
        int *tmp;
        int y;
@@ -479,7 +378,7 @@ static ImBuf * avi_fetchibuf (struct anim *anim, int position) {
                if (anim->pgf) {
                        lpbi = AVIStreamGetFrame(anim->pgf, position + AVIStreamStart(anim->pavi[anim->firstvideo]));
                        if (lpbi) {
-                               ibuf = IMB_ibImageFromMemory((unsigned char *) lpbi, 100, IB_rect);
+                               ibuf = IMB_ibImageFromMemory((unsigned char *) lpbi, 100, IB_rect, "<avi_fetchibuf>");
 //Oh brother...
                        }
                }
@@ -500,14 +399,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;
 }
 
@@ -515,24 +414,16 @@ static ImBuf * avi_fetchibuf (struct anim *anim, int position) {
 
 extern void do_init_ffmpeg(void);
 
-#ifdef FFMPEG_CODEC_IS_POINTER
-static AVCodecContext* get_codec_from_stream(AVStream* stream)
+static int startffmpeg(struct anim * anim)
 {
-       return stream->codec;
-}
-#else
-static AVCodecContext* get_codec_from_stream(AVStream* stream)
-{
-       return &stream->codec;
-}
-#endif
-
-static int startffmpeg(struct anim * anim) {
        int            i, videoStream;
 
        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 */
@@ -543,6 +434,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) {
@@ -554,15 +447,20 @@ static int startffmpeg(struct anim * anim) {
                return -1;
        }
 
-       dump_format(pFormatCtx, 0, anim->name, 0);
+       av_dump_format(pFormatCtx, 0, anim->name, 0);
+
 
+       /* Find the video stream */
+       videoStream = -1;
 
-               /* Find the first video stream */
-       videoStream=-1;
-       for(i=0; i<pFormatCtx->nb_streams; i++)
-               if(get_codec_from_stream(pFormatCtx->streams[i])->codec_type
-                  == CODEC_TYPE_VIDEO) {
-                       videoStream=i;
+       for(i = 0; i < pFormatCtx->nb_streams; i++)
+               if (pFormatCtx->streams[i]->codec->codec_type
+                  == AVMEDIA_TYPE_VIDEO) {
+                       if (streamcount > 0) {
+                               streamcount--;
+                               continue;
+                       }
+                       videoStream = i;
                        break;
                }
 
@@ -571,35 +469,39 @@ static int startffmpeg(struct anim * anim) {
                return -1;
        }
 
-       pCodecCtx = get_codec_from_stream(pFormatCtx->streams[videoStream]);
+       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;
        }
 
-#ifdef FFMPEG_OLD_FRAME_RATE
-       if(pCodecCtx->frame_rate>1000 && pCodecCtx->frame_rate_base==1)
-               pCodecCtx->frame_rate_base=1000;
-
-
-       anim->duration = pFormatCtx->duration * pCodecCtx->frame_rate 
-               / pCodecCtx->frame_rate_base / AV_TIME_BASE;
-#else
        anim->duration = ceil(pFormatCtx->duration
                * av_q2d(pFormatCtx->streams[videoStream]->r_frame_rate) 
                / AV_TIME_BASE);
 
-#endif
+       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;
@@ -609,6 +511,10 @@ 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_packet.stream_index = -1;
 
        anim->pFormatCtx = pFormatCtx;
        anim->pCodecCtx = pCodecCtx;
@@ -616,6 +522,7 @@ static int startffmpeg(struct anim * anim) {
        anim->videoStream = videoStream;
 
        anim->pFrame = avcodec_alloc_frame();
+       anim->pFrameComplete = FALSE;
        anim->pFrameDeinterlaced = avcodec_alloc_frame();
        anim->pFrameRGB = avcodec_alloc_frame();
 
@@ -633,12 +540,12 @@ static int startffmpeg(struct anim * anim) {
        }
 
        if (anim->ib_flags & IB_animdeinterlace) {
-               avpicture_fill((AVPicture*) anim->pFrameDeinterlaced, 
-                                  MEM_callocN(avpicture_get_size(
-                                                  anim->pCodecCtx->pix_fmt,
-                                                  anim->x, anim->y), 
-                                          "ffmpeg deinterlace"), 
-                                  anim->pCodecCtx->pix_fmt, anim->x, anim->y);
+               avpicture_fill((AVPicture*) anim->pFrameDeinterlaced,
+                              MEM_callocN(avpicture_get_size(
+                                              anim->pCodecCtx->pix_fmt,
+                                              anim->x, anim->y),
+                                          "ffmpeg deinterlace"),
+                              anim->pCodecCtx->pix_fmt, anim->x, anim->y);
        }
 
        if (pCodecCtx->has_b_frames) {
@@ -691,10 +598,23 @@ 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;
+
+       if (!anim->pFrameComplete) {
+               return;
+       }
 
        /* This means the data wasnt read properly, 
           this check stops crashing */
@@ -705,6 +625,12 @@ static void ffmpeg_postprocess(struct anim * anim, ImBuf * ibuf,
                return;
        }
 
+       av_log(anim->pFormatCtx, AV_LOG_DEBUG, 
+              "  POSTPROC: anim->pFrame planes: %p %p %p %p\n",
+              input->data[0], input->data[1], input->data[2],
+              input->data[3]);
+
+
        if (anim->ib_flags & IB_animdeinterlace) {
                if (avpicture_deinterlace(
                            (AVPicture*) 
@@ -715,12 +641,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;
@@ -731,16 +661,12 @@ static void ffmpeg_postprocess(struct anim * anim, ImBuf * ibuf,
                unsigned char* top;
                
                sws_scale(anim->img_convert_ctx,
-                         (const uint8_t * const *)input->data,
-                         input->linesize,
-                         0,
-                         anim->pCodecCtx->height,
-                         dst2,
-                         dstStride2);
-               
-               /* workaround: sws_scale bug
-                  sets alpha = 0 and compensate
-                  for altivec-bugs and flipy... */
+                         (const uint8_t * const *)input->data,
+                         input->linesize,
+                         0,
+                         anim->pCodecCtx->height,
+                         dst2,
+                         dstStride2);
                
                bottom = (unsigned char*) ibuf->rect;
                top = bottom + ibuf->x * (ibuf->y-1) * 4;
@@ -752,17 +678,17 @@ static void ffmpeg_postprocess(struct anim * anim, ImBuf * ibuf,
                        unsigned char tmp[4];
                        unsigned int * tmp_l =
                                (unsigned int*) tmp;
-                       tmp[3] = 0xff;
                        
                        for (x = 0; x < w; x++) {
                                tmp[0] = bottom[0];
                                tmp[1] = bottom[1];
                                tmp[2] = bottom[2];
+                               tmp[3] = bottom[3];
                                
                                bottom[0] = top[0];
                                bottom[1] = top[1];
                                bottom[2] = top[2];
-                               bottom[3] = 0xff;
+                               bottom[3] = top[3];
                                
                                *(unsigned int*) top = *tmp_l;
                                
@@ -777,181 +703,346 @@ static void ffmpeg_postprocess(struct anim * anim, ImBuf * ibuf,
                int dstStride2[4] = { -dstStride[0], 0, 0, 0 };
                uint8_t* dst2[4]  = { dst[0] + (anim->y - 1)*dstStride[0],
                                      0, 0, 0 };
-               int i;
-               unsigned char* r;
                
                sws_scale(anim->img_convert_ctx,
-                         (const uint8_t * const *)input->data,
-                         input->linesize,
-                         0,
-                         anim->pCodecCtx->height,
-                         dst2,
-                         dstStride2);
-               
-               r = (unsigned char*) ibuf->rect;
-               
-               /* workaround sws_scale bug: older version of 
-                  sws_scale set alpha = 0... */
-               if (r[3] == 0) {
-                       for (i = 0; i < ibuf->x * ibuf->y; i++) {
-                               r[3] = 0xff;
-                               r += 4;
+                         (const uint8_t * const *)input->data,
+                         input->linesize,
+                         0,
+                         anim->pCodecCtx->height,
+                         dst2,
+                         dstStride2);
+       }
+
+       if (filter_y) {
+               IMB_filtery(ibuf);
+       }
+}
+
+/* decode one video frame also considering the packet read into next_packet */
+
+static int ffmpeg_decode_video_frame(struct anim * anim)
+{
+       int rval = 0;
+
+       av_log(anim->pFormatCtx, AV_LOG_DEBUG, "  DECODE VIDEO FRAME\n");
+
+       if (anim->next_packet.stream_index == anim->videoStream) {
+               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:
+                      (long long int)anim->next_packet.dts,
+                      (anim->next_packet.pts == AV_NOPTS_VALUE) ? -1:
+                      (long long int)anim->next_packet.pts,
+                      (anim->next_packet.flags & AV_PKT_FLAG_KEY) ? 
+                      " KEY" : "");
+               if (anim->next_packet.stream_index == anim->videoStream) {
+                       anim->pFrameComplete = 0;
+
+                       avcodec_decode_video2(
+                               anim->pCodecCtx, 
+                               anim->pFrame, &anim->pFrameComplete, 
+                               &anim->next_packet);
+
+                       if (anim->pFrameComplete) {
+                               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 : (long long int)anim->pFrame->pts,
+                                      (anim->pFrame->pkt_pts 
+                                       == AV_NOPTS_VALUE) ?
+                                      -1 : (long long int)anim->pFrame->pkt_pts,
+                                       (long long int)anim->next_pts);
+                               break;
                        }
                }
+               av_free_packet(&anim->next_packet);
+               anim->next_packet.stream_index = -1;
+       }
+       
+       if (rval < 0) {
+               anim->next_packet.stream_index = -1;
+
+               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", 
+              (long long int)anim->next_pts, (long long int)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", 
+                      (long long int)anim->next_pts, (long long int)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", 
+                      (long long int)anim->next_pts, (long long int)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 ImBuf * ffmpeg_fetchibuf(struct anim * anim, int position) {
-       ImBuf * ibuf;
-       int frameFinished;
-       AVPacket packet;
+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 [] = { "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 = 0; /* To quite gcc barking... */
+       int old_frame_index = 0; /* To quite gcc barking... */
 
        if (anim == 0) return (0);
 
-       ibuf = IMB_allocImBuf(anim->x, anim->y, 32, IB_rect);
-
-       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_video(
-                                               anim->pCodecCtx, 
-                                               anim->pFrame, &frameFinished, 
-                                               packet.data, packet.size);
-
-                                       if (frameFinished) {
-                                               anim->curposition++;
-                                       }
-                               }
-                               av_free_packet(&packet);
-                               if (position == anim->curposition+1) {
-                                       break;
-                               }
-                       }
-               }
+       av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: pos=%d\n", position);
+
+       if (tc != IMB_TC_NONE) {
+               tc_index = IMB_anim_open_index(anim, tc);
        }
 
-/* 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
+       v_st = anim->pFormatCtx->streams[anim->videoStream];
 
-       if (position != anim->curposition + 1) { 
-#ifdef FFMPEG_OLD_FRAME_RATE
-               double frame_rate = 
-                       (double) anim->pCodecCtx->frame_rate
-                       / (double) anim->pCodecCtx->frame_rate_base;
-#else
-               double frame_rate = 
-                       av_q2d(anim->pFormatCtx->streams[anim->videoStream]
-                                  ->r_frame_rate);
-#endif
-               double pts_time_base = av_q2d(anim->pFormatCtx->streams[anim->videoStream]->time_base);
+       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;
+               }
+       }
+
+       av_log(anim->pFormatCtx, AV_LOG_DEBUG, 
+              "FETCH: looking for PTS=%lld "
+              "(pts_timebase=%g, frame_rate=%g, st_time=%lld)\n", 
+              (long long int)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",
+                      (long long int)anim->last_pts, 
+                      (long long int)anim->next_pts);
+               IMB_refImBuf(anim->last_frame);
+               anim->curposition = position;
+               return anim->last_frame;
+       }
+        
+       if (position > anim->curposition + 1 
+           && anim->preseek 
+           && !tc_index
+           && position - (anim->curposition + 1) < anim->preseek) {
+               av_log(anim->pFormatCtx, AV_LOG_DEBUG, 
+                      "FETCH: within preseek interval (no index)\n");
+
+               ffmpeg_decode_video_frame_scan(anim, pts_to_search);
+       } else if (tc_index && 
+                  IMB_indexer_can_scan(tc_index, old_frame_index,
+                                       new_frame_index)) {
+
+               av_log(anim->pFormatCtx, AV_LOG_DEBUG, 
+                      "FETCH: within preseek interval "
+                      "(index tells us)\n");
+
+               ffmpeg_decode_video_frame_scan(anim, pts_to_search);
+       } else if (position != anim->curposition + 1) { 
                long long pos;
-               long long st_time = anim->pFormatCtx->start_time;
                int ret;
 
-               if (seek_by_bytes) {
-                       pos = position - anim->preseek;
-                       if (pos < 0) {
-                               pos = 0;
-                       }
-                       preseek_count = position - pos;
+               if (tc_index) {
+                       unsigned long long dts;
+
+                       pos = 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;
+
+                       av_log(anim->pFormatCtx, AV_LOG_DEBUG, 
+                              "NO INDEX seek pos = %lld, st_time = %lld\n", 
+                              pos, (st_time != AV_NOPTS_VALUE) ? st_time : 0);
+
                        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);
+                       av_log(anim->pFormatCtx, AV_LOG_DEBUG, 
+                              "NO INDEX final seek pos = %lld\n", pos);
+
+                       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, (long long int)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_video(anim->pCodecCtx, 
-                                                anim->pFrame, &frameFinished, 
-                                                packet.data, packet.size);
+               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);
+       } else {
+               av_log(anim->pFormatCtx, AV_LOG_DEBUG, 
+                      "FETCH: no seek necessary, just continue...\n");
        }
 
-       if (filter_y && ibuf) {
-               IMB_filtery(ibuf);
-       }
+       IMB_freeImBuf(anim->last_frame);
+       anim->last_frame = IMB_allocImBuf(anim->x, anim->y, 32, IB_rect);
 
-       ibuf->profile = IB_PROFILE_SRGB;
+       ffmpeg_postprocess(anim);
+
+       anim->last_pts = anim->next_pts;
        
-       return(ibuf);
+       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) {
+static void free_anim_ffmpeg(struct anim * anim)
+{
        if (anim == NULL) return;
 
        if (anim->pCodecCtx) {
@@ -965,6 +1056,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;
 }
@@ -973,7 +1068,8 @@ static void free_anim_ffmpeg(struct anim * anim) {
 
 #ifdef WITH_REDCODE
 
-static int startredcode(struct anim * anim) {
+static int startredcode(struct anim * anim)
+{
        anim->redcodeCtx = redcode_open(anim->name);
        if (!anim->redcodeCtx) {
                return -1;
@@ -983,7 +1079,8 @@ static int startredcode(struct anim * anim) {
        return 0;
 }
 
-static ImBuf * redcode_fetchibuf(struct anim * anim, int position) {
+static ImBuf * redcode_fetchibuf(struct anim * anim, int position)
+{
        struct ImBuf * ibuf;
        struct redcode_frame * frame;
        struct redcode_frame_raw * raw_frame;
@@ -1007,14 +1104,15 @@ static ImBuf * redcode_fetchibuf(struct anim * anim, int position) {
        }
        
                ibuf = IMB_allocImBuf(raw_frame->width * 2, 
-                                 raw_frame->height * 2, 32, IB_rectfloat);
+                                     raw_frame->height * 2, 32, IB_rectfloat);
 
        redcode_decode_video_float(raw_frame, ibuf->rect_float, 1);
 
        return ibuf;
 }
 
-static void free_anim_redcode(struct anim * anim) {
+static void free_anim_redcode(struct anim * anim)
+{
        if (anim->redcodeCtx) {
                redcode_close(anim->redcodeCtx);
                anim->redcodeCtx = 0;
@@ -1028,10 +1126,11 @@ static void free_anim_redcode(struct anim * anim) {
 /* Geen plaatje, probeer dan volgende animatie te openen */
 /* gelukt, haal dan eerste plaatje van animatie */
 
-static struct ImBuf * anim_getnew(struct anim * anim) {
-       struct ImBuf *ibuf = 0;
+static struct ImBuf * anim_getnew(struct anim * anim)
+{
+       struct ImBuf *ibuf = NULL;
 
-       if (anim == NULL) return(0);
+       if (anim == NULL) return(NULL);
 
        free_anim_movie(anim);
        free_anim_avi(anim);
@@ -1046,25 +1145,25 @@ static struct ImBuf * anim_getnew(struct anim * anim) {
 #endif
 
 
-       if (anim->curtype != 0) return (0);
+       if (anim->curtype != 0) return (NULL);
        anim->curtype = imb_get_anim_type(anim->name);  
 
        switch (anim->curtype) {
        case ANIM_SEQUENCE:
                ibuf = IMB_loadiffname(anim->name, anim->ib_flags);
                if (ibuf) {
-                       strcpy(anim->first, anim->name);
+                       BLI_strncpy(anim->first, anim->name, sizeof(anim->first));
                        anim->duration = 1;
                }
                break;
        case ANIM_MOVIE:
-               if (startmovie(anim)) return (0);
+               if (startmovie(anim)) return (NULL);
                ibuf = IMB_allocImBuf (anim->x, anim->y, 24, 0); /* fake */
                break;
        case ANIM_AVI:
                if (startavi(anim)) {
                        printf("couldnt start avi\n"); 
-                       return (0);
+                       return (NULL);
                }
                ibuf = IMB_allocImBuf (anim->x, anim->y, 24, 0);
                break;
@@ -1090,41 +1189,57 @@ static struct ImBuf * anim_getnew(struct anim * anim) {
        return(ibuf);
 }
 
-struct ImBuf * IMB_anim_previewframe(struct anim * anim) {
-       struct ImBuf * ibuf = 0;
+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 * ibuf = 0;
+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;
        int pic;
        int filter_y;
-       if (anim == NULL) return(0);
+       if (anim == NULL) return(NULL);
 
        filter_y = (anim->ib_flags & IB_animdeinterlace);
 
        if (anim->curtype == 0) {
                ibuf = anim_getnew(anim);
                if (ibuf == NULL) {
-                       return (0);
+                       return(NULL);
                }
 
                IMB_freeImBuf(ibuf); /* ???? */
                ibuf= NULL;
        }
 
-       if (position < 0) return(0);
-       if (position >= anim->duration) return(0);
+       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:
@@ -1158,7 +1273,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 */
@@ -1174,7 +1289,7 @@ struct ImBuf * IMB_anim_absolute(struct anim * anim, int position) {
 
        if (ibuf) {
                if (filter_y) IMB_filtery(ibuf);
-               sprintf(ibuf->name, "%s.%04d", anim->name, anim->curposition + 1);
+               BLI_snprintf(ibuf->name, sizeof(ibuf->name), "%s.%04d", anim->name, anim->curposition + 1);
                
        }
        return(ibuf);
@@ -1182,8 +1297,30 @@ 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)