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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Schlaile <peter@schlaile.de>2011-08-28 18:46:03 +0400
committerPeter Schlaile <peter@schlaile.de>2011-08-28 18:46:03 +0400
commitc07bd1439a3f026b8603c52662c3e7ccc364321a (patch)
treef6ce8cd3e01b0701c9b3d0cd3e17987be33803b8 /source/blender/imbuf/intern/indexer.c
parent852a03a6af6d67da58154b848b45a118eb38cdc0 (diff)
== Sequencer ==
This patch adds: * support for proxy building again (missing feature from Blender 2.49) additionally to the way, Blender 2.49 worked, you can select several strips at once and make Blender build proxies in the background (using the job system) Also a new thing: movie proxies are now build into AVI files, and the proxy system is moved into ImBuf-library, so that other parts of blender can also benefit from it. * Timecode support: to fix seeking issues with files, that have a) varying frame rates b) very large GOP lengths c) are broken inbetween d) use different time code tracks the proxy builder can now also build timecode indices, which are used (optionally) for seeking. For the first time, it is possible, to do frame exact seeking on all file types. * Support for different video-streams in one video file (can be selected in sequencer, other parts of blender can also use it, but UI has to be added accordingly) * IMPORTANT: this patch *requires* ffmpeg 0.7 or newer, since older versions don't support the pkt_pts field, that is essential for building timecode indices. Windows and Mac libs are already updated, Linux-users have to build their own ffmpeg verions until distros keep up.
Diffstat (limited to 'source/blender/imbuf/intern/indexer.c')
-rw-r--r--source/blender/imbuf/intern/indexer.c1135
1 files changed, 1135 insertions, 0 deletions
diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c
new file mode 100644
index 00000000000..f624694b324
--- /dev/null
+++ b/source/blender/imbuf/intern/indexer.c
@@ -0,0 +1,1135 @@
+/*
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Peter Schlaile <peter [at] schlaile [dot] de> 2011
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+*/
+
+#include "IMB_indexer.h"
+#include "IMB_anim.h"
+#include "AVI_avi.h"
+#include "imbuf.h"
+#include "MEM_guardedalloc.h"
+#include "BLI_utildefines.h"
+#include "BLI_blenlib.h"
+#include "BLI_math_base.h"
+#include "BLI_string.h"
+#include "MEM_guardedalloc.h"
+#include "DNA_userdef_types.h"
+#include "BKE_global.h"
+#include <stdlib.h>
+
+#ifdef WITH_FFMPEG
+
+#include "ffmpeg_compat.h"
+
+#endif //WITH_FFMPEG
+
+
+static char magic[] = "BlenMIdx";
+static char temp_ext [] = "_part";
+
+static int proxy_sizes[] = { IMB_PROXY_25, IMB_PROXY_50, IMB_PROXY_75,
+ IMB_PROXY_100 };
+static float proxy_fac[] = { 0.25, 0.50, 0.75, 1.00 };
+static int tc_types[] = { IMB_TC_RECORD_RUN, IMB_TC_FREE_RUN,
+ IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN };
+
+#define INDEX_FILE_VERSION 1
+
+/* ----------------------------------------------------------------------
+ - special indexers
+ ----------------------------------------------------------------------
+ */
+
+extern void IMB_indexer_dv_new(anim_index_builder * idx);
+
+
+/* ----------------------------------------------------------------------
+ - time code index functions
+ ---------------------------------------------------------------------- */
+
+anim_index_builder * IMB_index_builder_create(const char * name)
+{
+
+ anim_index_builder * rv
+ = MEM_callocN( sizeof(struct anim_index_builder),
+ "index builder");
+
+ fprintf(stderr, "Starting work on index: %s\n", name);
+
+ BLI_strncpy(rv->name, name, sizeof(rv->name));
+ BLI_strncpy(rv->temp_name, name, sizeof(rv->temp_name));
+
+ strcat(rv->temp_name, temp_ext);
+
+ BLI_make_existing_file(rv->temp_name);
+
+ rv->fp = fopen(rv->temp_name, "w");
+
+ if (!rv->fp) {
+ fprintf(stderr, "Couldn't open index target: %s! "
+ "Index build broken!\n", rv->temp_name);
+ MEM_freeN(rv);
+ return NULL;
+ }
+
+ fprintf(rv->fp, "%s%c%.3d", magic, (ENDIAN_ORDER==B_ENDIAN)?'V':'v',
+ INDEX_FILE_VERSION);
+
+ return rv;
+}
+
+void IMB_index_builder_add_entry(anim_index_builder * fp,
+ int frameno,unsigned long long seek_pos,
+ unsigned long long seek_pos_dts,
+ unsigned long long pts)
+{
+ fwrite(&frameno, sizeof(int), 1, fp->fp);
+ fwrite(&seek_pos, sizeof(unsigned long long), 1, fp->fp);
+ fwrite(&seek_pos_dts, sizeof(unsigned long long), 1, fp->fp);
+ fwrite(&pts, sizeof(unsigned long long), 1, fp->fp);
+}
+
+void IMB_index_builder_proc_frame(anim_index_builder * fp,
+ unsigned char * buffer,
+ int data_size,
+ int frameno, unsigned long long seek_pos,
+ unsigned long long seek_pos_dts,
+ unsigned long long pts)
+{
+ if (fp->proc_frame) {
+ anim_index_entry e;
+ e.frameno = frameno;
+ e.seek_pos = seek_pos;
+ e.seek_pos_dts = seek_pos_dts;
+ e.pts = pts;
+
+ fp->proc_frame(fp, buffer, data_size, &e);
+ } else {
+ IMB_index_builder_add_entry(fp, frameno, seek_pos,
+ seek_pos_dts, pts);
+ }
+}
+
+void IMB_index_builder_finish(anim_index_builder * fp, int rollback)
+{
+ if (fp->delete_priv_data) {
+ fp->delete_priv_data(fp);
+ }
+
+ fclose(fp->fp);
+
+ if (rollback) {
+ unlink(fp->temp_name);
+ } else {
+ rename(fp->temp_name, fp->name);
+ }
+
+ MEM_freeN(fp);
+}
+
+struct anim_index * IMB_indexer_open(const char * name)
+{
+ char header[13];
+ struct anim_index * idx;
+ FILE * fp = fopen(name, "rb");
+ int i;
+
+ if (!fp) {
+ return 0;
+ }
+
+ if (fread(header, 12, 1, fp) != 1) {
+ fclose(fp);
+ return 0;
+ }
+
+ header[12] = 0;
+
+ if (memcmp(header, magic, 8) != 0) {
+ fclose(fp);
+ return 0;
+ }
+
+ if (atoi(header+9) != INDEX_FILE_VERSION) {
+ fclose(fp);
+ return 0;
+ }
+
+ idx = MEM_callocN( sizeof(struct anim_index), "anim_index");
+
+ BLI_strncpy(idx->name, name, sizeof(idx->name));
+
+ fseek(fp, 0, SEEK_END);
+
+ idx->num_entries = (ftell(fp) - 12)
+ / (sizeof(int) // framepos
+ + sizeof(unsigned long long) // seek_pos
+ + sizeof(unsigned long long) // seek_pos_dts
+ + sizeof(unsigned long long) // pts
+ );
+
+ fseek(fp, 12, SEEK_SET);
+
+ idx->entries = MEM_callocN( sizeof(struct anim_index_entry)
+ * idx->num_entries, "anim_index_entries");
+
+ for (i = 0; i < idx->num_entries; i++) {
+ fread(&idx->entries[i].frameno,
+ sizeof(int), 1, fp);
+ fread(&idx->entries[i].seek_pos,
+ sizeof(unsigned long long), 1, fp);
+ fread(&idx->entries[i].seek_pos_dts,
+ sizeof(unsigned long long), 1, fp);
+ fread(&idx->entries[i].pts,
+ sizeof(unsigned long long), 1, fp);
+ }
+
+ if (((ENDIAN_ORDER == B_ENDIAN) != (header[8] == 'V'))) {
+ for (i = 0; i < idx->num_entries; i++) {
+ SWITCH_INT(idx->entries[i].frameno);
+ SWITCH_INT64(idx->entries[i].seek_pos);
+ SWITCH_INT64(idx->entries[i].seek_pos_dts);
+ SWITCH_INT64(idx->entries[i].pts);
+ }
+ }
+
+ fclose(fp);
+
+ return idx;
+}
+
+unsigned long long IMB_indexer_get_seek_pos(
+ struct anim_index * idx, int frame_index)
+{
+ if (frame_index < 0) {
+ frame_index = 0;
+ }
+ if (frame_index >= idx->num_entries) {
+ frame_index = idx->num_entries - 1;
+ }
+ return idx->entries[frame_index].seek_pos;
+}
+
+unsigned long long IMB_indexer_get_seek_pos_dts(
+ struct anim_index * idx, int frame_index)
+{
+ if (frame_index < 0) {
+ frame_index = 0;
+ }
+ if (frame_index >= idx->num_entries) {
+ frame_index = idx->num_entries - 1;
+ }
+ return idx->entries[frame_index].seek_pos_dts;
+}
+
+int IMB_indexer_get_frame_index(struct anim_index * idx, int frameno)
+{
+ int len = idx->num_entries;
+ int half;
+ int middle;
+ int first = 0;
+
+ /* bsearch (lower bound) the right index */
+
+ while (len > 0) {
+ half = len >> 1;
+ middle = first;
+
+ middle += half;
+
+ if (idx->entries[middle].frameno < frameno) {
+ first = middle;
+ ++first;
+ len = len - half - 1;
+ } else {
+ len = half;
+ }
+ }
+
+ if (first == idx->num_entries) {
+ return idx->num_entries - 1;
+ } else {
+ return first;
+ }
+}
+
+unsigned long long IMB_indexer_get_pts(struct anim_index * idx,
+ int frame_index)
+{
+ if (frame_index < 0) {
+ frame_index = 0;
+ }
+ if (frame_index >= idx->num_entries) {
+ frame_index = idx->num_entries - 1;
+ }
+ return idx->entries[frame_index].pts;
+}
+
+int IMB_indexer_get_duration(struct anim_index * idx)
+{
+ if (idx->num_entries == 0) {
+ return 0;
+ }
+ return idx->entries[idx->num_entries-1].frameno + 1;
+}
+
+int IMB_indexer_can_scan(struct anim_index * idx,
+ int old_frame_index, int new_frame_index)
+{
+ /* makes only sense, if it is the same I-Frame and we are not
+ trying to run backwards in time... */
+ return (IMB_indexer_get_seek_pos(idx, old_frame_index)
+ == IMB_indexer_get_seek_pos(idx, new_frame_index) &&
+ old_frame_index < new_frame_index);
+}
+
+void IMB_indexer_close(struct anim_index * idx)
+{
+ MEM_freeN(idx->entries);
+ MEM_freeN(idx);
+}
+
+int IMB_proxy_size_to_array_index(IMB_Proxy_Size pr_size)
+{
+ switch (pr_size) {
+ case IMB_PROXY_NONE: /* if we got here, something is broken anyways,
+ so sane defaults... */
+ return 0;
+ case IMB_PROXY_25:
+ return 0;
+ case IMB_PROXY_50:
+ return 1;
+ case IMB_PROXY_75:
+ return 2;
+ case IMB_PROXY_100:
+ return 3;
+ default:
+ return 0;
+ };
+ return 0;
+}
+
+int IMB_timecode_to_array_index(IMB_Timecode_Type tc)
+{
+ switch (tc) {
+ case IMB_TC_NONE: /* if we got here, something is broken anyways,
+ so sane defaults... */
+ return 0;
+ case IMB_TC_RECORD_RUN:
+ return 0;
+ case IMB_TC_FREE_RUN:
+ return 1;
+ case IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN:
+ return 2;
+ default:
+ return 0;
+ };
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------
+ - rebuild helper functions
+ ---------------------------------------------------------------------- */
+
+static void get_index_dir(struct anim * anim, char * index_dir)
+{
+ if (!anim->index_dir[0]) {
+ char fname[FILE_MAXFILE];
+ BLI_strncpy(index_dir, anim->name, FILE_MAXDIR);
+ BLI_splitdirstring(index_dir, fname);
+ BLI_join_dirfile(index_dir, FILE_MAXDIR, index_dir, "BL_proxy");
+ BLI_join_dirfile(index_dir, FILE_MAXDIR, index_dir, fname);
+ } else {
+ BLI_strncpy(index_dir, anim->index_dir, FILE_MAXDIR);
+ }
+}
+
+static void get_proxy_filename(struct anim * anim, IMB_Proxy_Size preview_size,
+ char * fname, int temp)
+{
+ char index_dir[FILE_MAXDIR];
+ int i = IMB_proxy_size_to_array_index(preview_size);
+
+ char proxy_name[256];
+ char proxy_temp_name[256];
+ char stream_suffix[20];
+
+ stream_suffix[0] = 0;
+
+ if (anim->streamindex > 0) {
+ BLI_snprintf(stream_suffix, 20, "_st%d", anim->streamindex);
+ }
+
+ BLI_snprintf(proxy_name, 256, "proxy_%d%s.avi",
+ (int) (proxy_fac[i] * 100), stream_suffix);
+ BLI_snprintf(proxy_temp_name, 256, "proxy_%d%s_part.avi",
+ (int) (proxy_fac[i] * 100), stream_suffix);
+
+ get_index_dir(anim, index_dir);
+
+ BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR, index_dir,
+ temp ? proxy_temp_name : proxy_name);
+}
+
+static void get_tc_filename(struct anim * anim, IMB_Timecode_Type tc,
+ char * fname)
+{
+ char index_dir[FILE_MAXDIR];
+ int i = IMB_timecode_to_array_index(tc);
+ char * index_names[] = {
+ "record_run%s.blen_tc", "free_run%s.blen_tc",
+ "interp_free_run%s.blen_tc" };
+
+ char stream_suffix[20];
+ char index_name[256];
+
+ stream_suffix[0] = 0;
+
+ if (anim->streamindex > 0) {
+ BLI_snprintf(stream_suffix, 20, "_st%d", anim->streamindex);
+ }
+
+ BLI_snprintf(index_name, 256, index_names[i], stream_suffix);
+
+ get_index_dir(anim, index_dir);
+
+ BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR,
+ index_dir, index_name);
+}
+
+/* ----------------------------------------------------------------------
+ - ffmpeg rebuilder
+ ---------------------------------------------------------------------- */
+
+#ifdef WITH_FFMPEG
+
+struct proxy_output_ctx {
+ AVFormatContext* of;
+ AVStream* st;
+ AVCodecContext* c;
+ AVCodec* codec;
+ struct SwsContext * sws_ctx;
+ AVFrame* frame;
+ uint8_t* video_buffer;
+ int video_buffersize;
+ int cfra;
+ int proxy_size;
+ int orig_height;
+ struct anim * anim;
+};
+
+// work around stupid swscaler 16 bytes alignment bug...
+
+static int round_up(int x, int mod)
+{
+ return x + ((mod - (x % mod)) % mod);
+}
+
+static struct proxy_output_ctx * alloc_proxy_output_ffmpeg(
+ struct anim * anim,
+ AVStream * st, int proxy_size, int width, int height,
+ int quality)
+{
+ struct proxy_output_ctx * rv = MEM_callocN(
+ sizeof(struct proxy_output_ctx), "alloc_proxy_output");
+
+ char fname[FILE_MAXDIR+FILE_MAXFILE];
+
+ // JPEG requires this
+ width = round_up(width, 8);
+ height = round_up(height, 8);
+
+ rv->proxy_size = proxy_size;
+ rv->anim = anim;
+
+ get_proxy_filename(rv->anim, rv->proxy_size, fname, TRUE);
+ BLI_make_existing_file(fname);
+
+ rv->of = avformat_alloc_context();
+ rv->of->oformat = av_guess_format("avi", NULL, NULL);
+
+ BLI_snprintf(rv->of->filename, sizeof(rv->of->filename), "%s", fname);
+
+ fprintf(stderr, "Starting work on proxy: %s\n", rv->of->filename);
+
+ rv->st = av_new_stream(rv->of, 0);
+ rv->c = rv->st->codec;
+ rv->c->codec_type = AVMEDIA_TYPE_VIDEO;
+ rv->c->codec_id = CODEC_ID_MJPEG;
+ rv->c->width = width;
+ rv->c->height = height;
+
+ rv->of->oformat->video_codec = rv->c->codec_id;
+ rv->codec = avcodec_find_encoder(rv->c->codec_id);
+
+ if (!rv->codec) {
+ fprintf(stderr, "No ffmpeg MJPEG encoder available? "
+ "Proxy not built!\n");
+ av_free(rv->of);
+ return NULL;
+ }
+
+ if (rv->codec->pix_fmts) {
+ rv->c->pix_fmt = rv->codec->pix_fmts[0];
+ } else {
+ rv->c->pix_fmt = PIX_FMT_YUVJ420P;
+ }
+
+ rv->c->sample_aspect_ratio
+ = rv->st->sample_aspect_ratio
+ = st->codec->sample_aspect_ratio;
+
+ rv->c->time_base.den = 25;
+ rv->c->time_base.num = 1;
+ rv->st->time_base = rv->c->time_base;
+
+ if (rv->of->flags & AVFMT_GLOBALHEADER) {
+ rv->c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+ }
+
+ if (av_set_parameters(rv->of, NULL) < 0) {
+ fprintf(stderr, "Couldn't set output parameters? "
+ "Proxy not built!\n");
+ av_free(rv->of);
+ return 0;
+ }
+
+ if (avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE) < 0) {
+ fprintf(stderr, "Couldn't open outputfile! "
+ "Proxy not built!\n");
+ av_free(rv->of);
+ return 0;
+ }
+
+ avcodec_open(rv->c, rv->codec);
+
+ rv->video_buffersize = 2000000;
+ rv->video_buffer = (uint8_t*)MEM_mallocN(
+ rv->video_buffersize, "FFMPEG video buffer");
+
+ rv->orig_height = st->codec->height;
+
+ if (st->codec->width != width || st->codec->height != height
+ || st->codec->pix_fmt != rv->c->pix_fmt) {
+ rv->frame = avcodec_alloc_frame();
+ avpicture_fill((AVPicture*) rv->frame,
+ MEM_mallocN(avpicture_get_size(
+ rv->c->pix_fmt,
+ round_up(width, 16), height),
+ "alloc proxy output frame"),
+ rv->c->pix_fmt, round_up(width, 16), height);
+
+ rv->sws_ctx = sws_getContext(
+ st->codec->width,
+ st->codec->height,
+ st->codec->pix_fmt,
+ width, height,
+ rv->c->pix_fmt,
+ SWS_FAST_BILINEAR | SWS_PRINT_INFO,
+ NULL, NULL, NULL);
+ }
+
+ av_write_header(rv->of);
+
+ return rv;
+}
+
+static int add_to_proxy_output_ffmpeg(
+ struct proxy_output_ctx * ctx, AVFrame * frame)
+{
+ int outsize = 0;
+
+ if (!ctx) {
+ return 0;
+ }
+
+ if (ctx->sws_ctx && frame &&
+ (frame->data[0] || frame->data[1] ||
+ frame->data[2] || frame->data[3])) {
+ sws_scale(ctx->sws_ctx, (const uint8_t * const*) frame->data,
+ frame->linesize, 0, ctx->orig_height,
+ ctx->frame->data, ctx->frame->linesize);
+ }
+
+ ctx->frame->pts = ctx->cfra++;
+
+ outsize = avcodec_encode_video(
+ ctx->c, ctx->video_buffer, ctx->video_buffersize,
+ ctx->sws_ctx ? (frame ? ctx->frame : 0) : frame);
+
+ if (outsize < 0) {
+ fprintf(stderr, "Error encoding proxy frame %d for '%s'\n",
+ ctx->cfra - 1, ctx->of->filename);
+ return 0;
+ }
+
+ if (outsize != 0) {
+ AVPacket packet;
+ av_init_packet(&packet);
+
+ if (ctx->c->coded_frame->pts != AV_NOPTS_VALUE) {
+ packet.pts = av_rescale_q(ctx->c->coded_frame->pts,
+ ctx->c->time_base,
+ ctx->st->time_base);
+ }
+ if (ctx->c->coded_frame->key_frame)
+ packet.flags |= AV_PKT_FLAG_KEY;
+
+ packet.stream_index = ctx->st->index;
+ packet.data = ctx->video_buffer;
+ packet.size = outsize;
+
+ if (av_interleaved_write_frame(ctx->of, &packet) != 0) {
+ fprintf(stderr, "Error writing proxy frame %d "
+ "into '%s'\n", ctx->cfra - 1,
+ ctx->of->filename);
+ return 0;
+ }
+
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static void free_proxy_output_ffmpeg(struct proxy_output_ctx * ctx,
+ int rollback)
+{
+ int i;
+ char fname[FILE_MAXDIR+FILE_MAXFILE];
+ char fname_tmp[FILE_MAXDIR+FILE_MAXFILE];
+
+ if (!ctx) {
+ return;
+ }
+
+ if (!rollback) {
+ while (add_to_proxy_output_ffmpeg(ctx, NULL)) ;
+ }
+
+ avcodec_flush_buffers(ctx->c);
+
+ av_write_trailer(ctx->of);
+
+ avcodec_close(ctx->c);
+
+ for (i = 0; i < ctx->of->nb_streams; i++) {
+ if (&ctx->of->streams[i]) {
+ av_freep(&ctx->of->streams[i]);
+ }
+ }
+
+ if (ctx->of->oformat) {
+ if (!(ctx->of->oformat->flags & AVFMT_NOFILE)) {
+ avio_close(ctx->of->pb);
+ }
+ }
+ av_free(ctx->of);
+
+ MEM_freeN(ctx->video_buffer);
+
+ if (ctx->sws_ctx) {
+ sws_freeContext(ctx->sws_ctx);
+
+ MEM_freeN(ctx->frame->data[0]);
+ av_free(ctx->frame);
+ }
+
+ get_proxy_filename(ctx->anim, ctx->proxy_size,
+ fname_tmp, TRUE);
+
+ if (rollback) {
+ unlink(fname_tmp);
+ } else {
+ get_proxy_filename(ctx->anim, ctx->proxy_size,
+ fname, FALSE);
+ rename(fname_tmp, fname);
+ }
+
+ MEM_freeN(ctx);
+}
+
+
+static int index_rebuild_ffmpeg(struct anim * anim,
+ IMB_Timecode_Type tcs_in_use,
+ IMB_Proxy_Size proxy_sizes_in_use,
+ int quality,
+ short *stop, short *do_update,
+ float *progress)
+{
+ int i, videoStream;
+ unsigned long long seek_pos = 0;
+ unsigned long long last_seek_pos = 0;
+ unsigned long long seek_pos_dts = 0;
+ unsigned long long seek_pos_pts = 0;
+ unsigned long long last_seek_pos_dts = 0;
+ unsigned long long start_pts = 0;
+ double frame_rate;
+ double pts_time_base;
+ int frameno = 0;
+ int start_pts_set = FALSE;
+
+ AVFormatContext *iFormatCtx;
+ AVCodecContext *iCodecCtx;
+ AVCodec *iCodec;
+ AVStream *iStream;
+ AVFrame* in_frame = 0;
+ AVPacket next_packet;
+ int streamcount;
+
+ struct proxy_output_ctx * proxy_ctx[IMB_PROXY_MAX_SLOT];
+ anim_index_builder * indexer [IMB_TC_MAX_SLOT];
+
+ int num_proxy_sizes = IMB_PROXY_MAX_SLOT;
+ int num_indexers = IMB_TC_MAX_SLOT;
+ uint64_t stream_size;
+
+ memset(proxy_ctx, 0, sizeof(proxy_ctx));
+ memset(indexer, 0, sizeof(indexer));
+
+ if(av_open_input_file(&iFormatCtx, anim->name, NULL, 0, NULL) != 0) {
+ return 0;
+ }
+
+ if (av_find_stream_info(iFormatCtx) < 0) {
+ av_close_input_file(iFormatCtx);
+ return 0;
+ }
+
+ streamcount = anim->streamindex;
+
+ /* Find the video stream */
+ videoStream = -1;
+ for (i = 0; i < iFormatCtx->nb_streams; i++)
+ if(iFormatCtx->streams[i]->codec->codec_type
+ == AVMEDIA_TYPE_VIDEO) {
+ if (streamcount > 0) {
+ streamcount--;
+ continue;
+ }
+ videoStream = i;
+ break;
+ }
+
+ if (videoStream == -1) {
+ av_close_input_file(iFormatCtx);
+ return 0;
+ }
+
+ iStream = iFormatCtx->streams[videoStream];
+ iCodecCtx = iStream->codec;
+
+ iCodec = avcodec_find_decoder(iCodecCtx->codec_id);
+
+ if (iCodec == NULL) {
+ av_close_input_file(iFormatCtx);
+ return 0;
+ }
+
+ iCodecCtx->workaround_bugs = 1;
+
+ if (avcodec_open(iCodecCtx, iCodec) < 0) {
+ av_close_input_file(iFormatCtx);
+ return 0;
+ }
+
+ in_frame = avcodec_alloc_frame();
+
+ stream_size = avio_size(iFormatCtx->pb);
+
+ for (i = 0; i < num_proxy_sizes; i++) {
+ if (proxy_sizes_in_use & proxy_sizes[i]) {
+ proxy_ctx[i] = alloc_proxy_output_ffmpeg(
+ anim, iStream, proxy_sizes[i],
+ iCodecCtx->width * proxy_fac[i],
+ iCodecCtx->height * proxy_fac[i],
+ quality);
+ if (!proxy_ctx[i]) {
+ proxy_sizes_in_use &= ~proxy_sizes[i];
+ }
+ }
+ }
+
+ for (i = 0; i < num_indexers; i++) {
+ if (tcs_in_use & tc_types[i]) {
+ char fname[FILE_MAXDIR+FILE_MAXFILE];
+
+ get_tc_filename(anim, tc_types[i], fname);
+
+ indexer[i] = IMB_index_builder_create(fname);
+ if (!indexer[i]) {
+ tcs_in_use &= ~tc_types[i];
+ }
+ }
+ }
+
+ frame_rate = av_q2d(iStream->r_frame_rate);
+ pts_time_base = av_q2d(iStream->time_base);
+
+ while(av_read_frame(iFormatCtx, &next_packet) >= 0) {
+ int frame_finished = 0;
+ float next_progress = ((int)floor(((double) next_packet.pos) * 100 /
+ ((double) stream_size)+0.5)) / 100;
+
+ if (*progress != next_progress) {
+ *progress = next_progress;
+ *do_update = 1;
+ }
+
+ if (*stop) {
+ av_free_packet(&next_packet);
+ break;
+ }
+
+ if (next_packet.stream_index == videoStream) {
+ if (next_packet.flags & AV_PKT_FLAG_KEY) {
+ last_seek_pos = seek_pos;
+ last_seek_pos_dts = seek_pos_dts;
+ seek_pos = next_packet.pos;
+ seek_pos_dts = next_packet.dts;
+ seek_pos_pts = next_packet.pts;
+ }
+
+ avcodec_decode_video2(
+ iCodecCtx, in_frame, &frame_finished,
+ &next_packet);
+ }
+
+ if (frame_finished) {
+ unsigned long long s_pos = seek_pos;
+ unsigned long long s_dts = seek_pos_dts;
+ unsigned long long pts
+ = av_get_pts_from_frame(iFormatCtx, in_frame);
+
+ for (i = 0; i < num_proxy_sizes; i++) {
+ add_to_proxy_output_ffmpeg(
+ proxy_ctx[i], in_frame);
+ }
+
+ if (!start_pts_set) {
+ start_pts = pts;
+ start_pts_set = TRUE;
+ }
+
+ frameno = (pts - start_pts)
+ * pts_time_base * frame_rate;
+
+ /* decoding starts *always* on I-Frames,
+ so: P-Frames won't work, even if all the
+ information is in place, when we seek
+ to the I-Frame presented *after* the P-Frame,
+ but located before the P-Frame within
+ the stream */
+
+ if (pts < seek_pos_pts) {
+ s_pos = last_seek_pos;
+ s_dts = last_seek_pos_dts;
+ }
+
+ for (i = 0; i < num_indexers; i++) {
+ if (tcs_in_use & tc_types[i]) {
+ IMB_index_builder_proc_frame(
+ indexer[i],
+ next_packet.data,
+ next_packet.size,
+ frameno, s_pos, s_dts, pts);
+ }
+ }
+ }
+ av_free_packet(&next_packet);
+ }
+
+ for (i = 0; i < num_indexers; i++) {
+ if (tcs_in_use & tc_types[i]) {
+ IMB_index_builder_finish(indexer[i], *stop);
+ }
+ }
+
+ for (i = 0; i < num_proxy_sizes; i++) {
+ if (proxy_sizes_in_use & proxy_sizes[i]) {
+ free_proxy_output_ffmpeg(proxy_ctx[i], *stop);
+ }
+ }
+
+ av_free(in_frame);
+
+ return 1;
+}
+
+#endif
+
+/* ----------------------------------------------------------------------
+ - internal AVI (fallback) rebuilder
+ ---------------------------------------------------------------------- */
+
+static AviMovie * alloc_proxy_output_avi(
+ struct anim * anim, char * filename, int width, int height,
+ int quality)
+{
+ int x, y;
+ AviFormat format;
+ double framerate;
+ AviMovie * avi;
+ short frs_sec = 25; /* it doesn't really matter for proxies,
+ but sane defaults help anyways...*/
+ float frs_sec_base = 1.0;
+
+ IMB_anim_get_fps(anim, &frs_sec, &frs_sec_base);
+
+ x = width;
+ y = height;
+
+ framerate= (double) frs_sec / (double) frs_sec_base;
+
+ avi = MEM_mallocN (sizeof(AviMovie), "avimovie");
+
+ format = AVI_FORMAT_MJPEG;
+
+ if (AVI_open_compress (filename, avi, 1, format) != AVI_ERROR_NONE) {
+ MEM_freeN(avi);
+ return 0;
+ }
+
+ AVI_set_compress_option (avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_WIDTH, &x);
+ AVI_set_compress_option (avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_HEIGHT, &y);
+ AVI_set_compress_option (avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_QUALITY, &quality);
+ AVI_set_compress_option (avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_FRAMERATE, &framerate);
+
+ avi->interlace= 0;
+ avi->odd_fields= 0;
+
+ return avi;
+}
+
+static void index_rebuild_fallback(struct anim * anim,
+ IMB_Timecode_Type tcs_in_use,
+ IMB_Proxy_Size proxy_sizes_in_use,
+ int quality,
+ short *stop, short *do_update,
+ float *progress)
+{
+ int cnt = IMB_anim_get_duration(anim, IMB_TC_NONE);
+ int i, pos;
+ AviMovie * proxy_ctx[IMB_PROXY_MAX_SLOT];
+ char fname[FILE_MAXDIR+FILE_MAXFILE];
+ char fname_tmp[FILE_MAXDIR+FILE_MAXFILE];
+
+ memset(proxy_ctx, 0, sizeof(proxy_ctx));
+
+ /* since timecode indices only work with ffmpeg right now,
+ don't know a sensible fallback here...
+
+ so no proxies, no game to play...
+ */
+ if (proxy_sizes_in_use == IMB_PROXY_NONE) {
+ return;
+ }
+
+ for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
+ if (proxy_sizes_in_use & proxy_sizes[i]) {
+ char fname[FILE_MAXDIR+FILE_MAXFILE];
+
+ get_proxy_filename(anim, proxy_sizes[i], fname, TRUE);
+ BLI_make_existing_file(fname);
+
+ proxy_ctx[i] = alloc_proxy_output_avi(
+ anim, fname,
+ anim->x * proxy_fac[i],
+ anim->y * proxy_fac[i],
+ quality);
+ }
+ }
+
+ for (pos = 0; pos < cnt; pos++) {
+ struct ImBuf * ibuf = IMB_anim_absolute(
+ anim, pos, IMB_TC_NONE, IMB_PROXY_NONE);
+ int next_progress = (int) ((double) pos / (double) cnt);
+
+ if (*progress != next_progress) {
+ *progress = next_progress;
+ *do_update = 1;
+ }
+
+ if (*stop) {
+ break;
+ }
+
+ IMB_flipy(ibuf);
+
+ for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
+ if (proxy_sizes_in_use & proxy_sizes[i]) {
+ int x = anim->x * proxy_fac[i];
+ int y = anim->y * proxy_fac[i];
+
+ struct ImBuf * s_ibuf = IMB_scalefastImBuf(
+ ibuf, x, y);
+
+ IMB_convert_rgba_to_abgr(s_ibuf);
+
+ AVI_write_frame (proxy_ctx[i], pos,
+ AVI_FORMAT_RGB32,
+ s_ibuf->rect, x * y * 4);
+
+ /* note that libavi free's the buffer... */
+ s_ibuf->rect = 0;
+
+ IMB_freeImBuf(s_ibuf);
+ }
+ }
+ }
+
+ for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
+ if (proxy_sizes_in_use & proxy_sizes[i]) {
+ AVI_close_compress (proxy_ctx[i]);
+ MEM_freeN (proxy_ctx[i]);
+
+ get_proxy_filename(anim, proxy_sizes[i],
+ fname_tmp, TRUE);
+ get_proxy_filename(anim, proxy_sizes[i],
+ fname, FALSE);
+
+ if (*stop) {
+ unlink(fname_tmp);
+ } else {
+ rename(fname_tmp, fname);
+ }
+ }
+ }
+}
+
+/* ----------------------------------------------------------------------
+ - public API
+ ---------------------------------------------------------------------- */
+
+void IMB_anim_index_rebuild(struct anim * anim, IMB_Timecode_Type tcs_in_use,
+ IMB_Proxy_Size proxy_sizes_in_use,
+ int quality,
+ short *stop, short *do_update, float *progress)
+{
+ switch (anim->curtype) {
+#ifdef WITH_FFMPEG
+ case ANIM_FFMPEG:
+ index_rebuild_ffmpeg(anim, tcs_in_use, proxy_sizes_in_use,
+ quality, stop, do_update, progress);
+ break;
+#endif
+ default:
+ index_rebuild_fallback(anim, tcs_in_use, proxy_sizes_in_use,
+ quality, stop, do_update, progress);
+ break;
+ }
+}
+
+void IMB_free_indices(struct anim * anim)
+{
+ int i;
+
+ for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
+ if (anim->proxy_anim[i]) {
+ IMB_close_anim(anim->proxy_anim[i]);
+ anim->proxy_anim[i] = 0;
+ }
+ }
+
+ for (i = 0; i < IMB_TC_MAX_SLOT; i++) {
+ if (anim->curr_idx[i]) {
+ IMB_indexer_close(anim->curr_idx[i]);
+ anim->curr_idx[i] = 0;
+ }
+ }
+
+
+ anim->proxies_tried = 0;
+ anim->indices_tried = 0;
+}
+
+void IMB_anim_set_index_dir(struct anim * anim, const char * dir)
+{
+ if (strcmp(anim->index_dir, dir) == 0) {
+ return;
+ }
+ BLI_strncpy(anim->index_dir, dir, sizeof(anim->index_dir));
+
+ IMB_free_indices(anim);
+}
+
+struct anim * IMB_anim_open_proxy(
+ struct anim * anim, IMB_Proxy_Size preview_size)
+{
+ char fname[FILE_MAXDIR+FILE_MAXFILE];
+ int i = IMB_proxy_size_to_array_index(preview_size);
+
+ if (anim->proxy_anim[i]) {
+ return anim->proxy_anim[i];
+ }
+
+ if (anim->proxies_tried & preview_size) {
+ return NULL;
+ }
+
+ get_proxy_filename(anim, preview_size, fname, FALSE);
+
+ anim->proxy_anim[i] = IMB_open_anim(fname, 0, 0);
+
+ anim->proxies_tried |= preview_size;
+
+ return anim->proxy_anim[i];
+}
+
+struct anim_index * IMB_anim_open_index(
+ struct anim * anim, IMB_Timecode_Type tc)
+{
+ char fname[FILE_MAXDIR+FILE_MAXFILE];
+ int i = IMB_timecode_to_array_index(tc);
+
+ if (anim->curr_idx[i]) {
+ return anim->curr_idx[i];
+ }
+
+ if (anim->indices_tried & tc) {
+ return 0;
+ }
+
+ get_tc_filename(anim, tc, fname);
+
+ anim->curr_idx[i] = IMB_indexer_open(fname);
+
+ anim->indices_tried |= tc;
+
+ return anim->curr_idx[i];
+}
+
+int IMB_anim_index_get_frame_index(struct anim * anim, IMB_Timecode_Type tc,
+ int position)
+{
+ struct anim_index * idx = IMB_anim_open_index(anim, tc);
+
+ if (!idx) {
+ return position;
+ }
+
+ return IMB_indexer_get_frame_index(idx, position);
+}
+