Refactor of proxies build operators

Split proxy build operator into three parts:
- Prepare context (IMB_anim_index_rebuild_context) which prepares all
  needed data and stores it in an anonymous structure used by specific
  builder lately.
- Build proxies/timecodes into temporary files (IMB_anim_index_rebuild)
  This function will build all selected proxies/timecodes into a temporary
  files so old proxies will be still available during building.
- Finish building proxies (IMB_anim_index_rebuild_finish) which copies
  temporary files over old proxies filed and releases all resources used
  by a context.

Context creation and finishing building happens in a main thread so
it's easy and safe to close all opened handles of proxies files and
refresh cache after rebuilding is finished.

This should finally fix #30315: Temporary proxy files are not erased and old proxys are not updated if the proxy is built more then once (windows)
This commit is contained in:
Sergey Sharybin 2012-02-29 12:08:26 +00:00
parent 31cd0521ae
commit d8bdd4497e
7 changed files with 380 additions and 226 deletions

@ -40,6 +40,8 @@ struct Strip;
struct StripElem; struct StripElem;
struct bSound; struct bSound;
struct SeqIndexBuildContext;
#define BUILD_SEQAR_COUNT_NOTHING 0 #define BUILD_SEQAR_COUNT_NOTHING 0
#define BUILD_SEQAR_COUNT_CURRENT 1 #define BUILD_SEQAR_COUNT_CURRENT 1
#define BUILD_SEQAR_COUNT_CHILDREN 2 #define BUILD_SEQAR_COUNT_CHILDREN 2
@ -194,9 +196,10 @@ void update_changed_seq_and_deps(struct Scene *scene, struct Sequence *changed_s
int input_have_to_preprocess( int input_have_to_preprocess(
SeqRenderData context, struct Sequence * seq, float cfra); SeqRenderData context, struct Sequence * seq, float cfra);
void seq_proxy_rebuild(struct Main * bmain, struct SeqIndexBuildContext *seq_proxy_rebuild_context(struct Main *bmain, struct Scene *scene, struct Sequence *seq);
struct Scene *scene, struct Sequence * seq, void seq_proxy_rebuild(struct SeqIndexBuildContext *context,
short *stop, short *do_update, float *progress); short *stop, short *do_update, float *progress);
void seq_proxy_rebuild_finish(struct SeqIndexBuildContext *context, short stop);
/* ********************************************************************** /* **********************************************************************

@ -1140,6 +1140,18 @@ static int get_shown_sequences( ListBase * seqbasep, int cfra, int chanshown, Se
proxy management proxy management
********************************************************************** */ ********************************************************************** */
typedef struct SeqIndexBuildContext {
struct IndexBuildContext *index_context;
int tc_flags;
int size_flags;
int quality;
Main *bmain;
Scene *scene;
Sequence *seq, *orig_seq;
} SeqIndexBuildContext;
#define PROXY_MAXFILE (2*FILE_MAXDIR+FILE_MAXFILE) #define PROXY_MAXFILE (2*FILE_MAXDIR+FILE_MAXFILE)
static IMB_Proxy_Size seq_rendersize_to_proxysize(int size) static IMB_Proxy_Size seq_rendersize_to_proxysize(int size)
@ -1345,35 +1357,56 @@ static void seq_proxy_build_frame(SeqRenderData context,
IMB_freeImBuf(ibuf); IMB_freeImBuf(ibuf);
} }
void seq_proxy_rebuild(struct Main * bmain, Scene *scene, Sequence * seq, struct SeqIndexBuildContext *seq_proxy_rebuild_context(Main *bmain, Scene *scene, Sequence *seq)
short *stop, short *do_update, float *progress)
{ {
SeqRenderData context; SeqIndexBuildContext *context;
int cfra; Sequence *nseq;
int tc_flags;
int size_flags;
int quality;
if (!seq->strip || !seq->strip->proxy) { if (!seq->strip || !seq->strip->proxy) {
return; return NULL;
} }
if (!(seq->flag & SEQ_USE_PROXY)) { if (!(seq->flag & SEQ_USE_PROXY)) {
return; return NULL;
} }
tc_flags = seq->strip->proxy->build_tc_flags; context = MEM_callocN(sizeof(SeqIndexBuildContext), "seq proxy rebuild context");
size_flags = seq->strip->proxy->build_size_flags;
quality = seq->strip->proxy->quality; nseq = seq_dupli_recursive(scene, scene, seq, 0);
context->tc_flags = nseq->strip->proxy->build_tc_flags;
context->size_flags = nseq->strip->proxy->build_size_flags;
context->quality = nseq->strip->proxy->quality;
context->bmain = bmain;
context->scene = scene;
context->orig_seq = seq;
context->seq = nseq;
if (nseq->type == SEQ_MOVIE) {
seq_open_anim_file(nseq);
if (nseq->anim) {
context->index_context = IMB_anim_index_rebuild_context(nseq->anim,
context->tc_flags, context->size_flags, context->quality);
}
}
return context;
}
void seq_proxy_rebuild(SeqIndexBuildContext *context, short *stop, short *do_update, float *progress)
{
SeqRenderData render_context;
Sequence *seq = context->seq;
Scene *scene = context->scene;
int cfra;
if (seq->type == SEQ_MOVIE) { if (seq->type == SEQ_MOVIE) {
seq_open_anim_file(seq); if (context->index_context) {
IMB_anim_index_rebuild(context->index_context, stop, do_update, progress);
if (seq->anim) {
IMB_anim_index_rebuild(
seq->anim, tc_flags, size_flags, quality,
stop, do_update, progress);
} }
return; return;
} }
@ -1388,25 +1421,25 @@ void seq_proxy_rebuild(struct Main * bmain, Scene *scene, Sequence * seq,
/* fail safe code */ /* fail safe code */
context = seq_new_render_data( render_context = seq_new_render_data(
bmain, scene, context->bmain, context->scene,
(scene->r.size * (float) scene->r.xsch) / 100.0f + 0.5f, (scene->r.size * (float) scene->r.xsch) / 100.0f + 0.5f,
(scene->r.size * (float) scene->r.ysch) / 100.0f + 0.5f, (scene->r.size * (float) scene->r.ysch) / 100.0f + 0.5f,
100); 100);
for (cfra = seq->startdisp + seq->startstill; for (cfra = seq->startdisp + seq->startstill;
cfra < seq->enddisp - seq->endstill; cfra++) { cfra < seq->enddisp - seq->endstill; cfra++) {
if (size_flags & IMB_PROXY_25) { if (context->size_flags & IMB_PROXY_25) {
seq_proxy_build_frame(context, seq, cfra, 25); seq_proxy_build_frame(render_context, seq, cfra, 25);
} }
if (size_flags & IMB_PROXY_50) { if (context->size_flags & IMB_PROXY_50) {
seq_proxy_build_frame(context, seq, cfra, 50); seq_proxy_build_frame(render_context, seq, cfra, 50);
} }
if (size_flags & IMB_PROXY_75) { if (context->size_flags & IMB_PROXY_75) {
seq_proxy_build_frame(context, seq, cfra, 75); seq_proxy_build_frame(render_context, seq, cfra, 75);
} }
if (size_flags & IMB_PROXY_100) { if (context->size_flags & IMB_PROXY_100) {
seq_proxy_build_frame(context, seq, cfra, 100); seq_proxy_build_frame(render_context, seq, cfra, 100);
} }
*progress= (float)cfra/(seq->enddisp - seq->endstill *progress= (float)cfra/(seq->enddisp - seq->endstill
@ -1418,6 +1451,18 @@ void seq_proxy_rebuild(struct Main * bmain, Scene *scene, Sequence * seq,
} }
} }
void seq_proxy_rebuild_finish(SeqIndexBuildContext *context, short stop)
{
if (context->index_context) {
IMB_close_anim_proxies(context->seq->anim);
IMB_close_anim_proxies(context->orig_seq->anim);
IMB_anim_index_rebuild_finish(context->index_context, stop);
}
seq_free_sequence_recurse(context->scene, context->seq);
MEM_freeN(context);
}
/* ********************************************************************** /* **********************************************************************
color balance color balance

@ -845,7 +845,8 @@ typedef struct ProxyBuildJob {
Scene *scene; Scene *scene;
struct Main *main; struct Main *main;
MovieClip *clip; MovieClip *clip;
int clip_flag; int clip_flag, stop;
struct IndexBuildContext *index_context;
} ProxyJob; } ProxyJob;
static void proxy_freejob(void *pjv) static void proxy_freejob(void *pjv)
@ -896,11 +897,13 @@ static void proxy_startjob(void *pjv, short *stop, short *do_update, float *prog
build_undistort_count= proxy_bitflag_to_array(size_flag, build_undistort_sizes, 1); build_undistort_count= proxy_bitflag_to_array(size_flag, build_undistort_sizes, 1);
if(clip->source == MCLIP_SRC_MOVIE) { if(clip->source == MCLIP_SRC_MOVIE) {
if(clip->anim) if (pj->index_context)
IMB_anim_index_rebuild(clip->anim, tc_flag, size_flag, quality, stop, do_update, progress); IMB_anim_index_rebuild(pj->index_context, stop, do_update, progress);
if(!build_undistort_count) { if(!build_undistort_count) {
BKE_movieclip_reload(clip); if (*stop)
pj->stop = 1;
return; return;
} }
else { else {
@ -928,7 +931,23 @@ static void proxy_startjob(void *pjv, short *stop, short *do_update, float *prog
if(distortion) if(distortion)
BKE_tracking_distortion_destroy(distortion); BKE_tracking_distortion_destroy(distortion);
BKE_movieclip_reload(clip); if (*stop)
pj->stop = 1;
}
static void proxy_endjob(void *pjv)
{
ProxyJob *pj = pjv;
if (pj->clip->anim)
IMB_close_anim_proxies(pj->clip->anim);
if (pj->index_context)
IMB_anim_index_rebuild_finish(pj->index_context, pj->stop);
BKE_movieclip_reload(pj->clip);
WM_main_add_notifier(NC_MOVIECLIP|ND_DISPLAY, pj->clip);
} }
static int clip_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op)) static int clip_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
@ -951,9 +970,14 @@ static int clip_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
pj->clip= clip; pj->clip= clip;
pj->clip_flag= clip->flag&MCLIP_TIMECODE_FLAGS; pj->clip_flag= clip->flag&MCLIP_TIMECODE_FLAGS;
if (clip->anim) {
pj->index_context = IMB_anim_index_rebuild_context(clip->anim, clip->proxy.build_tc_flag,
clip->proxy.build_size_flag, clip->proxy.quality);
}
WM_jobs_customdata(steve, pj, proxy_freejob); WM_jobs_customdata(steve, pj, proxy_freejob);
WM_jobs_timer(steve, 0.2, NC_MOVIECLIP|ND_DISPLAY, 0); WM_jobs_timer(steve, 0.2, NC_MOVIECLIP|ND_DISPLAY, 0);
WM_jobs_callbacks(steve, proxy_startjob, NULL, NULL, NULL); WM_jobs_callbacks(steve, proxy_startjob, NULL, NULL, proxy_endjob);
G.afbreek= 0; G.afbreek= 0;
WM_jobs_start(CTX_wm_manager(C), steve); WM_jobs_start(CTX_wm_manager(C), steve);

@ -56,6 +56,8 @@
#include "BKE_report.h" #include "BKE_report.h"
#include "BKE_sound.h" #include "BKE_sound.h"
#include "IMB_imbuf.h"
#include "WM_api.h" #include "WM_api.h"
#include "WM_types.h" #include "WM_types.h"
@ -131,20 +133,14 @@ typedef struct ProxyBuildJob {
Scene *scene; Scene *scene;
struct Main * main; struct Main * main;
ListBase queue; ListBase queue;
ThreadMutex queue_lock; int stop;
} ProxyJob; } ProxyJob;
static void proxy_freejob(void *pjv) static void proxy_freejob(void *pjv)
{ {
ProxyJob *pj= pjv; ProxyJob *pj= pjv;
Sequence * seq;
for (seq = pj->queue.first; seq; seq = seq->next) { BLI_freelistN(&pj->queue);
BLI_remlink(&pj->queue, seq);
seq_free_sequence_recurse(pj->scene, seq);
}
BLI_mutex_end(&pj->queue_lock);
MEM_freeN(pj); MEM_freeN(pj);
} }
@ -153,30 +149,17 @@ static void proxy_freejob(void *pjv)
static void proxy_startjob(void *pjv, short *stop, short *do_update, float *progress) static void proxy_startjob(void *pjv, short *stop, short *do_update, float *progress)
{ {
ProxyJob *pj = pjv; ProxyJob *pj = pjv;
LinkData *link;
while (!*stop) { for (link = pj->queue.first; link; link = link->next) {
Sequence * seq; struct SeqIndexBuildContext *context = link->data;
BLI_mutex_lock(&pj->queue_lock); seq_proxy_rebuild(context, stop, do_update, progress);
if (!pj->queue.first) {
BLI_mutex_unlock(&pj->queue_lock);
break;
}
seq = pj->queue.first;
BLI_remlink(&pj->queue, seq);
BLI_mutex_unlock(&pj->queue_lock);
seq_proxy_rebuild(pj->main, pj->scene, seq,
stop, do_update, progress);
seq_free_sequence_recurse(pj->scene, seq);
} }
if (*stop) { if (*stop) {
fprintf(stderr, pj->stop = 1;
"Canceling proxy rebuild on users request...\n"); fprintf(stderr, "Canceling proxy rebuild on users request...\n");
} }
} }
@ -184,23 +167,29 @@ static void proxy_endjob(void *pjv)
{ {
ProxyJob *pj = pjv; ProxyJob *pj = pjv;
Editing *ed = seq_give_editing(pj->scene, FALSE); Editing *ed = seq_give_editing(pj->scene, FALSE);
LinkData *link;
for (link = pj->queue.first; link; link = link->next) {
seq_proxy_rebuild_finish(link->data, pj->stop);
}
free_imbuf_seq(pj->scene, &ed->seqbase, FALSE, FALSE); free_imbuf_seq(pj->scene, &ed->seqbase, FALSE, FALSE);
WM_main_add_notifier(NC_SCENE|ND_SEQUENCER, pj->scene); WM_main_add_notifier(NC_SCENE|ND_SEQUENCER, pj->scene);
} }
static void seq_proxy_build_job(const bContext *C, Sequence * seq) static void seq_proxy_build_job(const bContext *C)
{ {
wmJob * steve; wmJob * steve;
ProxyJob *pj; ProxyJob *pj;
Scene *scene= CTX_data_scene(C); Scene *scene= CTX_data_scene(C);
Editing *ed = seq_give_editing(scene, FALSE);
ScrArea * sa= CTX_wm_area(C); ScrArea * sa= CTX_wm_area(C);
struct SeqIndexBuildContext *context;
LinkData *link;
Sequence * seq;
seq = seq_dupli_recursive(scene, scene, seq, 0); steve = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), sa, "Building Proxies", WM_JOB_PROGRESS);
steve = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C),
sa, "Building Proxies", WM_JOB_PROGRESS);
pj = WM_jobs_get_customdata(steve); pj = WM_jobs_get_customdata(steve);
@ -210,18 +199,19 @@ static void seq_proxy_build_job(const bContext *C, Sequence * seq)
pj->scene= scene; pj->scene= scene;
pj->main = CTX_data_main(C); pj->main = CTX_data_main(C);
BLI_mutex_init(&pj->queue_lock);
WM_jobs_customdata(steve, pj, proxy_freejob); WM_jobs_customdata(steve, pj, proxy_freejob);
WM_jobs_timer(steve, 0.1, NC_SCENE|ND_SEQUENCER, WM_jobs_timer(steve, 0.1, NC_SCENE|ND_SEQUENCER, NC_SCENE|ND_SEQUENCER);
NC_SCENE|ND_SEQUENCER); WM_jobs_callbacks(steve, proxy_startjob, NULL, NULL, proxy_endjob);
WM_jobs_callbacks(steve, proxy_startjob, NULL, NULL,
proxy_endjob);
} }
BLI_mutex_lock(&pj->queue_lock); SEQP_BEGIN(ed, seq) {
BLI_addtail(&pj->queue, seq); if ((seq->flag & SELECT)) {
BLI_mutex_unlock(&pj->queue_lock); context = seq_proxy_rebuild_context(pj->main, pj->scene, seq);
link = BLI_genericNodeN(context);
BLI_addtail(&pj->queue, link);
}
}
SEQ_END
if (!WM_jobs_is_running(steve)) { if (!WM_jobs_is_running(steve)) {
G.afbreek = 0; G.afbreek = 0;
@ -2822,16 +2812,7 @@ void SEQUENCER_OT_view_ghost_border(wmOperatorType *ot)
/* rebuild_proxy operator */ /* rebuild_proxy operator */
static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op)) static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
{ {
Scene *scene = CTX_data_scene(C); seq_proxy_build_job(C);
Editing *ed = seq_give_editing(scene, FALSE);
Sequence * seq;
SEQP_BEGIN(ed, seq) {
if ((seq->flag & SELECT)) {
seq_proxy_build_job(C, seq);
}
}
SEQ_END
return OPERATOR_FINISHED; return OPERATOR_FINISHED;
} }

@ -217,13 +217,19 @@ 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 IMB_anim_index_get_frame_index(struct anim * anim, IMB_Timecode_Type tc,
int position); int position);
struct IndexBuildContext;
/* prepare context for proxies/imecodes builder */
struct IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, IMB_Timecode_Type tcs_in_use,
IMB_Proxy_Size proxy_sizes_in_use, int quality);
/* will rebuild all used indices and proxies at once */ /* will rebuild all used indices and proxies at once */
void IMB_anim_index_rebuild(struct anim * anim, void IMB_anim_index_rebuild(struct IndexBuildContext *context,
IMB_Timecode_Type build_tcs,
IMB_Proxy_Size build_preview_sizes,
int build_quality,
short *stop, short *do_update, float *progress); short *stop, short *do_update, float *progress);
/* finish rebuilding proxises/timecodes and free temporary contexts used */
void IMB_anim_index_rebuild_finish(struct IndexBuildContext *context, short stop);
/** /**
* Return the length (in frames) of the given @a anim. * Return the length (in frames) of the given @a anim.
*/ */
@ -243,6 +249,7 @@ int IMB_anim_get_fps(struct anim * anim,
*/ */
struct anim *IMB_open_anim(const char *name, int ib_flags, int streamindex); struct anim *IMB_open_anim(const char *name, int ib_flags, int streamindex);
void IMB_close_anim(struct anim *anim); void IMB_close_anim(struct anim *anim);
void IMB_close_anim_proxies(struct anim *anim);
/** /**

@ -241,6 +241,10 @@ void IMB_close_anim(struct anim * anim)
IMB_free_anim(anim); IMB_free_anim(anim);
} }
void IMB_close_anim_proxies(struct anim *anim)
{
IMB_free_indices(anim);
}
struct anim * IMB_open_anim( const char * name, int ib_flags, int streamindex) struct anim * IMB_open_anim( const char * name, int ib_flags, int streamindex)
{ {

@ -695,102 +695,112 @@ static void free_proxy_output_ffmpeg(struct proxy_output_ctx * ctx,
MEM_freeN(ctx); MEM_freeN(ctx);
} }
typedef struct IndexBuildContext {
int anim_type;
} IndexBuildContext;
static int index_rebuild_ffmpeg(struct anim * anim, typedef struct FFmpegIndexBuilderContext {
IMB_Timecode_Type tcs_in_use, int anim_type;
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, frameno_gapless = 0;
int start_pts_set = FALSE;
AVFormatContext *iFormatCtx; AVFormatContext *iFormatCtx;
AVCodecContext *iCodecCtx; AVCodecContext *iCodecCtx;
AVCodec *iCodec; AVCodec *iCodec;
AVStream *iStream; AVStream *iStream;
AVFrame* in_frame = 0; int videoStream;
AVPacket next_packet;
int streamcount; int num_proxy_sizes;
int num_indexers;
struct proxy_output_ctx * proxy_ctx[IMB_PROXY_MAX_SLOT]; struct proxy_output_ctx * proxy_ctx[IMB_PROXY_MAX_SLOT];
anim_index_builder * indexer [IMB_TC_MAX_SLOT]; anim_index_builder * indexer [IMB_TC_MAX_SLOT];
IMB_Timecode_Type tcs_in_use;
IMB_Proxy_Size proxy_sizes_in_use;
} FFmpegIndexBuilderContext;
typedef struct FallbackIndexBuilderContext {
int anim_type;
struct anim *anim;
AviMovie *proxy_ctx[IMB_PROXY_MAX_SLOT];
IMB_Proxy_Size proxy_sizes_in_use;
} FallbackIndexBuilderContext;
static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, IMB_Timecode_Type tcs_in_use,
IMB_Proxy_Size proxy_sizes_in_use, int quality)
{
FFmpegIndexBuilderContext *context = MEM_callocN(sizeof(FFmpegIndexBuilderContext), "FFmpeg index builder context");
int num_proxy_sizes = IMB_PROXY_MAX_SLOT; int num_proxy_sizes = IMB_PROXY_MAX_SLOT;
int num_indexers = IMB_TC_MAX_SLOT; int num_indexers = IMB_TC_MAX_SLOT;
uint64_t stream_size; int i, streamcount;
memset(proxy_ctx, 0, sizeof(proxy_ctx)); context->tcs_in_use = tcs_in_use;
memset(indexer, 0, sizeof(indexer)); context->proxy_sizes_in_use = proxy_sizes_in_use;
context->num_proxy_sizes = IMB_PROXY_MAX_SLOT;
context->num_indexers = IMB_TC_MAX_SLOT;
if(av_open_input_file(&iFormatCtx, anim->name, NULL, 0, NULL) != 0) { memset(context->proxy_ctx, 0, sizeof(context->proxy_ctx));
return 0; memset(context->indexer, 0, sizeof(context->indexer));
if(av_open_input_file(&context->iFormatCtx, anim->name, NULL, 0, NULL) != 0) {
MEM_freeN(context);
return NULL;
} }
if (av_find_stream_info(iFormatCtx) < 0) { if (av_find_stream_info(context->iFormatCtx) < 0) {
av_close_input_file(iFormatCtx); av_close_input_file(context->iFormatCtx);
return 0; MEM_freeN(context);
return NULL;
} }
streamcount = anim->streamindex; streamcount = anim->streamindex;
/* Find the video stream */ /* Find the video stream */
videoStream = -1; context->videoStream = -1;
for (i = 0; i < iFormatCtx->nb_streams; i++) for (i = 0; i < context->iFormatCtx->nb_streams; i++)
if(iFormatCtx->streams[i]->codec->codec_type if(context->iFormatCtx->streams[i]->codec->codec_type
== AVMEDIA_TYPE_VIDEO) { == AVMEDIA_TYPE_VIDEO) {
if (streamcount > 0) { if (streamcount > 0) {
streamcount--; streamcount--;
continue; continue;
} }
videoStream = i; context->videoStream = i;
break; break;
} }
if (videoStream == -1) { if (context->videoStream == -1) {
av_close_input_file(iFormatCtx); av_close_input_file(context->iFormatCtx);
return 0; MEM_freeN(context);
return NULL;
} }
iStream = iFormatCtx->streams[videoStream]; context->iStream = context->iFormatCtx->streams[context->videoStream];
iCodecCtx = iStream->codec; context->iCodecCtx = context->iStream->codec;
iCodec = avcodec_find_decoder(iCodecCtx->codec_id); context->iCodec = avcodec_find_decoder(context->iCodecCtx->codec_id);
if (iCodec == NULL) { if (context->iCodec == NULL) {
av_close_input_file(iFormatCtx); av_close_input_file(context->iFormatCtx);
return 0; MEM_freeN(context);
return NULL;
} }
iCodecCtx->workaround_bugs = 1; context->iCodecCtx->workaround_bugs = 1;
if (avcodec_open(iCodecCtx, iCodec) < 0) { if (avcodec_open(context->iCodecCtx, context->iCodec) < 0) {
av_close_input_file(iFormatCtx); av_close_input_file(context->iFormatCtx);
return 0; MEM_freeN(context);
return NULL;
} }
in_frame = avcodec_alloc_frame();
stream_size = avio_size(iFormatCtx->pb);
for (i = 0; i < num_proxy_sizes; i++) { for (i = 0; i < num_proxy_sizes; i++) {
if (proxy_sizes_in_use & proxy_sizes[i]) { if (proxy_sizes_in_use & proxy_sizes[i]) {
proxy_ctx[i] = alloc_proxy_output_ffmpeg( context->proxy_ctx[i] = alloc_proxy_output_ffmpeg(
anim, iStream, proxy_sizes[i], anim, context->iStream, proxy_sizes[i],
iCodecCtx->width * proxy_fac[i], context->iCodecCtx->width * proxy_fac[i],
iCodecCtx->height * proxy_fac[i], context->iCodecCtx->height * proxy_fac[i],
quality); quality);
if (!proxy_ctx[i]) { if (!context->proxy_ctx[i]) {
proxy_sizes_in_use &= ~proxy_sizes[i]; proxy_sizes_in_use &= ~proxy_sizes[i];
} }
} }
@ -802,17 +812,61 @@ static int index_rebuild_ffmpeg(struct anim * anim,
get_tc_filename(anim, tc_types[i], fname); get_tc_filename(anim, tc_types[i], fname);
indexer[i] = IMB_index_builder_create(fname); context->indexer[i] = IMB_index_builder_create(fname);
if (!indexer[i]) { if (!context->indexer[i]) {
tcs_in_use &= ~tc_types[i]; tcs_in_use &= ~tc_types[i];
} }
} }
} }
frame_rate = av_q2d(iStream->r_frame_rate); return (IndexBuildContext *)context;
pts_time_base = av_q2d(iStream->time_base); }
while(av_read_frame(iFormatCtx, &next_packet) >= 0) { static void index_rebuild_ffmpeg_finish(FFmpegIndexBuilderContext *context, int stop)
{
int i;
for (i = 0; i < context->num_indexers; i++) {
if (context->tcs_in_use & tc_types[i]) {
IMB_index_builder_finish(context->indexer[i], stop);
}
}
for (i = 0; i < context->num_proxy_sizes; i++) {
if (context->proxy_sizes_in_use & proxy_sizes[i]) {
free_proxy_output_ffmpeg(context->proxy_ctx[i], stop);
}
}
MEM_freeN(context);
}
static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context,
short *stop, short *do_update, float *progress)
{
int i;
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, frameno_gapless = 0;
int start_pts_set = FALSE;
AVFrame* in_frame = 0;
AVPacket next_packet;
uint64_t stream_size;
in_frame = avcodec_alloc_frame();
stream_size = avio_size(context->iFormatCtx->pb);
frame_rate = av_q2d(context->iStream->r_frame_rate);
pts_time_base = av_q2d(context->iStream->time_base);
while(av_read_frame(context->iFormatCtx, &next_packet) >= 0) {
int frame_finished = 0; int frame_finished = 0;
float next_progress = (float)((int)floor(((double) next_packet.pos) * 100 / float next_progress = (float)((int)floor(((double) next_packet.pos) * 100 /
((double) stream_size)+0.5)) / 100; ((double) stream_size)+0.5)) / 100;
@ -827,7 +881,7 @@ static int index_rebuild_ffmpeg(struct anim * anim,
break; break;
} }
if (next_packet.stream_index == videoStream) { if (next_packet.stream_index == context->videoStream) {
if (next_packet.flags & AV_PKT_FLAG_KEY) { if (next_packet.flags & AV_PKT_FLAG_KEY) {
last_seek_pos = seek_pos; last_seek_pos = seek_pos;
last_seek_pos_dts = seek_pos_dts; last_seek_pos_dts = seek_pos_dts;
@ -837,7 +891,7 @@ static int index_rebuild_ffmpeg(struct anim * anim,
} }
avcodec_decode_video2( avcodec_decode_video2(
iCodecCtx, in_frame, &frame_finished, context->iCodecCtx, in_frame, &frame_finished,
&next_packet); &next_packet);
} }
@ -845,11 +899,11 @@ static int index_rebuild_ffmpeg(struct anim * anim,
unsigned long long s_pos = seek_pos; unsigned long long s_pos = seek_pos;
unsigned long long s_dts = seek_pos_dts; unsigned long long s_dts = seek_pos_dts;
unsigned long long pts unsigned long long pts
= av_get_pts_from_frame(iFormatCtx, in_frame); = av_get_pts_from_frame(context->iFormatCtx, in_frame);
for (i = 0; i < num_proxy_sizes; i++) { for (i = 0; i < context->num_proxy_sizes; i++) {
add_to_proxy_output_ffmpeg( add_to_proxy_output_ffmpeg(
proxy_ctx[i], in_frame); context->proxy_ctx[i], in_frame);
} }
if (!start_pts_set) { if (!start_pts_set) {
@ -872,15 +926,15 @@ static int index_rebuild_ffmpeg(struct anim * anim,
s_dts = last_seek_pos_dts; s_dts = last_seek_pos_dts;
} }
for (i = 0; i < num_indexers; i++) { for (i = 0; i < context->num_indexers; i++) {
if (tcs_in_use & tc_types[i]) { if (context->tcs_in_use & tc_types[i]) {
int tc_frameno = frameno; int tc_frameno = frameno;
if(tc_types[i] == IMB_TC_RECORD_RUN_NO_GAPS) if(tc_types[i] == IMB_TC_RECORD_RUN_NO_GAPS)
tc_frameno = frameno_gapless; tc_frameno = frameno_gapless;
IMB_index_builder_proc_frame( IMB_index_builder_proc_frame(
indexer[i], context->indexer[i],
next_packet.data, next_packet.data,
next_packet.size, next_packet.size,
tc_frameno, tc_frameno,
@ -893,18 +947,6 @@ static int index_rebuild_ffmpeg(struct anim * anim,
av_free_packet(&next_packet); 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); av_free(in_frame);
return 1; return 1;
@ -955,20 +997,11 @@ static AviMovie * alloc_proxy_output_avi(
return avi; return avi;
} }
static void index_rebuild_fallback(struct anim * anim, static IndexBuildContext *index_fallback_create_context(struct anim *anim, IMB_Timecode_Type UNUSED(tcs_in_use),
IMB_Timecode_Type UNUSED(tcs_in_use), IMB_Proxy_Size proxy_sizes_in_use, int quality)
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); FallbackIndexBuilderContext *context;
int i, pos; int i;
AviMovie * proxy_ctx[IMB_PROXY_MAX_SLOT];
char fname[FILE_MAX];
char fname_tmp[FILE_MAX];
memset(proxy_ctx, 0, sizeof(proxy_ctx));
/* since timecode indices only work with ffmpeg right now, /* since timecode indices only work with ffmpeg right now,
don't know a sensible fallback here... don't know a sensible fallback here...
@ -976,28 +1009,67 @@ static void index_rebuild_fallback(struct anim * anim,
so no proxies, no game to play... so no proxies, no game to play...
*/ */
if (proxy_sizes_in_use == IMB_PROXY_NONE) { if (proxy_sizes_in_use == IMB_PROXY_NONE) {
return; return NULL;
} }
context = MEM_callocN(sizeof(FallbackIndexBuilderContext), "fallback index builder context");
context->anim = anim;
context->proxy_sizes_in_use = proxy_sizes_in_use;
memset(context->proxy_ctx, 0, sizeof(context->proxy_ctx));
for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) { for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
if (proxy_sizes_in_use & proxy_sizes[i]) { if (context->proxy_sizes_in_use & proxy_sizes[i]) {
char fname[FILE_MAX]; char fname[FILE_MAX];
get_proxy_filename(anim, proxy_sizes[i], fname, TRUE); get_proxy_filename(anim, proxy_sizes[i], fname, TRUE);
BLI_make_existing_file(fname); BLI_make_existing_file(fname);
proxy_ctx[i] = alloc_proxy_output_avi( context->proxy_ctx[i] = alloc_proxy_output_avi(anim, fname,
anim, fname, anim->x * proxy_fac[i], anim->y * proxy_fac[i], quality);
anim->x * proxy_fac[i],
anim->y * proxy_fac[i],
quality);
} }
} }
return (IndexBuildContext *)context;
}
static void index_rebuild_fallback_finish(FallbackIndexBuilderContext *context, int stop)
{
struct anim *anim = context->anim;
char fname[FILE_MAX];
char fname_tmp[FILE_MAX];
int i;
for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
if (context->proxy_sizes_in_use & proxy_sizes[i]) {
AVI_close_compress(context->proxy_ctx[i]);
MEM_freeN(context->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 {
unlink(fname);
rename(fname_tmp, fname);
}
}
}
}
static void index_rebuild_fallback(FallbackIndexBuilderContext *context,
short *stop, short *do_update, float *progress)
{
int cnt = IMB_anim_get_duration(context->anim, IMB_TC_NONE);
int i, pos;
struct anim *anim = context->anim;
for (pos = 0; pos < cnt; pos++) { for (pos = 0; pos < cnt; pos++) {
struct ImBuf * ibuf = IMB_anim_absolute( struct ImBuf *ibuf = IMB_anim_absolute(anim, pos, IMB_TC_NONE, IMB_PROXY_NONE);
anim, pos, IMB_TC_NONE, IMB_PROXY_NONE); struct ImBuf *tmp_ibuf = IMB_dupImBuf(ibuf);
int next_progress = (int) ((double) pos / (double) cnt); float next_progress = (float) pos / (float) cnt;
if (*progress != next_progress) { if (*progress != next_progress) {
*progress = next_progress; *progress = next_progress;
@ -1008,19 +1080,20 @@ static void index_rebuild_fallback(struct anim * anim,
break; break;
} }
IMB_flipy(ibuf); IMB_flipy(tmp_ibuf);
for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) { for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
if (proxy_sizes_in_use & proxy_sizes[i]) { if (context->proxy_sizes_in_use & proxy_sizes[i]) {
int x = anim->x * proxy_fac[i]; int x = anim->x * proxy_fac[i];
int y = anim->y * proxy_fac[i]; int y = anim->y * proxy_fac[i];
struct ImBuf * s_ibuf = IMB_scalefastImBuf( struct ImBuf * s_ibuf = IMB_dupImBuf(tmp_ibuf);
ibuf, x, y);
IMB_scalefastImBuf(s_ibuf, x, y);
IMB_convert_rgba_to_abgr(s_ibuf); IMB_convert_rgba_to_abgr(s_ibuf);
AVI_write_frame (proxy_ctx[i], pos, AVI_write_frame (context->proxy_ctx[i], pos,
AVI_FORMAT_RGB32, AVI_FORMAT_RGB32,
s_ibuf->rect, x * y * 4); s_ibuf->rect, x * y * 4);
@ -1030,25 +1103,9 @@ static void index_rebuild_fallback(struct anim * anim,
IMB_freeImBuf(s_ibuf); IMB_freeImBuf(s_ibuf);
} }
} }
}
for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) { IMB_freeImBuf(tmp_ibuf);
if (proxy_sizes_in_use & proxy_sizes[i]) { IMB_freeImBuf(ibuf);
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 {
unlink(fname);
rename(fname_tmp, fname);
}
}
} }
} }
@ -1056,25 +1113,58 @@ static void index_rebuild_fallback(struct anim * anim,
- public API - public API
---------------------------------------------------------------------- */ ---------------------------------------------------------------------- */
void IMB_anim_index_rebuild(struct anim * anim, IMB_Timecode_Type tcs_in_use, IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, IMB_Timecode_Type tcs_in_use,
IMB_Proxy_Size proxy_sizes_in_use, IMB_Proxy_Size proxy_sizes_in_use, int quality)
int quality,
short *stop, short *do_update, float *progress)
{ {
IndexBuildContext *context = NULL;
switch (anim->curtype) { switch (anim->curtype) {
#ifdef WITH_FFMPEG #ifdef WITH_FFMPEG
case ANIM_FFMPEG: case ANIM_FFMPEG:
index_rebuild_ffmpeg(anim, tcs_in_use, proxy_sizes_in_use, context = index_ffmpeg_create_context(anim, tcs_in_use, proxy_sizes_in_use, quality);
quality, stop, do_update, progress);
break; break;
#endif #endif
default: default:
index_rebuild_fallback(anim, tcs_in_use, proxy_sizes_in_use, context = index_fallback_create_context(anim, tcs_in_use, proxy_sizes_in_use, quality);
quality, stop, do_update, progress); break;
}
if (context)
context->anim_type = anim->curtype;
return context;
}
void IMB_anim_index_rebuild(struct IndexBuildContext *context,
short *stop, short *do_update, float *progress)
{
switch (context->anim_type) {
#ifdef WITH_FFMPEG
case ANIM_FFMPEG:
index_rebuild_ffmpeg((FFmpegIndexBuilderContext*)context, stop, do_update, progress);
break;
#endif
default:
index_rebuild_fallback((FallbackIndexBuilderContext*)context, stop, do_update, progress);
break; break;
} }
} }
void IMB_anim_index_rebuild_finish(IndexBuildContext *context, short stop)
{
switch (context->anim_type) {
#ifdef WITH_FFMPEG
case ANIM_FFMPEG:
index_rebuild_ffmpeg_finish((FFmpegIndexBuilderContext*)context, stop);
break;
#endif
default:
index_rebuild_fallback_finish((FallbackIndexBuilderContext*)context, stop);
break;
}
}
void IMB_free_indices(struct anim * anim) void IMB_free_indices(struct anim * anim)
{ {
int i; int i;