diff options
author | Joerg Mueller <nexyon@gmail.com> | 2011-08-30 12:22:03 +0400 |
---|---|---|
committer | Joerg Mueller <nexyon@gmail.com> | 2011-08-30 12:22:03 +0400 |
commit | 43ab8e86247b7889d16d915b4f370ceb618aaad4 (patch) | |
tree | 64b1fec1adcc399eb1de5cd86ebafd753a921d8d /source/blender/imbuf | |
parent | 5b5e600db6f529ad7e1af9d4bb3a193be2265342 (diff) | |
parent | d049a722fea3d150fbfad06ffdbbb5c150717134 (diff) |
* Merge trunk up to r39790.soc-2011-pepper
* Subversion bump (also for init_userdef_do_versions).
* Minor fix for compilation without ffmpeg.
Diffstat (limited to 'source/blender/imbuf')
-rw-r--r-- | source/blender/imbuf/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/imbuf/IMB_imbuf.h | 69 | ||||
-rw-r--r-- | source/blender/imbuf/intern/IMB_anim.h | 24 | ||||
-rw-r--r-- | source/blender/imbuf/intern/IMB_indexer.h | 133 | ||||
-rw-r--r-- | source/blender/imbuf/intern/allocimbuf.c | 13 | ||||
-rw-r--r-- | source/blender/imbuf/intern/anim_movie.c | 566 | ||||
-rw-r--r-- | source/blender/imbuf/intern/dds/DirectDrawSurface.cpp | 16 | ||||
-rw-r--r-- | source/blender/imbuf/intern/filter.c | 2 | ||||
-rw-r--r-- | source/blender/imbuf/intern/indexer.c | 1142 | ||||
-rw-r--r-- | source/blender/imbuf/intern/indexer_dv.c | 391 | ||||
-rw-r--r-- | source/blender/imbuf/intern/thumbs.c | 4 | ||||
-rw-r--r-- | source/blender/imbuf/intern/tiff.c | 2 | ||||
-rw-r--r-- | source/blender/imbuf/intern/util.c | 7 |
13 files changed, 2206 insertions, 164 deletions
diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt index 18b5eff5c73..ff13be20d4e 100644 --- a/source/blender/imbuf/CMakeLists.txt +++ b/source/blender/imbuf/CMakeLists.txt @@ -73,6 +73,7 @@ set(SRC intern/tiff.c intern/util.c intern/writeimage.c + intern/indexer.c IMB_imbuf.h IMB_imbuf_types.h diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 36123592c54..1fbe8e01fd4 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -133,6 +133,7 @@ struct ImBuf *IMB_allocImBuf(unsigned int x, unsigned int y, */ void IMB_refImBuf(struct ImBuf *ibuf); +struct ImBuf * IMB_makeSingleUser(struct ImBuf *ibuf); /** * @@ -193,17 +194,70 @@ void IMB_rectblend(struct ImBuf *dbuf, struct ImBuf *sbuf, int destx, int desty, int srcx, int srcy, int width, int height, IMB_BlendMode mode); /** + * + * @attention Defined in indexer.c + */ + +typedef enum IMB_Timecode_Type { + IMB_TC_NONE = 0, /* don't use timecode files at all */ + IMB_TC_RECORD_RUN = 1, /* use images in the order as they are recorded + (currently, this is the only one implemented + and is a sane default) + */ + IMB_TC_FREE_RUN = 2, /* use global timestamp written by recording + device (prosumer camcorders e.g. can do + that) */ + IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN = 4, + /* interpolate a global timestamp using the + record date and time written by recording + device (*every* consumer camcorder can do + that :) )*/ + IMB_TC_MAX_SLOT = 3 +} IMB_Timecode_Type; + +typedef enum IMB_Proxy_Size { + IMB_PROXY_NONE = 0, + IMB_PROXY_25 = 1, + IMB_PROXY_50 = 2, + IMB_PROXY_75 = 4, + IMB_PROXY_100 = 8, + IMB_PROXY_MAX_SLOT = 4 +} IMB_Proxy_Size; + +/* defaults to BL_proxy within the directory of the animation */ +void IMB_anim_set_index_dir(struct anim * anim, const char * dir); + +int IMB_anim_index_get_frame_index(struct anim * anim, IMB_Timecode_Type tc, + int position); + +/* will rebuild all used indices and proxies at once */ +void IMB_anim_index_rebuild(struct anim * anim, + IMB_Timecode_Type build_tcs, + IMB_Proxy_Size build_preview_sizes, + int build_quality, + short *stop, short *do_update, float *progress); + +/** * Return the length (in frames) of the given @a anim. */ -int IMB_anim_get_duration(struct anim *anim); +int IMB_anim_get_duration(struct anim *anim, IMB_Timecode_Type tc); + + +/** + * Return the fps contained in movie files (function rval is FALSE, + * and frs_sec and frs_sec_base untouched if none available!) + */ +int IMB_anim_get_fps(struct anim * anim, + short * frs_sec, float * frs_sec_base); /** * * @attention Defined in anim.c */ -struct anim *IMB_open_anim(const char *name, int ib_flags); +struct anim *IMB_open_anim(const char *name, int ib_flags, int streamindex); void IMB_close_anim(struct anim *anim); + /** * * @attention Defined in anim.c @@ -218,7 +272,10 @@ int IMB_anim_get_preseek(struct anim *anim); * @attention Defined in anim.c */ -struct ImBuf *IMB_anim_absolute(struct anim *anim, int position); +struct ImBuf *IMB_anim_absolute( + struct anim *anim, int position, + IMB_Timecode_Type tc /* = 1 = IMB_TC_RECORD_RUN */, + IMB_Proxy_Size preview_size /* = 0 = IMB_PROXY_NONE */); /** * @@ -231,12 +288,6 @@ struct ImBuf *IMB_anim_previewframe(struct anim *anim); * * @attention Defined in anim.c */ -void IMB_free_anim_ibuf(struct anim *anim); - -/** - * - * @attention Defined in anim.c - */ void IMB_free_anim(struct anim *anim); /** diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h index fba0772dd93..8436846bf2e 100644 --- a/source/blender/imbuf/intern/IMB_anim.h +++ b/source/blender/imbuf/intern/IMB_anim.h @@ -127,19 +127,22 @@ #define MAXNUMSTREAMS 50 struct _AviMovie; +struct anim_index; struct anim { int ib_flags; int curtype; int curposition; /* index 0 = 1e, 1 = 2e, enz. */ int duration; + short frs_sec; + float frs_sec_base; int x, y; /* voor op nummer */ char name[256]; /* voor sequence */ char first[256]; - + /* movie */ void *movie; void *track; @@ -148,9 +151,7 @@ struct anim { size_t framesize; int interlacing; int preseek; - - /* data */ - struct ImBuf * ibuf1, * ibuf2; + int streamindex; /* avi */ struct _AviMovie *avi; @@ -179,11 +180,26 @@ struct anim { AVFrame *pFrameDeinterlaced; struct SwsContext *img_convert_ctx; int videoStream; + + struct ImBuf * last_frame; + int64_t last_pts; + int64_t next_pts; + int64_t next_undecoded_pts; + AVPacket next_packet; #endif #ifdef WITH_REDCODE struct redcode_handle * redcodeCtx; #endif + + char index_dir[256]; + + int proxies_tried; + int indices_tried; + + struct anim * proxy_anim[IMB_PROXY_MAX_SLOT]; + struct anim_index * curr_idx[IMB_TC_MAX_SLOT]; + }; #endif diff --git a/source/blender/imbuf/intern/IMB_indexer.h b/source/blender/imbuf/intern/IMB_indexer.h new file mode 100644 index 00000000000..f55420fd106 --- /dev/null +++ b/source/blender/imbuf/intern/IMB_indexer.h @@ -0,0 +1,133 @@ +/** + * $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. + * + * + * Contributor(s): Peter Schlaile + * + * ***** END GPL LICENSE BLOCK ***** + */ + + +#ifndef IMB_INDEXER_H +#define IMB_INDEXER_H + +#ifdef WIN32 +#include <io.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include "BKE_utildefines.h" +#include "IMB_anim.h" + +/* + seperate animation index files to solve the following problems: + + a) different timecodes within one file (like DTS/PTS, Timecode-Track, + "implicit" timecodes within DV-files and HDV-files etc.) + b) seeking difficulties within ffmpeg for files with timestamp holes + c) broken files that miss several frames / have varying framerates + d) use proxies accordingly + + ... we need index files, that provide us with + + the binary(!) position, where we have to seek into the file *and* + the continuous frame number (ignoring the holes) starting from the + beginning of the file, so that we know, which proxy frame to serve. + + This index has to be only built once for a file and is written into + the BL_proxy directory structure for later reuse in different blender files. + +*/ + +typedef struct anim_index_entry { + int frameno; + unsigned long long seek_pos; + unsigned long long seek_pos_dts; + unsigned long long pts; +} anim_index_entry; + +struct anim_index { + char name[256]; + + int num_entries; + struct anim_index_entry * entries; +}; + +struct anim_index_builder; + +typedef struct anim_index_builder { + FILE * fp; + char name[FILE_MAXDIR + FILE_MAXFILE]; + char temp_name[FILE_MAXDIR + FILE_MAXFILE]; + + void * private_data; + + void (*delete_priv_data)(struct anim_index_builder * idx); + void (*proc_frame)(struct anim_index_builder * idx, + unsigned char * buffer, + int data_size, + struct anim_index_entry * entry); +} anim_index_builder; + +anim_index_builder * IMB_index_builder_create(const char * name); +void IMB_index_builder_add_entry(anim_index_builder * fp, + int frameno, unsigned long long seek_pos, + unsigned long long seek_pos_dts, + unsigned long long pts); + +void IMB_index_builder_proc_frame(anim_index_builder * fp, + unsigned char * buffer, + int data_size, + int frameno, unsigned long long seek_pos, + unsigned long long seek_pos_dts, + unsigned long long pts); + +void IMB_index_builder_finish(anim_index_builder * fp, int rollback); + +struct anim_index * IMB_indexer_open(const char * name); +unsigned long long IMB_indexer_get_seek_pos( + struct anim_index * idx, int frameno_index); +unsigned long long IMB_indexer_get_seek_pos_dts( + struct anim_index * idx, int frameno_index); + +int IMB_indexer_get_frame_index(struct anim_index * idx, int frameno); +unsigned long long IMB_indexer_get_pts(struct anim_index * idx, + int frame_index); +int IMB_indexer_get_duration(struct anim_index * idx); + +int IMB_indexer_can_scan(struct anim_index * idx, + int old_frame_index, int new_frame_index); + +void IMB_indexer_close(struct anim_index * idx); + +void IMB_free_indices(struct anim * anim); + +int IMB_anim_index_get_frame_index( + struct anim * anim, IMB_Timecode_Type tc, int position); + +struct anim * IMB_anim_open_proxy( + struct anim * anim, IMB_Proxy_Size preview_size); +struct anim_index * IMB_anim_open_index( + struct anim * anim, IMB_Timecode_Type tc); + +int IMB_proxy_size_to_array_index(IMB_Proxy_Size pr_size); +int IMB_timecode_to_array_index(IMB_Timecode_Type tc); + +#endif diff --git a/source/blender/imbuf/intern/allocimbuf.c b/source/blender/imbuf/intern/allocimbuf.c index 59772771f3b..6ce6c9409d1 100644 --- a/source/blender/imbuf/intern/allocimbuf.c +++ b/source/blender/imbuf/intern/allocimbuf.c @@ -177,6 +177,19 @@ void IMB_refImBuf(ImBuf *ibuf) ibuf->refcounter++; } +ImBuf * IMB_makeSingleUser(ImBuf *ibuf) +{ + ImBuf * rval; + + if (!ibuf || ibuf->refcounter == 0) { return ibuf; } + + rval = IMB_dupImBuf(ibuf); + + IMB_freeImBuf(ibuf); + + return rval; +} + short addzbufImBuf(ImBuf *ibuf) { int size; diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 8b0104fcdca..7b172008bee 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -57,6 +57,7 @@ #include <ctype.h> #include <stdlib.h> #include <stdio.h> +#include <math.h> #ifndef _WIN32 #include <dirent.h> #else @@ -66,6 +67,7 @@ #include "BLI_blenlib.h" /* BLI_remlink BLI_filesize BLI_addtail BLI_countlist BLI_stringdec */ #include "BLI_utildefines.h" +#include "BLI_math_base.h" #include "MEM_guardedalloc.h" @@ -90,6 +92,7 @@ #include "IMB_allocimbuf.h" #include "IMB_anim.h" +#include "IMB_indexer.h" #ifdef WITH_FFMPEG #include <libavformat/avformat.h> @@ -304,15 +307,6 @@ static void free_anim_avi (struct anim *anim) { anim->duration = 0; } -void IMB_free_anim_ibuf(struct anim * anim) { - if (anim == NULL) return; - - if (anim->ibuf1) IMB_freeImBuf(anim->ibuf1); - if (anim->ibuf2) IMB_freeImBuf(anim->ibuf2); - - anim->ibuf1 = anim->ibuf2 = NULL; -} - #ifdef WITH_FFMPEG static void free_anim_ffmpeg(struct anim * anim); #endif @@ -326,7 +320,6 @@ void IMB_free_anim(struct anim * anim) { return; } - IMB_free_anim_ibuf(anim); free_anim_movie(anim); free_anim_avi(anim); @@ -339,6 +332,7 @@ void IMB_free_anim(struct anim * anim) { #ifdef WITH_REDCODE free_anim_redcode(anim); #endif + IMB_free_indices(anim); MEM_freeN(anim); } @@ -350,13 +344,14 @@ void IMB_close_anim(struct anim * anim) { } -struct anim * IMB_open_anim( const char * name, int ib_flags) { +struct anim * IMB_open_anim( const char * name, int ib_flags, int streamindex) { struct anim * anim; anim = (struct anim*)MEM_callocN(sizeof(struct anim), "anim struct"); if (anim != NULL) { BLI_strncpy(anim->name, name, sizeof(anim->name)); anim->ib_flags = ib_flags; + anim->streamindex = streamindex; } return(anim); } @@ -368,10 +363,13 @@ static int startavi (struct anim *anim) { #if defined(_WIN32) && !defined(FREE_WINDOWS) HRESULT hr; int i, firstvideo = -1; + int streamcount; BYTE abFormat[1024]; LONG l; LPBITMAPINFOHEADER lpbi; AVISTREAMINFO avis; + + streamcount = anim->streamindex; #endif anim->avi = MEM_callocN (sizeof(AviMovie),"animavi"); @@ -396,6 +394,10 @@ static int startavi (struct anim *anim) { AVIStreamInfo(anim->pavi[i], &avis, sizeof(avis)); if ((avis.fccType == streamtypeVIDEO) && (firstvideo == -1)) { + if (streamcount > 0) { + streamcount--; + continue; + } anim->pgf = AVIStreamGetFrameOpen(anim->pavi[i], NULL); if (anim->pgf) { firstvideo = i; @@ -496,14 +498,14 @@ static ImBuf * avi_fetchibuf (struct anim *anim, int position) { for (y=0; y < anim->y; y++) { memcpy (&(ibuf->rect)[((anim->y-y)-1)*anim->x], &tmp[y*anim->x], - anim->x * 4); + anim->x * 4); } MEM_freeN (tmp); } - + ibuf->profile = IB_PROFILE_SRGB; - + return ibuf; } @@ -517,6 +519,9 @@ static int startffmpeg(struct anim * anim) { AVCodec *pCodec; AVFormatContext *pFormatCtx; AVCodecContext *pCodecCtx; + int frs_num; + double frs_den; + int streamcount; #ifdef FFMPEG_SWSCALE_COLOR_SPACE_SUPPORT /* The following for color space determination */ @@ -527,6 +532,8 @@ static int startffmpeg(struct anim * anim) { if (anim == 0) return(-1); + streamcount = anim->streamindex; + do_init_ffmpeg(); if(av_open_input_file(&pFormatCtx, anim->name, NULL, 0, NULL)!=0) { @@ -541,12 +548,17 @@ static int startffmpeg(struct anim * anim) { av_dump_format(pFormatCtx, 0, anim->name, 0); - /* Find the first video stream */ - videoStream=-1; - for(i=0; i<pFormatCtx->nb_streams; i++) - if(pFormatCtx->streams[i]->codec->codec_type + /* Find the video stream */ + videoStream = -1; + + for(i = 0; i < pFormatCtx->nb_streams; i++) + if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { - videoStream=i; + if (streamcount > 0) { + streamcount--; + continue; + } + videoStream = i; break; } @@ -557,16 +569,16 @@ static int startffmpeg(struct anim * anim) { pCodecCtx = pFormatCtx->streams[videoStream]->codec; - /* Find the decoder for the video stream */ - pCodec=avcodec_find_decoder(pCodecCtx->codec_id); - if(pCodec==NULL) { + /* Find the decoder for the video stream */ + pCodec = avcodec_find_decoder(pCodecCtx->codec_id); + if(pCodec == NULL) { av_close_input_file(pFormatCtx); return -1; } pCodecCtx->workaround_bugs = 1; - if(avcodec_open(pCodecCtx, pCodec)<0) { + if(avcodec_open(pCodecCtx, pCodec) < 0) { av_close_input_file(pFormatCtx); return -1; } @@ -575,6 +587,19 @@ static int startffmpeg(struct anim * anim) { * av_q2d(pFormatCtx->streams[videoStream]->r_frame_rate) / AV_TIME_BASE); + frs_num = pFormatCtx->streams[videoStream]->r_frame_rate.num; + frs_den = pFormatCtx->streams[videoStream]->r_frame_rate.den; + + frs_den *= AV_TIME_BASE; + + while (frs_num % 10 == 0 && frs_den >= 2.0 && frs_num > 10) { + frs_num /= 10; + frs_den /= 10; + } + + anim->frs_sec = frs_num; + anim->frs_sec_base = frs_den; + anim->params = 0; anim->x = pCodecCtx->width; @@ -584,6 +609,11 @@ static int startffmpeg(struct anim * anim) { anim->framesize = anim->x * anim->y * 4; anim->curposition = -1; + anim->last_frame = 0; + anim->last_pts = -1; + anim->next_pts = -1; + anim->next_undecoded_pts = -1; + anim->next_packet.stream_index = -1; anim->pFormatCtx = pFormatCtx; anim->pCodecCtx = pCodecCtx; @@ -666,10 +696,19 @@ static int startffmpeg(struct anim * anim) { return (0); } -static void ffmpeg_postprocess(struct anim * anim, ImBuf * ibuf, - int * filter_y) +/* postprocess the image in anim->pFrame and do color conversion + and deinterlacing stuff. + + Output is anim->last_frame +*/ + +static void ffmpeg_postprocess(struct anim * anim) { AVFrame * input = anim->pFrame; + ImBuf * ibuf = anim->last_frame; + int filter_y = 0; + + ibuf->profile = IB_PROFILE_SRGB; /* This means the data wasnt read properly, this check stops crashing */ @@ -690,12 +729,16 @@ static void ffmpeg_postprocess(struct anim * anim, ImBuf * ibuf, anim->pCodecCtx->width, anim->pCodecCtx->height) < 0) { - *filter_y = 1; + filter_y = TRUE; } else { input = anim->pFrameDeinterlaced; } } + avpicture_fill((AVPicture*) anim->pFrameRGB, + (unsigned char*) ibuf->rect, + PIX_FMT_RGBA, anim->x, anim->y); + if (ENDIAN_ORDER == B_ENDIAN) { int * dstStride = anim->pFrameRGB->linesize; uint8_t** dst = anim->pFrameRGB->data; @@ -774,150 +817,359 @@ static void ffmpeg_postprocess(struct anim * anim, ImBuf * ibuf, } } } + + if (filter_y) { + IMB_filtery(ibuf); + } } -static ImBuf * ffmpeg_fetchibuf(struct anim * anim, int position) { - ImBuf * ibuf; - int frameFinished; - AVPacket packet; +/* decode one video frame and load the next packet into anim->packet, + so that we can obtain next_pts and next undecoded pts */ + +static int ffmpeg_decode_video_frame(struct anim * anim) +{ + int frameFinished = 0; + int rval = 0; + + av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE VIDEO FRAME\n"); + + anim->next_undecoded_pts = -1; + + if (anim->next_packet.stream_index == anim->videoStream) { + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + " DECODE: cached next packet\n"); + + avcodec_decode_video2(anim->pCodecCtx, + anim->pFrame, &frameFinished, + &anim->next_packet); + + if (frameFinished) { + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, + " FRAME DONE: " + "next_pts=%lld pkt_pts=%lld\n", + (anim->pFrame->pts == AV_NOPTS_VALUE) ? + -1 : anim->pFrame->pts, + (anim->pFrame->pkt_pts == AV_NOPTS_VALUE) ? + -1 : anim->pFrame->pkt_pts); + anim->next_pts = + av_get_pts_from_frame(anim->pFormatCtx, + anim->pFrame); + } + + av_free_packet(&anim->next_packet); + anim->next_packet.stream_index = -1; + } + + while((rval = av_read_frame(anim->pFormatCtx, &anim->next_packet)) >= 0) { + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, + "%sREAD: strID=%d (VID: %d) dts=%lld pts=%lld " + "%s\n", + (anim->next_packet.stream_index == anim->videoStream) + ? "->" : " ", + anim->next_packet.stream_index, + anim->videoStream, + (anim->next_packet.dts == AV_NOPTS_VALUE) ? -1: + anim->next_packet.dts, + (anim->next_packet.pts == AV_NOPTS_VALUE) ? -1: + anim->next_packet.pts, + (anim->next_packet.flags & AV_PKT_FLAG_KEY) ? + " KEY" : ""); + if (anim->next_packet.stream_index == anim->videoStream) { + if (frameFinished) { + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, + " FRAME finished, we leave\n"); + anim->next_undecoded_pts + = anim->next_packet.dts; + break; + } + + avcodec_decode_video2( + anim->pCodecCtx, + anim->pFrame, &frameFinished, + &anim->next_packet); + + if (frameFinished) { + anim->next_pts = av_get_pts_from_frame( + anim->pFormatCtx, anim->pFrame); + + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, + " FRAME DONE: next_pts=%lld " + "pkt_pts=%lld, guessed_pts=%lld\n", + (anim->pFrame->pts == AV_NOPTS_VALUE) ? + -1 : anim->pFrame->pts, + (anim->pFrame->pkt_pts + == AV_NOPTS_VALUE) ? + -1 : anim->pFrame->pkt_pts, + anim->next_pts); + } + } + av_free_packet(&anim->next_packet); + anim->next_packet.stream_index = -1; + } + + if (rval < 0) { + av_log(anim->pFormatCtx, + AV_LOG_ERROR, " DECODE READ FAILED: av_read_frame() " + "returned error: %d\n", rval); + } + return (rval >= 0); +} + +static void ffmpeg_decode_video_frame_scan( + struct anim * anim, int64_t pts_to_search) +{ + /* there seem to exist *very* silly GOP lengths out in the wild... */ + int count = 1000; + + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, + "SCAN start: considering pts=%lld in search of %lld\n", + anim->next_pts, pts_to_search); + + while (count > 0 && anim->next_pts < pts_to_search) { + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, + " WHILE: pts=%lld in search of %lld\n", + anim->next_pts, pts_to_search); + if (!ffmpeg_decode_video_frame(anim)) { + break; + } + count--; + } + if (count == 0) { + av_log(anim->pFormatCtx, + AV_LOG_ERROR, + "SCAN failed: completely lost in stream, " + "bailing out at PTS=%lld, searching for PTS=%lld\n", + anim->next_pts, pts_to_search); + } + if (anim->next_pts == pts_to_search) { + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, "SCAN HAPPY: we found our PTS!\n"); + } else { + av_log(anim->pFormatCtx, + AV_LOG_ERROR, "SCAN UNHAPPY: PTS not matched!\n"); + } +} + +static int match_format(const char *name, AVFormatContext * pFormatCtx) +{ + const char *p; + int len, namelen; + + const char *names = pFormatCtx->iformat->name; + + if (!name || !names) + return 0; + + namelen = strlen(name); + while ((p = strchr(names, ','))) { + len = MAX2(p - names, namelen); + if (!BLI_strncasecmp(name, names, len)) + return 1; + names = p+1; + } + return !BLI_strcasecmp(name, names); +} + +static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx) +{ + static const char * byte_seek_list [] = { "dv", "mpegts", 0 }; + const char ** p; + + if (pFormatCtx->iformat->flags & AVFMT_TS_DISCONT) { + return TRUE; + } + + p = byte_seek_list; + + while (*p) { + if (match_format(*p++, pFormatCtx)) { + return TRUE; + } + } + + return FALSE; +} + +static ImBuf * ffmpeg_fetchibuf(struct anim * anim, int position, + IMB_Timecode_Type tc) { int64_t pts_to_search = 0; - int pos_found = 1; - int filter_y = 0; - int seek_by_bytes= 0; - int preseek_count = 0; + double frame_rate; + double pts_time_base; + long long st_time; + struct anim_index * tc_index = 0; + AVStream * v_st; + int new_frame_index; + int old_frame_index; if (anim == 0) return (0); - ibuf = IMB_allocImBuf(anim->x, anim->y, 32, IB_rect); + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: pos=%d\n", position); - avpicture_fill((AVPicture*) anim->pFrameRGB, - (unsigned char*) ibuf->rect, - PIX_FMT_RGBA, anim->x, anim->y); - - if (position != anim->curposition + 1) { - if (position > anim->curposition + 1 - && anim->preseek - && position - (anim->curposition + 1) < anim->preseek) { - while(av_read_frame(anim->pFormatCtx, &packet)>=0) { - if (packet.stream_index == anim->videoStream) { - avcodec_decode_video2( - anim->pCodecCtx, - anim->pFrame, &frameFinished, - &packet); - - if (frameFinished) { - anim->curposition++; - } - } - av_free_packet(&packet); - if (position == anim->curposition+1) { - break; - } - } + if (tc != IMB_TC_NONE) { + tc_index = IMB_anim_open_index(anim, tc); + } + + v_st = anim->pFormatCtx->streams[anim->videoStream]; + + frame_rate = av_q2d(v_st->r_frame_rate); + + st_time = anim->pFormatCtx->start_time; + pts_time_base = av_q2d(v_st->time_base); + + if (tc_index) { + new_frame_index = IMB_indexer_get_frame_index( + tc_index, position); + old_frame_index = IMB_indexer_get_frame_index( + tc_index, anim->curposition); + pts_to_search = IMB_indexer_get_pts( + tc_index, new_frame_index); + } else { + pts_to_search = (long long) + floor(((double) position) / pts_time_base / frame_rate + 0.5); + + if (st_time != AV_NOPTS_VALUE) { + pts_to_search += st_time / pts_time_base + / AV_TIME_BASE; } } -/* disable seek_by_bytes for now, since bitrates are guessed wrong! - also: MPEG2TS-seeking was fixed in later versions of ffmpeg, so problem - is somewhat fixed by now (until we add correct timecode management code...) -*/ -#if 0 - seek_by_bytes = !!(anim->pFormatCtx->iformat->flags & AVFMT_TS_DISCONT); -#else - seek_by_bytes = FALSE; -#endif + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + "FETCH: looking for PTS=%lld " + "(pts_timebase=%g, frame_rate=%g, st_time=%lld)\n", + pts_to_search, pts_time_base, frame_rate, st_time); + + if (anim->last_frame && + anim->last_pts <= pts_to_search && anim->next_pts > pts_to_search){ + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + "FETCH: frame repeat: last: %lld next: %lld\n", + anim->last_pts, anim->next_pts); + IMB_refImBuf(anim->last_frame); + anim->curposition = position; + return anim->last_frame; + } + + IMB_freeImBuf(anim->last_frame); + + if (anim->next_pts <= pts_to_search && + anim->next_undecoded_pts > pts_to_search) { + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + "FETCH: no seek necessary: " + "next: %lld next undecoded: %lld\n", + anim->next_pts, anim->next_undecoded_pts); + + /* we are already done :) */ + + } else if (position > anim->curposition + 1 + && anim->preseek + && !tc_index + && position - (anim->curposition + 1) < anim->preseek) { + + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + "FETCH: within preseek interval (no index)\n"); + + ffmpeg_decode_video_frame_scan(anim, pts_to_search); + } else if (tc_index && + IMB_indexer_can_scan(tc_index, old_frame_index, + new_frame_index)) { + + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + "FETCH: within preseek interval " + "(index tells us)\n"); - if (position != anim->curposition + 1) { - double frame_rate = - av_q2d(anim->pFormatCtx->streams[anim->videoStream] - ->r_frame_rate); - double pts_time_base = av_q2d(anim->pFormatCtx->streams[anim->videoStream]->time_base); + ffmpeg_decode_video_frame_scan(anim, pts_to_search); + } else if (position != anim->curposition + 1) { long long pos; - long long st_time = anim->pFormatCtx->start_time; int ret; - if (seek_by_bytes) { - pos = position - anim->preseek; - if (pos < 0) { - pos = 0; - } - preseek_count = position - pos; + if (tc_index) { + unsigned long long dts; + + pos = IMB_indexer_get_seek_pos( + tc_index, new_frame_index); + dts = IMB_indexer_get_seek_pos_dts( + tc_index, new_frame_index); + + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + "TC INDEX seek pos = %lld\n", pos); + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + "TC INDEX seek dts = %lld\n", dts); - pos *= anim->pFormatCtx->bit_rate / frame_rate; - pos /= 8; + if (ffmpeg_seek_by_byte(anim->pFormatCtx)) { + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + "... using BYTE pos\n"); + + ret = av_seek_frame(anim->pFormatCtx, + -1, + pos, AVSEEK_FLAG_BYTE); + av_update_cur_dts(anim->pFormatCtx, v_st, dts); + } else { + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + "... using DTS pos\n"); + ret = av_seek_frame(anim->pFormatCtx, + anim->videoStream, + dts, AVSEEK_FLAG_BACKWARD); + } } else { pos = (long long) (position - anim->preseek) * AV_TIME_BASE / frame_rate; if (pos < 0) { pos = 0; } - + if (st_time != AV_NOPTS_VALUE) { pos += st_time; } - } - ret = av_seek_frame(anim->pFormatCtx, -1, - pos, - AVSEEK_FLAG_BACKWARD | ( - seek_by_bytes - ? AVSEEK_FLAG_ANY - | AVSEEK_FLAG_BYTE : 0)); - if (ret < 0) { - fprintf(stderr, "error while seeking: %d\n", ret); + ret = av_seek_frame(anim->pFormatCtx, -1, + pos, AVSEEK_FLAG_BACKWARD); } - pts_to_search = (long long) - (((double) position) / pts_time_base / frame_rate); - if (st_time != AV_NOPTS_VALUE) { - pts_to_search += st_time / pts_time_base/ AV_TIME_BASE; + if (ret < 0) { + av_log(anim->pFormatCtx, AV_LOG_ERROR, + "FETCH: " + "error while seeking to DTS = %lld " + "(frameno = %d, PTS = %lld): errcode = %d\n", + pos, position, pts_to_search, ret); } - pos_found = 0; avcodec_flush_buffers(anim->pCodecCtx); - } - while(av_read_frame(anim->pFormatCtx, &packet)>=0) { - if(packet.stream_index == anim->videoStream) { - avcodec_decode_video2(anim->pCodecCtx, - anim->pFrame, &frameFinished, - &packet); + anim->next_pts = -1; - if (seek_by_bytes && preseek_count > 0) { - preseek_count--; - } + if (anim->next_packet.stream_index == anim->videoStream) { + av_free_packet(&anim->next_packet); + anim->next_packet.stream_index = -1; + } - if (frameFinished && !pos_found) { - if (seek_by_bytes) { - if (!preseek_count) { - pos_found = 1; - anim->curposition = position; - } - } else { - if (packet.dts >= pts_to_search) { - pos_found = 1; - anim->curposition = position; - } - } - } + /* memset(anim->pFrame,...) ?? */ - if(frameFinished && pos_found == 1) { - ffmpeg_postprocess(anim, ibuf, &filter_y); - av_free_packet(&packet); - break; - } + if (ret >= 0) { + ffmpeg_decode_video_frame_scan(anim, pts_to_search); } - - av_free_packet(&packet); + } else if (position == 0 && anim->curposition == -1) { + /* first frame without seeking special case... */ + ffmpeg_decode_video_frame(anim); } - if (filter_y && ibuf) { - IMB_filtery(ibuf); - } + anim->last_frame = IMB_allocImBuf(anim->x, anim->y, 32, IB_rect); - ibuf->profile = IB_PROFILE_SRGB; + ffmpeg_postprocess(anim); - return(ibuf); + anim->last_pts = anim->next_pts; + + ffmpeg_decode_video_frame(anim); + + anim->curposition = position; + + IMB_refImBuf(anim->last_frame); + + return anim->last_frame; } static void free_anim_ffmpeg(struct anim * anim) { @@ -934,6 +1186,10 @@ static void free_anim_ffmpeg(struct anim * anim) { } av_free(anim->pFrameDeinterlaced); sws_freeContext(anim->img_convert_ctx); + IMB_freeImBuf(anim->last_frame); + if (anim->next_packet.stream_index != -1) { + av_free_packet(&anim->next_packet); + } } anim->duration = 0; } @@ -1063,16 +1319,19 @@ struct ImBuf * IMB_anim_previewframe(struct anim * anim) { struct ImBuf * ibuf = NULL; int position = 0; - ibuf = IMB_anim_absolute(anim, 0); + ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE); if (ibuf) { IMB_freeImBuf(ibuf); position = anim->duration / 2; - ibuf = IMB_anim_absolute(anim, position); + ibuf = IMB_anim_absolute(anim, position, IMB_TC_NONE, + IMB_PROXY_NONE); } return ibuf; } -struct ImBuf * IMB_anim_absolute(struct anim * anim, int position) { +struct ImBuf * IMB_anim_absolute(struct anim * anim, int position, + IMB_Timecode_Type tc, + IMB_Proxy_Size preview_size) { struct ImBuf * ibuf = NULL; char head[256], tail[256]; unsigned short digits; @@ -1095,6 +1354,18 @@ struct ImBuf * IMB_anim_absolute(struct anim * anim, int position) { if (position < 0) return(NULL); if (position >= anim->duration) return(NULL); + if (preview_size != IMB_PROXY_NONE) { + struct anim * proxy = IMB_anim_open_proxy(anim, preview_size); + + if (proxy) { + position = IMB_anim_index_get_frame_index( + anim, tc, position); + return IMB_anim_absolute( + proxy, position, + IMB_TC_NONE, IMB_PROXY_NONE); + } + } + switch(anim->curtype) { case ANIM_SEQUENCE: pic = an_stringdec(anim->first, head, tail, &digits); @@ -1127,7 +1398,7 @@ struct ImBuf * IMB_anim_absolute(struct anim * anim, int position) { #endif #ifdef WITH_FFMPEG case ANIM_FFMPEG: - ibuf = ffmpeg_fetchibuf(anim, position); + ibuf = ffmpeg_fetchibuf(anim, position, tc); if (ibuf) anim->curposition = position; filter_y = 0; /* done internally */ @@ -1151,8 +1422,29 @@ struct ImBuf * IMB_anim_absolute(struct anim * anim, int position) { /***/ -int IMB_anim_get_duration(struct anim *anim) { - return anim->duration; +int IMB_anim_get_duration(struct anim *anim, IMB_Timecode_Type tc) { + struct anim_index * idx; + if (tc == IMB_TC_NONE) { + return anim->duration; + } + + idx = IMB_anim_open_index(anim, tc); + if (!idx) { + return anim->duration; + } + + return IMB_indexer_get_duration(idx); +} + +int IMB_anim_get_fps(struct anim * anim, + short * frs_sec, float * frs_sec_base) +{ + if (anim->frs_sec) { + *frs_sec = anim->frs_sec; + *frs_sec_base = anim->frs_sec_base; + return TRUE; + } + return FALSE; } void IMB_anim_set_preseek(struct anim * anim, int preseek) diff --git a/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp b/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp index 971658ff482..44e029bd7ce 100644 --- a/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp +++ b/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp @@ -1426,12 +1426,12 @@ void DirectDrawSurface::printInfo() const if (header.flags & DDSD_LINEARSIZE) printf("\tDDSD_LINEARSIZE\n"); if (header.flags & DDSD_MIPMAPCOUNT) printf("\tDDSD_MIPMAPCOUNT\n"); - printf("Height: %d\n", header.height); - printf("Width: %d\n", header.width); - printf("Depth: %d\n", header.depth); - if (header.flags & DDSD_PITCH) printf("Pitch: %d\n", header.pitch); - else if (header.flags & DDSD_LINEARSIZE) printf("Linear size: %d\n", header.pitch); - printf("Mipmap count: %d\n", header.mipmapcount); + printf("Height: %u\n", header.height); + printf("Width: %u\n", header.width); + printf("Depth: %u\n", header.depth); + if (header.flags & DDSD_PITCH) printf("Pitch: %u\n", header.pitch); + else if (header.flags & DDSD_LINEARSIZE) printf("Linear size: %u\n", header.pitch); + printf("Mipmap count: %u\n", header.mipmapcount); printf("Pixel Format:\n"); printf("\tFlags: 0x%.8X\n", header.pf.flags); @@ -1468,7 +1468,7 @@ void DirectDrawSurface::printInfo() const } else { - printf("\tBit count: %d\n", header.pf.bitcount); + printf("\tBit count: %u\n", header.pf.bitcount); } printf("\tRed mask: 0x%.8X\n", header.pf.rmask); @@ -1522,7 +1522,7 @@ void DirectDrawSurface::printInfo() const if (header.reserved[7] == FOURCC_UVER) { - printf("User Version: %d\n", header.reserved[8]); + printf("User Version: %u\n", header.reserved[8]); } } diff --git a/source/blender/imbuf/intern/filter.c b/source/blender/imbuf/intern/filter.c index 7f1eef80318..2677913caed 100644 --- a/source/blender/imbuf/intern/filter.c +++ b/source/blender/imbuf/intern/filter.c @@ -1,5 +1,7 @@ /* * + * $Id$ + * * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c new file mode 100644 index 00000000000..3528318ba81 --- /dev/null +++ b/source/blender/imbuf/intern/indexer.c @@ -0,0 +1,1142 @@ +/* + * $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 }; + +#ifdef WITH_FFMPEG +static int tc_types[] = { IMB_TC_RECORD_RUN, IMB_TC_FREE_RUN, + IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN }; +#endif + +#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); + const 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); + } + + frame = ctx->sws_ctx ? (frame ? ctx->frame : 0) : frame; + + if (frame) { + frame->pts = ctx->cfra++; + } + + outsize = avcodec_encode_video( + ctx->c, ctx->video_buffer, ctx->video_buffersize, + 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 UNUSED(tcs_in_use), + IMB_Proxy_Size proxy_sizes_in_use, + int quality, + short *stop, short *do_update, + float *progress) +{ + int cnt = IMB_anim_get_duration(anim, IMB_TC_NONE); + int i, pos; + AviMovie * proxy_ctx[IMB_PROXY_MAX_SLOT]; + char fname[FILE_MAXDIR+FILE_MAXFILE]; + char fname_tmp[FILE_MAXDIR+FILE_MAXFILE]; + + memset(proxy_ctx, 0, sizeof(proxy_ctx)); + + /* since timecode indices only work with ffmpeg right now, + don't know a sensible fallback here... + + so no proxies, no game to play... + */ + if (proxy_sizes_in_use == IMB_PROXY_NONE) { + return; + } + + for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) { + if (proxy_sizes_in_use & proxy_sizes[i]) { + char fname[FILE_MAXDIR+FILE_MAXFILE]; + + get_proxy_filename(anim, proxy_sizes[i], fname, TRUE); + BLI_make_existing_file(fname); + + proxy_ctx[i] = alloc_proxy_output_avi( + anim, fname, + anim->x * proxy_fac[i], + anim->y * proxy_fac[i], + quality); + } + } + + for (pos = 0; pos < cnt; pos++) { + struct ImBuf * ibuf = IMB_anim_absolute( + anim, pos, IMB_TC_NONE, IMB_PROXY_NONE); + int next_progress = (int) ((double) pos / (double) cnt); + + if (*progress != next_progress) { + *progress = next_progress; + *do_update = 1; + } + + if (*stop) { + break; + } + + IMB_flipy(ibuf); + + for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) { + if (proxy_sizes_in_use & proxy_sizes[i]) { + int x = anim->x * proxy_fac[i]; + int y = anim->y * proxy_fac[i]; + + struct ImBuf * s_ibuf = IMB_scalefastImBuf( + ibuf, x, y); + + IMB_convert_rgba_to_abgr(s_ibuf); + + AVI_write_frame (proxy_ctx[i], pos, + AVI_FORMAT_RGB32, + s_ibuf->rect, x * y * 4); + + /* note that libavi free's the buffer... */ + s_ibuf->rect = 0; + + IMB_freeImBuf(s_ibuf); + } + } + } + + for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) { + if (proxy_sizes_in_use & proxy_sizes[i]) { + AVI_close_compress (proxy_ctx[i]); + MEM_freeN (proxy_ctx[i]); + + get_proxy_filename(anim, proxy_sizes[i], + fname_tmp, TRUE); + get_proxy_filename(anim, proxy_sizes[i], + fname, FALSE); + + if (*stop) { + unlink(fname_tmp); + } else { + rename(fname_tmp, fname); + } + } + } +} + +/* ---------------------------------------------------------------------- + - public API + ---------------------------------------------------------------------- */ + +void IMB_anim_index_rebuild(struct anim * anim, IMB_Timecode_Type tcs_in_use, + IMB_Proxy_Size proxy_sizes_in_use, + int quality, + short *stop, short *do_update, float *progress) +{ + switch (anim->curtype) { +#ifdef WITH_FFMPEG + case ANIM_FFMPEG: + index_rebuild_ffmpeg(anim, tcs_in_use, proxy_sizes_in_use, + quality, stop, do_update, progress); + break; +#endif + default: + index_rebuild_fallback(anim, tcs_in_use, proxy_sizes_in_use, + quality, stop, do_update, progress); + break; + } +} + +void IMB_free_indices(struct anim * anim) +{ + int i; + + for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) { + if (anim->proxy_anim[i]) { + IMB_close_anim(anim->proxy_anim[i]); + anim->proxy_anim[i] = 0; + } + } + + for (i = 0; i < IMB_TC_MAX_SLOT; i++) { + if (anim->curr_idx[i]) { + IMB_indexer_close(anim->curr_idx[i]); + anim->curr_idx[i] = 0; + } + } + + + anim->proxies_tried = 0; + anim->indices_tried = 0; +} + +void IMB_anim_set_index_dir(struct anim * anim, const char * dir) +{ + if (strcmp(anim->index_dir, dir) == 0) { + return; + } + BLI_strncpy(anim->index_dir, dir, sizeof(anim->index_dir)); + + IMB_free_indices(anim); +} + +struct anim * IMB_anim_open_proxy( + struct anim * anim, IMB_Proxy_Size preview_size) +{ + char fname[FILE_MAXDIR+FILE_MAXFILE]; + int i = IMB_proxy_size_to_array_index(preview_size); + + if (anim->proxy_anim[i]) { + return anim->proxy_anim[i]; + } + + if (anim->proxies_tried & preview_size) { + return NULL; + } + + get_proxy_filename(anim, preview_size, fname, FALSE); + + anim->proxy_anim[i] = IMB_open_anim(fname, 0, 0); + + anim->proxies_tried |= preview_size; + + return anim->proxy_anim[i]; +} + +struct anim_index * IMB_anim_open_index( + struct anim * anim, IMB_Timecode_Type tc) +{ + char fname[FILE_MAXDIR+FILE_MAXFILE]; + int i = IMB_timecode_to_array_index(tc); + + if (anim->curr_idx[i]) { + return anim->curr_idx[i]; + } + + if (anim->indices_tried & tc) { + return 0; + } + + get_tc_filename(anim, tc, fname); + + anim->curr_idx[i] = IMB_indexer_open(fname); + + anim->indices_tried |= tc; + + return anim->curr_idx[i]; +} + +int IMB_anim_index_get_frame_index(struct anim * anim, IMB_Timecode_Type tc, + int position) +{ + struct anim_index * idx = IMB_anim_open_index(anim, tc); + + if (!idx) { + return position; + } + + return IMB_indexer_get_frame_index(idx, position); +} + diff --git a/source/blender/imbuf/intern/indexer_dv.c b/source/blender/imbuf/intern/indexer_dv.c new file mode 100644 index 00000000000..0961af10f69 --- /dev/null +++ b/source/blender/imbuf/intern/indexer_dv.c @@ -0,0 +1,391 @@ +/* + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Peter Schlaile <peter [at] schlaile [dot] de> 2011 + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** +*/ + +#include "IMB_indexer.h" +#include "MEM_guardedalloc.h" +#include <time.h> + +typedef struct indexer_dv_bitstream { + unsigned char* buffer; + int bit_pos; +} indexer_dv_bitstream; + +static indexer_dv_bitstream bitstream_new(unsigned char* buffer_) +{ + indexer_dv_bitstream rv; + + rv.buffer = buffer_; + rv.bit_pos = 0; + + return rv; +} + +static unsigned long bitstream_get_bits(indexer_dv_bitstream * This, int num) +{ + int byte_pos = This->bit_pos >> 3; + unsigned long i = + This->buffer[byte_pos] | (This->buffer[byte_pos + 1] << 8) | + (This->buffer[byte_pos + 2] << 16) | + (This->buffer[byte_pos + 3] << 24); + int rval = (i >> (This->bit_pos & 0x7)) & ((1 << num) - 1); + This->bit_pos += num; + return rval; +} + +static int parse_num(indexer_dv_bitstream * b, int numbits) { + return bitstream_get_bits(b, numbits); +} + +static int parse_bcd(indexer_dv_bitstream * b, int n) +{ + char s[256]; + char * p = s + (n+3)/4; + + *p-- = 0; + + while (n > 4) { + char a; + int v = bitstream_get_bits(b, 4); + + n -= 4; + a = '0' + v; + + if (a > '9') { + bitstream_get_bits(b, n); + return -1; + } + + *p-- = a; + } + if (n) { + char a; + int v = bitstream_get_bits(b, n); + a = '0' + v; + if (a > '9') { + return -1; + } + *p-- = a; + } + + return atol(s); +} + +typedef struct indexer_dv_context +{ + int rec_curr_frame; + int rec_curr_second; + int rec_curr_minute; + int rec_curr_hour; + + int rec_curr_day; + int rec_curr_month; + int rec_curr_year; + + char got_record_date; + char got_record_time; + + time_t ref_time_read; + time_t ref_time_read_new; + int curr_frame; + + time_t gap_start; + int gap_frame; + + int frameno_offset; + + anim_index_entry backbuffer[31]; + int fsize; + + anim_index_builder * idx; +} indexer_dv_context; + +static void parse_packet(indexer_dv_context * This, unsigned char * p) +{ + indexer_dv_bitstream b; + int type = p[0]; + + b = bitstream_new(p + 1); + + switch (type) { + case 0x62: // Record date + parse_num(&b, 8); + This->rec_curr_day = parse_bcd(&b, 6); + parse_num(&b, 2); + This->rec_curr_month = parse_bcd(&b, 5); + parse_num(&b, 3); + This->rec_curr_year = parse_bcd(&b, 8); + if (This->rec_curr_year < 25) { + This->rec_curr_year += 2000; + } else { + This->rec_curr_year += 1900; + } + This->got_record_date = 1; + break; + case 0x63: // Record time + This->rec_curr_frame = parse_bcd(&b, 6); + parse_num(&b, 2); + This->rec_curr_second = parse_bcd(&b, 7); + parse_num(&b, 1); + This->rec_curr_minute = parse_bcd(&b, 7); + parse_num(&b, 1); + This->rec_curr_hour = parse_bcd(&b, 6); + This->got_record_time = 1; + break; + } +} + +static void parse_header_block(indexer_dv_context * This, unsigned char* target) +{ + int i; + for (i = 3; i < 80; i += 5) { + if (target[i] != 0xff) { + parse_packet(This, target + i); + } + } +} + +static void parse_subcode_blocks( + indexer_dv_context * This, unsigned char* target) +{ + int i,j; + + for (j = 0; j < 2; j++) { + for (i = 3; i < 80; i += 5) { + if (target[i] != 0xff) { + parse_packet(This, target + i); + } + } + } +} + +static void parse_vaux_blocks( + indexer_dv_context * This, unsigned char* target) +{ + int i,j; + + for (j = 0; j < 3; j++) { + for (i = 3; i < 80; i += 5) { + if (target[i] != 0xff) { + parse_packet(This, target + i); + } + } + target += 80; + } +} + +static void parse_audio_headers( + indexer_dv_context * This, unsigned char* target) +{ + int i; + + for(i = 0; i < 9; i++) { + if (target[3] != 0xff) { + parse_packet(This, target + 3); + } + target += 16 * 80; + } +} + +static void parse_frame(indexer_dv_context * This, + unsigned char * framebuffer, int isPAL) +{ + int numDIFseq = isPAL ? 12 : 10; + unsigned char* target = framebuffer; + int ds; + + for (ds = 0; ds < numDIFseq; ds++) { + parse_header_block(This, target); + target += 1 * 80; + parse_subcode_blocks(This, target); + target += 2 * 80; + parse_vaux_blocks(This, target); + target += 3 * 80; + parse_audio_headers(This, target); + target += 144 * 80; + } +} + +static void inc_frame(int * frame, time_t * t, int isPAL) +{ + if ((isPAL && *frame >= 25) || (!isPAL && *frame >= 30)) { + fprintf(stderr, "Ouchie: inc_frame: invalid_frameno: %d\n", + *frame); + } + (*frame)++; + if (isPAL && *frame >= 25) { + (*t)++; + *frame = 0; + } else if (!isPAL && *frame >= 30) { + (*t)++; + *frame = 0; + } +} + +static void write_index(indexer_dv_context * This, anim_index_entry * entry) +{ + IMB_index_builder_add_entry( + This->idx, entry->frameno + This->frameno_offset, + entry->seek_pos, entry->seek_pos_dts, entry->pts); +} + +static void fill_gap(indexer_dv_context * This, int isPAL) +{ + int i; + + for (i = 0; i < This->fsize; i++) { + if (This->gap_start == This->ref_time_read && + This->gap_frame == This->curr_frame) { + fprintf(stderr, + "indexer_dv::fill_gap: " + "can't seek backwards !\n"); + break; + } + inc_frame(&This->gap_frame, &This->gap_start, isPAL); + } + + while (This->gap_start != This->ref_time_read || + This->gap_frame != This->curr_frame) { + inc_frame(&This->gap_frame, &This->gap_start, isPAL); + This->frameno_offset++; + } + + for (i = 0; i < This->fsize; i++) { + write_index(This, This->backbuffer + i); + } + This->fsize = 0; +} + +static void proc_frame(indexer_dv_context * This, + unsigned char* framebuffer, int isPAL) +{ + struct tm recDate; + time_t t; + + if (!This->got_record_date || !This->got_record_time) { + return; + } + + recDate.tm_sec = This->rec_curr_second; + recDate.tm_min = This->rec_curr_minute; + recDate.tm_hour = This->rec_curr_hour; + recDate.tm_mday = This->rec_curr_day; + recDate.tm_mon = This->rec_curr_month - 1; + recDate.tm_year = This->rec_curr_year - 1900; + recDate.tm_wday = -1; + recDate.tm_yday = -1; + recDate.tm_isdst = -1; + + t = mktime(&recDate); + if (t == -1) { + return; + } + + This->ref_time_read_new = t; + + if (This->ref_time_read < 0) { + This->ref_time_read = This->ref_time_read_new; + This->curr_frame = 0; + } else { + if (This->ref_time_read_new - This->ref_time_read == 1) { + This->curr_frame = 0; + This->ref_time_read = This->ref_time_read_new; + if (This->gap_frame >= 0) { + fill_gap(This, isPAL); + This->gap_frame = -1; + } + } else if (This->ref_time_read_new == This->ref_time_read) { + // do nothing + } else { + This->gap_start = This->ref_time_read; + This->gap_frame = This->curr_frame; + This->ref_time_read = This->ref_time_read_new; + This->curr_frame = -1; + } + } +} + +static void indexer_dv_proc_frame(anim_index_builder * idx, + unsigned char * buffer, + int data_size, + struct anim_index_entry * entry) +{ + int isPAL; + + indexer_dv_context * This = (indexer_dv_context *) idx->private_data; + + isPAL = (buffer[3] & 0x80); + + This->got_record_date = FALSE; + This->got_record_time = FALSE; + + parse_frame(This, buffer, isPAL); + proc_frame(This, buffer, isPAL); + + if (This->curr_frame >= 0) { + write_index(This, entry); + inc_frame(&This->curr_frame, &This->ref_time_read, isPAL); + } else { + This->backbuffer[This->fsize++] = *entry; + if (This->fsize >= 31) { + int i; + + fprintf(stderr, "indexer_dv::indexer_dv_proc_frame: " + "backbuffer overrun, emergency flush"); + + for (i = 0; i < This->fsize; i++) { + write_index(This, This->backbuffer+i); + } + This->fsize = 0; + } + } +} + +static void indexer_dv_delete(anim_index_builder * idx) +{ + int i = 0; + indexer_dv_context * This = (indexer_dv_context *) idx->private_data; + + for (i = 0; i < This->fsize; i++) { + write_index(This, This->backbuffer+i); + } + + MEM_freeN(This); +} + +void IMB_indexer_dv_new(anim_index_builder * idx) +{ + indexer_dv_context * rv = MEM_callocN( + sizeof(indexer_dv_context), "index_dv builder context"); + + rv->ref_time_read = -1; + rv->curr_frame = -1; + rv->gap_frame = -1; + rv->idx = idx; + + idx->private_data = rv; + idx->proc_frame = indexer_dv_proc_frame; + idx->delete_priv_data = indexer_dv_delete; +} diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c index 1d91f34f4fa..2ab7e55d1f8 100644 --- a/source/blender/imbuf/intern/thumbs.c +++ b/source/blender/imbuf/intern/thumbs.c @@ -317,9 +317,9 @@ ImBuf* IMB_thumb_create(const char* path, ThumbSize size, ThumbSource source, Im } } else if (THB_SOURCE_MOVIE == source) { struct anim * anim = NULL; - anim = IMB_open_anim(path, IB_rect | IB_metadata); + anim = IMB_open_anim(path, IB_rect | IB_metadata, 0); if (anim != NULL) { - img = IMB_anim_absolute(anim, 0); + img = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE); if (img == NULL) { printf("not an anim; %s\n", path); } else { diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c index 36130aa0dbf..7beb853fe62 100644 --- a/source/blender/imbuf/intern/tiff.c +++ b/source/blender/imbuf/intern/tiff.c @@ -646,7 +646,7 @@ void imb_loadtiletiff(ImBuf *ibuf, unsigned char *mem, size_t size, int tx, int } } else - printf("imb_loadtiff: mipmap level %d has unexpected size %dx%d instead of %dx%d\n", ibuf->miplevel, width, height, ibuf->x, ibuf->y); + printf("imb_loadtiff: mipmap level %d has unexpected size %ux%u instead of %dx%d\n", ibuf->miplevel, width, height, ibuf->x, ibuf->y); } else printf("imb_loadtiff: could not find mipmap level %d\n", ibuf->miplevel); diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c index 6db8dcc06cf..85d31f18a03 100644 --- a/source/blender/imbuf/intern/util.c +++ b/source/blender/imbuf/intern/util.c @@ -221,7 +221,7 @@ void silence_log_ffmpeg(int quiet) } else { - av_log_set_level(AV_LOG_INFO); + av_log_set_level(AV_LOG_DEBUG); } } @@ -234,9 +234,10 @@ void do_init_ffmpeg(void) av_register_all(); avdevice_register_all(); - if ((G.f & G_DEBUG) == 0) - { + if ((G.f & G_DEBUG) == 0) { silence_log_ffmpeg(1); + } else { + silence_log_ffmpeg(0); } } } |