== 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.
This commit is contained in:
Peter Schlaile 2011-08-28 14:46:03 +00:00
parent 852a03a6af
commit c07bd1439a
30 changed files with 2738 additions and 317 deletions

@ -71,6 +71,7 @@
#define avio_open url_fopen
#define avio_tell url_ftell
#define avio_close url_fclose
#define avio_size url_fsize
#endif
/* there are some version inbetween, which have avio_... functions but no
@ -130,4 +131,19 @@ int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
}
#endif
static inline
int64_t av_get_pts_from_frame(AVFormatContext *avctx, AVFrame * picture)
{
int64_t pts = picture->pkt_pts;
if (pts == AV_NOPTS_VALUE) {
pts = picture->pkt_dts;
}
if (pts == AV_NOPTS_VALUE) {
pts = 0;
}
return pts;
}
#endif

@ -60,6 +60,7 @@ class SEQUENCER_HT_header(Header):
layout.separator()
layout.operator("sequencer.refresh_all")
layout.template_running_jobs()
elif st.view_type == 'SEQUENCER_PREVIEW':
layout.separator()
layout.operator("sequencer.refresh_all")
@ -241,6 +242,7 @@ class SEQUENCER_MT_strip(Menu):
layout.operator("sequencer.images_separate")
layout.operator("sequencer.offset_clear")
layout.operator("sequencer.deinterlace_selected_movies")
layout.operator("sequencer.rebuild_proxy")
layout.separator()
layout.operator("sequencer.duplicate")
@ -578,6 +580,7 @@ class SEQUENCER_PT_input(SequencerButtonsPanel, Panel):
col = split.column()
col.prop(strip, "filepath", text="")
col.prop(strip, "mpeg_preseek", text="MPEG Preseek")
col.prop(strip, "streamindex", text="Stream Index")
# TODO, sound???
# end drawing filename
@ -746,7 +749,7 @@ class SEQUENCER_PT_filter(SequencerButtonsPanel, Panel):
class SEQUENCER_PT_proxy(SequencerButtonsPanel, Panel):
bl_label = "Proxy"
bl_label = "Proxy / Timecode"
@classmethod
def poll(cls, context):
@ -772,12 +775,29 @@ class SEQUENCER_PT_proxy(SequencerButtonsPanel, Panel):
flow = layout.column_flow()
flow.prop(strip, "use_proxy_custom_directory")
flow.prop(strip, "use_proxy_custom_file")
if strip.proxy: # TODO - need to add this somehow
if strip.proxy:
if strip.use_proxy_custom_directory and not strip.use_proxy_custom_file:
flow.prop(strip.proxy, "directory")
if strip.use_proxy_custom_file:
flow.prop(strip.proxy, "filepath")
row = layout.row()
row.prop(strip.proxy, "build_25")
row.prop(strip.proxy, "build_50")
row.prop(strip.proxy, "build_75")
row.prop(strip.proxy, "build_100")
col = layout.column()
col.label(text="Build JPEG quality")
col.prop(strip.proxy, "quality")
if strip.type == "MOVIE":
col = layout.column()
col.label(text="Use timecode index:")
col.prop(strip.proxy, "timecode")
class SEQUENCER_PT_preview(SequencerButtonsPanel_Output, Panel):
bl_label = "Scene Preview/Render"

@ -60,7 +60,7 @@ int BKE_ftype_to_imtype(int ftype);
int BKE_imtype_to_ftype(int imtype);
int BKE_imtype_is_movie(int imtype);
struct anim *openanim(char * name, int flags);
struct anim *openanim(char * name, int flags, int streamindex);
void image_de_interlace(struct Image *ima, int odd);

@ -177,6 +177,7 @@ int seq_recursive_apply(struct Sequence *seq, int (*apply_func)(struct Sequence
/* maintainance functions, mostly for RNA */
// extern
void seq_free_sequence(struct Scene *scene, struct Sequence *seq);
void seq_free_sequence_recurse(struct Scene *scene, struct Sequence *seq);
void seq_free_strip(struct Strip *strip);
void seq_free_editing(struct Scene *scene);
void seq_free_clipboard(void);
@ -199,6 +200,11 @@ void update_changed_seq_and_deps(struct Scene *scene, struct Sequence *changed_s
int input_have_to_preprocess(
SeqRenderData context, struct Sequence * seq, float cfra);
void seq_proxy_rebuild(struct Main * bmain,
struct Scene *scene, struct Sequence * seq,
short *stop, short *do_update, float *progress);
/* **********************************************************************
seqcache.c

@ -1371,15 +1371,15 @@ void BKE_makepicstring(char *string, const char *base, int frame, int imtype, co
}
/* used by sequencer too */
struct anim *openanim(char *name, int flags)
struct anim *openanim(char *name, int flags, int streamindex)
{
struct anim *anim;
struct ImBuf *ibuf;
anim = IMB_open_anim(name, flags);
anim = IMB_open_anim(name, flags, streamindex);
if (anim == NULL) return NULL;
ibuf = IMB_anim_absolute(anim, 0);
ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
if (ibuf == NULL) {
if(BLI_exists(name))
printf("not an anim: %s\n", name);
@ -1773,20 +1773,26 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame)
else
BLI_path_abs(str, G.main->name);
ima->anim = openanim(str, IB_rect);
/* FIXME: make several stream accessible in image editor, too*/
ima->anim = openanim(str, IB_rect, 0);
/* let's initialize this user */
if(ima->anim && iuser && iuser->frames==0)
iuser->frames= IMB_anim_get_duration(ima->anim);
iuser->frames= IMB_anim_get_duration(ima->anim,
IMB_TC_RECORD_RUN);
}
if(ima->anim) {
int dur = IMB_anim_get_duration(ima->anim);
int dur = IMB_anim_get_duration(ima->anim,
IMB_TC_RECORD_RUN);
int fra= frame-1;
if(fra<0) fra = 0;
if(fra>(dur-1)) fra= dur-1;
ibuf = IMB_anim_absolute(ima->anim, fra);
ibuf = IMB_makeSingleUser(
IMB_anim_absolute(ima->anim, fra,
IMB_TC_RECORD_RUN,
IMB_PROXY_NONE));
if(ibuf) {
image_initialize_after_load(ima, ibuf);

@ -218,6 +218,18 @@ void seq_free_sequence(Scene *scene, Sequence *seq)
MEM_freeN(seq);
}
void seq_free_sequence_recurse(Scene *scene, Sequence *seq)
{
Sequence *iseq;
for(iseq= seq->seqbase.first; iseq; iseq= iseq->next) {
seq_free_sequence_recurse(scene, iseq);
}
seq_free_sequence(scene, seq);
}
Editing *seq_give_editing(Scene *scene, int alloc)
{
if (scene->ed == NULL && alloc) {
@ -683,13 +695,16 @@ void reload_sequence_new_file(Scene *scene, Sequence * seq, int lock_range)
}
case SEQ_MOVIE:
if(seq->anim) IMB_free_anim(seq->anim);
seq->anim = openanim(str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0));
seq->anim = openanim(str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), seq->streamindex);
if (!seq->anim) {
return;
}
seq->len = IMB_anim_get_duration(seq->anim);
seq->len = IMB_anim_get_duration(seq->anim,
seq->strip->proxy ?
seq->strip->proxy->tc :
IMB_TC_RECORD_RUN);
seq->anim_preseek = IMB_anim_get_preseek(seq->anim);
@ -1117,7 +1132,7 @@ static int get_shown_sequences( ListBase * seqbasep, int cfra, int chanshown, Se
return cnt;
}
/* **********************************************************************
proxy management
@ -1125,48 +1140,105 @@ static int get_shown_sequences( ListBase * seqbasep, int cfra, int chanshown, Se
#define PROXY_MAXFILE (2*FILE_MAXDIR+FILE_MAXFILE)
static IMB_Proxy_Size seq_rendersize_to_proxysize(int size)
{
if (size >= 100) {
return IMB_PROXY_NONE;
}
if (size >= 99) {
return IMB_PROXY_100;
}
if (size >= 75) {
return IMB_PROXY_75;
}
if (size >= 50) {
return IMB_PROXY_50;
}
return IMB_PROXY_25;
}
static void seq_open_anim_file(Sequence * seq)
{
char name[FILE_MAXDIR+FILE_MAXFILE];
StripProxy * proxy;
if(seq->anim != NULL) {
return;
}
BLI_join_dirfile(name, sizeof(name),
seq->strip->dir, seq->strip->stripdata->name);
BLI_path_abs(name, G.main->name);
seq->anim = openanim(name, IB_rect |
((seq->flag & SEQ_FILTERY) ?
IB_animdeinterlace : 0), seq->streamindex);
if (seq->anim == NULL) {
return;
}
proxy = seq->strip->proxy;
if (proxy == NULL) {
return;
}
if (seq->flag & SEQ_USE_PROXY_CUSTOM_DIR) {
IMB_anim_set_index_dir(seq->anim, seq->strip->proxy->dir);
}
}
static int seq_proxy_get_fname(SeqRenderData context, Sequence * seq, int cfra, char * name)
{
int frameno;
char dir[FILE_MAXDIR];
int render_size = context.preview_render_size;
if (!seq->strip->proxy) {
return FALSE;
}
/* MOVIE tracks (only exception: custom files) are now handled
internally by ImBuf module for various reasons: proper time code
support, quicker index build, using one file instead
of a full directory of jpeg files, etc. Trying to support old
and new method at once could lead to funny effects, if people
have both, a directory full of jpeg files and proxy avis, so
sorry folks, please rebuild your proxies... */
if (seq->flag & (SEQ_USE_PROXY_CUSTOM_DIR|SEQ_USE_PROXY_CUSTOM_FILE)) {
strcpy(dir, seq->strip->proxy->dir);
} else if (seq->type == SEQ_IMAGE) {
snprintf(dir, PROXY_MAXFILE, "%s/BL_proxy", seq->strip->dir);
} else {
if (ELEM(seq->type, SEQ_IMAGE, SEQ_MOVIE)) {
snprintf(dir, FILE_MAXDIR, "%s/BL_proxy", seq->strip->dir);
} else {
return FALSE;
}
return FALSE;
}
if (seq->flag & SEQ_USE_PROXY_CUSTOM_FILE) {
BLI_join_dirfile(name, FILE_MAX, dir, seq->strip->proxy->file); /* XXX, not real length */
BLI_join_dirfile(name, PROXY_MAXFILE,
dir, seq->strip->proxy->file);
BLI_path_abs(name, G.main->name);
return TRUE;
}
/* dirty hack to distinguish 100% render size from PROXY_100 */
if (render_size == 99) {
render_size = 100;
}
/* generate a separate proxy directory for each preview size */
switch(seq->type) {
case SEQ_IMAGE:
if (seq->type == SEQ_IMAGE) {
snprintf(name, PROXY_MAXFILE, "%s/images/%d/%s_proxy", dir,
context.preview_render_size,
give_stripelem(seq, cfra)->name);
frameno = 1;
break;
case SEQ_MOVIE:
frameno = (int) give_stripelem_index(seq, cfra) + seq->anim_startofs;
snprintf(name, PROXY_MAXFILE, "%s/%s/%d/####", dir,
seq->strip->stripdata->name, context.preview_render_size);
break;
default:
frameno = (int) give_stripelem_index(seq, cfra) + seq->anim_startofs;
} else {
frameno = (int) give_stripelem_index(seq, cfra)
+ seq->anim_startofs;
snprintf(name, PROXY_MAXFILE, "%s/proxy_misc/%d/####", dir,
context.preview_render_size);
}
@ -1182,13 +1254,18 @@ static int seq_proxy_get_fname(SeqRenderData context, Sequence * seq, int cfra,
static struct ImBuf * seq_proxy_fetch(SeqRenderData context, Sequence * seq, int cfra)
{
char name[PROXY_MAXFILE];
IMB_Proxy_Size psize = seq_rendersize_to_proxysize(
context.preview_render_size);
int size_flags;
if (!(seq->flag & SEQ_USE_PROXY)) {
return NULL;
}
/* rendering at 100% ? No real sense in proxy-ing, right? */
if (context.preview_render_size == 100) {
size_flags = seq->strip->proxy->build_size_flags;
/* only use proxies, if they are enabled (even if present!) */
if (psize != IMB_PROXY_NONE && ((size_flags & psize) != psize)) {
return NULL;
}
@ -1199,13 +1276,19 @@ static struct ImBuf * seq_proxy_fetch(SeqRenderData context, Sequence * seq, int
return NULL;
}
seq->strip->proxy->anim = openanim(name, IB_rect);
seq->strip->proxy->anim = openanim(name, IB_rect, 0);
}
if (seq->strip->proxy->anim==NULL) {
return NULL;
}
return IMB_anim_absolute(seq->strip->proxy->anim, frameno);
seq_open_anim_file(seq);
frameno = IMB_anim_index_get_frame_index(
seq->anim, seq->strip->proxy->tc, frameno);
return IMB_anim_absolute(seq->strip->proxy->anim, frameno,
IMB_TC_NONE, IMB_PROXY_NONE);
}
if (seq_proxy_get_fname(context, seq, cfra, name) == 0) {
@ -1219,67 +1302,30 @@ static struct ImBuf * seq_proxy_fetch(SeqRenderData context, Sequence * seq, int
}
}
#if 0
static void do_build_seq_ibuf(Scene *scene, Sequence * seq, TStripElem *se, int cfra,
int build_proxy_run, int preview_render_size);
static void seq_proxy_build_frame(Scene *scene, Sequence * seq, int cfra, int preview_render_size, int seqrectx, int seqrecty)
static void seq_proxy_build_frame(SeqRenderData context,
Sequence* seq, int cfra,
int proxy_render_size)
{
char name[PROXY_MAXFILE];
int quality;
TStripElem * se;
int ok;
int rectx, recty;
int ok;
struct ImBuf * ibuf;
if (!(seq->flag & SEQ_USE_PROXY)) {
if (!seq_proxy_get_fname(context, seq, cfra, name)) {
return;
}
/* rendering at 100% ? No real sense in proxy-ing, right? */
if (preview_render_size == 100) {
return;
}
ibuf = seq_render_strip(context, seq, cfra);
/* that's why it is called custom... */
if (seq->flag & SEQ_USE_PROXY_CUSTOM_FILE) {
return;
}
if (!seq_proxy_get_fname(scene, seq, cfra, name, preview_render_size)) {
return;
}
se = give_tstripelem(seq, cfra);
if (!se) {
return;
}
if(se->ibuf) {
IMB_freeImBuf(se->ibuf);
se->ibuf = 0;
}
do_build_seq_ibuf(scene, seq, se, cfra, TRUE, preview_render_size,
seqrectx, seqrecty);
if (!se->ibuf) {
return;
}
rectx= (preview_render_size*scene->r.xsch)/100;
recty= (preview_render_size*scene->r.ysch)/100;
ibuf = se->ibuf;
rectx = (proxy_render_size * context.scene->r.xsch) / 100;
recty = (proxy_render_size * context.scene->r.ysch) / 100;
if (ibuf->x != rectx || ibuf->y != recty) {
IMB_scalefastImBuf(ibuf, (short)rectx, (short)recty);
}
/* quality is fixed, otherwise one has to generate separate
directories for every quality...
depth = 32 is intentionally left in, otherwise ALPHA channels
/* depth = 32 is intentionally left in, otherwise ALPHA channels
won't work... */
quality = seq->strip->proxy->quality;
ibuf->ftype= JPG | quality;
@ -1292,69 +1338,80 @@ static void seq_proxy_build_frame(Scene *scene, Sequence * seq, int cfra, int pr
}
IMB_freeImBuf(ibuf);
se->ibuf = 0;
}
static void seq_proxy_rebuild(Scene *scene, Sequence * seq, int seqrectx,
int seqrecty)
void seq_proxy_rebuild(struct Main * bmain, Scene *scene, Sequence * seq,
short *stop, short *do_update, float *progress)
{
SeqRenderData context;
int cfra;
float rsize = seq->strip->proxy->size;
int tc_flags;
int size_flags;
int quality;
waitcursor(1);
G.afbreek = 0;
/* flag management tries to account for strobe and
other "non-linearities", that might come in the future...
better way would be to "touch" the files, so that _really_
no one is rebuild twice.
*/
for (cfra = seq->startdisp; cfra < seq->enddisp; cfra++) {
TStripElem * tse = give_tstripelem(seq, cfra);
tse->flag &= ~STRIPELEM_PREVIEW_DONE;
if (!seq->strip || !seq->strip->proxy) {
return;
}
/* a _lot_ faster for movie files, if we read frames in
sequential order */
if (seq->flag & SEQ_REVERSE_FRAMES) {
for (cfra = seq->enddisp-seq->endstill-1;
cfra >= seq->startdisp + seq->startstill; cfra--) {
TStripElem * tse = give_tstripelem(seq, cfra);
if (!(tse->flag & STRIPELEM_PREVIEW_DONE)) {
//XXX set_timecursor(cfra);
seq_proxy_build_frame(scene, seq, cfra, rsize,
seqrectx, seqrecty);
tse->flag |= STRIPELEM_PREVIEW_DONE;
}
if (blender_test_break()) {
break;
}
}
} else {
for (cfra = seq->startdisp + seq->startstill;
cfra < seq->enddisp - seq->endstill; cfra++) {
TStripElem * tse = give_tstripelem(seq, cfra);
if (!(tse->flag & STRIPELEM_PREVIEW_DONE)) {
//XXX set_timecursor(cfra);
seq_proxy_build_frame(scene, seq, cfra, rsize,
seqrectx, seqrecty);
tse->flag |= STRIPELEM_PREVIEW_DONE;
}
if (blender_test_break()) {
break;
}
}
if (!(seq->flag & SEQ_USE_PROXY)) {
return;
}
tc_flags = seq->strip->proxy->build_tc_flags;
size_flags = seq->strip->proxy->build_size_flags;
quality = seq->strip->proxy->quality;
if (seq->type == SEQ_MOVIE) {
seq_open_anim_file(seq);
if (seq->anim) {
IMB_anim_index_rebuild(
seq->anim, tc_flags, size_flags, quality,
stop, do_update, progress);
}
return;
}
if (!(seq->flag & SEQ_USE_PROXY)) {
return;
}
/* that's why it is called custom... */
if (seq->flag & SEQ_USE_PROXY_CUSTOM_FILE) {
return;
}
/* fail safe code */
context = seq_new_render_data(
bmain, scene,
(scene->r.size * (float) scene->r.xsch) / 100.0f + 0.5f,
(scene->r.size * (float) scene->r.ysch) / 100.0f + 0.5f,
100);
for (cfra = seq->startdisp + seq->startstill;
cfra < seq->enddisp - seq->endstill; cfra++) {
if (size_flags & IMB_PROXY_25) {
seq_proxy_build_frame(context, seq, cfra, 25);
}
if (size_flags & IMB_PROXY_50) {
seq_proxy_build_frame(context, seq, cfra, 50);
}
if (size_flags & IMB_PROXY_75) {
seq_proxy_build_frame(context, seq, cfra, 75);
}
if (size_flags & IMB_PROXY_100) {
seq_proxy_build_frame(context, seq, cfra, 100);
}
*progress= (float)cfra/(seq->enddisp - seq->endstill
- seq->startdisp + seq->startstill);
*do_update= 1;
if(*stop || G.afbreek)
break;
}
waitcursor(0);
}
#endif
/* **********************************************************************
@ -1571,6 +1628,8 @@ static ImBuf * input_preprocess(
{
float mul;
ibuf = IMB_makeSingleUser(ibuf);
if((seq->flag & SEQ_FILTERY) && seq->type != SEQ_MOVIE) {
IMB_filtery(ibuf);
}
@ -2096,17 +2155,20 @@ static ImBuf * seq_render_strip(SeqRenderData context, Sequence * seq, float cfr
}
case SEQ_MOVIE:
{
if(seq->anim==NULL) {
BLI_join_dirfile(name, sizeof(name), seq->strip->dir, seq->strip->stripdata->name);
BLI_path_abs(name, G.main->name);
seq->anim = openanim(name, IB_rect |
((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0));
}
seq_open_anim_file(seq);
if(seq->anim) {
IMB_anim_set_preseek(seq->anim, seq->anim_preseek);
ibuf = IMB_anim_absolute(seq->anim, nr + seq->anim_startofs);
IMB_anim_set_preseek(seq->anim,
seq->anim_preseek);
ibuf = IMB_anim_absolute(
seq->anim, nr + seq->anim_startofs,
seq->strip->proxy ?
seq->strip->proxy->tc
: IMB_TC_RECORD_RUN,
seq_rendersize_to_proxysize(
context.preview_render_size));
/* we don't need both (speed reasons)! */
if (ibuf && ibuf->rect_float && ibuf->rect)
imb_freerectImBuf(ibuf);
@ -3584,7 +3646,7 @@ Sequence *sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoadInfo
BLI_strncpy(path, seq_load->path, sizeof(path));
BLI_path_abs(path, G.main->name);
an = openanim(path, IB_rect);
an = openanim(path, IB_rect, 0);
if(an==NULL)
return NULL;
@ -3600,7 +3662,7 @@ Sequence *sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoadInfo
/* basic defaults */
seq->strip= strip= MEM_callocN(sizeof(Strip), "strip");
strip->len = seq->len = IMB_anim_get_duration( an );
strip->len = seq->len = IMB_anim_get_duration( an, IMB_TC_RECORD_RUN );
strip->us= 1;
/* we only need 1 element for MOVIE strips */

@ -9951,12 +9951,6 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
if(ed) {
SEQP_BEGIN(ed, seq) {
if (seq->strip && seq->strip->proxy){
if (sce->r.size != 100.0) {
seq->strip->proxy->size
= sce->r.size;
} else {
seq->strip->proxy->size = 25;
}
seq->strip->proxy->quality =90;
}
}

@ -2369,6 +2369,7 @@ void uiTemplateOperatorSearch(uiLayout *layout)
#define B_STOPCAST 2
#define B_STOPANIM 3
#define B_STOPCOMPO 4
#define B_STOPSEQ 5
static void do_running_jobs(bContext *C, void *UNUSED(arg), int event)
{
@ -2385,6 +2386,9 @@ static void do_running_jobs(bContext *C, void *UNUSED(arg), int event)
case B_STOPCOMPO:
WM_jobs_stop(CTX_wm_manager(C), CTX_wm_area(C), NULL);
break;
case B_STOPSEQ:
WM_jobs_stop(CTX_wm_manager(C), CTX_wm_area(C), NULL);
break;
}
}
@ -2406,8 +2410,11 @@ void uiTemplateRunningJobs(uiLayout *layout, bContext *C)
if(WM_jobs_test(wm, sa))
owner = sa;
handle_event= B_STOPCOMPO;
}
else {
} else if (sa->spacetype==SPACE_SEQ) {
if(WM_jobs_test(wm, sa))
owner = sa;
handle_event = B_STOPSEQ;
} else {
Scene *scene;
/* another scene can be rendering too, for example via compositor */
for(scene= CTX_data_main(C)->scene.first; scene; scene= scene->id.next)

@ -113,7 +113,7 @@ static void image_info(Scene *scene, ImageUser *iuser, Image *ima, ImBuf *ibuf,
if(ima->source==IMA_SRC_MOVIE) {
ofs+= sprintf(str, "Movie");
if(ima->anim)
ofs+= sprintf(str+ofs, "%d frs", IMB_anim_get_duration(ima->anim));
ofs+= sprintf(str+ofs, "%d frs", IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN));
}
else
ofs+= sprintf(str, "Image");
@ -428,7 +428,7 @@ static void set_frames_cb(bContext *C, void *ima_v, void *iuser_v)
ImageUser *iuser= iuser_v;
if(ima->anim) {
iuser->frames = IMB_anim_get_duration(ima->anim);
iuser->frames = IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN);
BKE_image_user_calc_frame(iuser, scene->r.cfra, 0);
}
}

@ -8,4 +8,7 @@ incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include'
incs += ' ../../makesrna ../../blenloader'
incs += ' #/intern/audaspace/intern'
if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'):
incs += ' ' + env['BF_PTHREADS_INC']
env.BlenderLib ( 'bf_editors_space_sequencer', sources, Split(incs), [], libtype=['core'], priority=[100] )

@ -47,6 +47,7 @@
#include "BLI_math.h"
#include "BLI_storage_types.h"
#include "BLI_utildefines.h"
#include "BLI_threads.h"
#include "DNA_scene_types.h"
#include "DNA_userdef_types.h"
@ -125,6 +126,111 @@ typedef struct TransSeq {
int len;
} TransSeq;
/* ********************************************************************** */
/* ***************** proxy job manager ********************** */
typedef struct ProxyBuildJob {
Scene *scene;
struct Main * main;
ListBase queue;
ThreadMutex queue_lock;
} ProxyJob;
static void proxy_freejob(void *pjv)
{
ProxyJob *pj= pjv;
Sequence * seq;
for (seq = pj->queue.first; seq; seq = seq->next) {
BLI_remlink(&pj->queue, seq);
seq_free_sequence_recurse(pj->scene, seq);
}
BLI_mutex_end(&pj->queue_lock);
MEM_freeN(pj);
}
/* only this runs inside thread */
static void proxy_startjob(void *pjv, short *stop, short *do_update, float *progress)
{
ProxyJob *pj = pjv;
while (!*stop) {
Sequence * seq;
BLI_mutex_lock(&pj->queue_lock);
if (!pj->queue.first) {
BLI_mutex_unlock(&pj->queue_lock);
break;
}
seq = pj->queue.first;
BLI_remlink(&pj->queue, seq);
BLI_mutex_unlock(&pj->queue_lock);
seq_proxy_rebuild(pj->main, pj->scene, seq,
stop, do_update, progress);
seq_free_sequence_recurse(pj->scene, seq);
}
if (*stop) {
fprintf(stderr,
"Canceling proxy rebuild on users request...\n");
}
}
static void proxy_endjob(void *UNUSED(customdata))
{
}
void seq_proxy_build_job(const bContext *C, Sequence * seq)
{
wmJob * steve;
ProxyJob *pj;
Scene *scene= CTX_data_scene(C);
ScrArea * sa= CTX_wm_area(C);
seq = seq_dupli_recursive(scene, scene, seq, 0);
steve = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C),
sa, "Building Proxies", WM_JOB_PROGRESS);
pj = WM_jobs_get_customdata(steve);
if (!pj) {
pj = MEM_callocN(sizeof(ProxyJob), "proxy rebuild job");
pj->scene= scene;
pj->main = CTX_data_main(C);
BLI_mutex_init(&pj->queue_lock);
WM_jobs_customdata(steve, pj, proxy_freejob);
WM_jobs_timer(steve, 0.1, NC_SCENE|ND_SEQUENCER,
NC_SCENE|ND_SEQUENCER);
WM_jobs_callbacks(steve, proxy_startjob, NULL, NULL,
proxy_endjob);
}
BLI_mutex_lock(&pj->queue_lock);
BLI_addtail(&pj->queue, seq);
BLI_mutex_unlock(&pj->queue_lock);
if (!WM_jobs_is_running(steve)) {
G.afbreek = 0;
WM_jobs_start(CTX_wm_manager(C), steve);
}
ED_area_tag_redraw(CTX_wm_area(C));
}
/* ********************************************************************** */
void seq_rectf(Sequence *seq, rctf *rectf)
{
if(seq->startstill) rectf->xmin= seq->start;
@ -2690,6 +2796,37 @@ void SEQUENCER_OT_view_ghost_border(wmOperatorType *ot)
WM_operator_properties_gesture_border(ot, FALSE);
}
/* rebuild_proxy operator */
static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
Editing *ed = seq_give_editing(scene, FALSE);
Sequence * seq;
SEQP_BEGIN(ed, seq) {
if ((seq->flag & SELECT)) {
seq_proxy_build_job(C, seq);
}
}
SEQ_END
return OPERATOR_FINISHED;
}
void SEQUENCER_OT_rebuild_proxy(wmOperatorType *ot)
{
/* identifiers */
ot->name= "Rebuild Proxy and Timecode Indices";
ot->idname= "SEQUENCER_OT_rebuild_proxy";
ot->description="Rebuild all selected proxies and timecode indeces using the job system";
/* api callbacks */
ot->exec= sequencer_rebuild_proxy_exec;
ot->poll= ED_operator_sequencer_active;
/* flags */
ot->flag= OPTYPE_REGISTER;
}
/* change ops */

@ -118,6 +118,8 @@ void SEQUENCER_OT_change_path(struct wmOperatorType *ot);
void SEQUENCER_OT_copy(struct wmOperatorType *ot);
void SEQUENCER_OT_paste(struct wmOperatorType *ot);
void SEQUENCER_OT_rebuild_proxy(struct wmOperatorType *ot);
/* preview specific operators */
void SEQUENCER_OT_view_all_preview(struct wmOperatorType *ot);

@ -87,6 +87,7 @@ void sequencer_operatortypes(void)
WM_operatortype_append(SEQUENCER_OT_view_zoom_ratio);
WM_operatortype_append(SEQUENCER_OT_view_ghost_border);
WM_operatortype_append(SEQUENCER_OT_rebuild_proxy);
WM_operatortype_append(SEQUENCER_OT_change_effect_input);
WM_operatortype_append(SEQUENCER_OT_change_effect_type);
WM_operatortype_append(SEQUENCER_OT_change_path);

@ -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

@ -133,6 +133,7 @@ struct ImBuf *IMB_allocImBuf(unsigned int x, unsigned int y,
*/
void IMB_refImBuf(struct ImBuf *ibuf);
struct ImBuf * IMB_makeSingleUser(struct ImBuf *ibuf);
/**
*
@ -192,18 +193,71 @@ void IMB_rectcpy(struct ImBuf *drect, struct ImBuf *srect, int destx,
void IMB_rectblend(struct ImBuf *dbuf, struct ImBuf *sbuf, int destx,
int desty, int srcx, int srcy, int width, int height, IMB_BlendMode mode);
/**
*
* @attention Defined in indexer.c
*/
typedef enum IMB_Timecode_Type {
IMB_TC_NONE = 0, /* don't use timecode files at all */
IMB_TC_RECORD_RUN = 1, /* use images in the order as they are recorded
(currently, this is the only one implemented
and is a sane default)
*/
IMB_TC_FREE_RUN = 2, /* use global timestamp written by recording
device (prosumer camcorders e.g. can do
that) */
IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN = 4,
/* interpolate a global timestamp using the
record date and time written by recording
device (*every* consumer camcorder can do
that :) )*/
IMB_TC_MAX_SLOT = 3
} IMB_Timecode_Type;
typedef enum IMB_Proxy_Size {
IMB_PROXY_NONE = 0,
IMB_PROXY_25 = 1,
IMB_PROXY_50 = 2,
IMB_PROXY_75 = 4,
IMB_PROXY_100 = 8,
IMB_PROXY_MAX_SLOT = 4
} IMB_Proxy_Size;
/* defaults to BL_proxy within the directory of the animation */
void IMB_anim_set_index_dir(struct anim * anim, const char * dir);
int IMB_anim_index_get_frame_index(struct anim * anim, IMB_Timecode_Type tc,
int position);
/* will rebuild all used indices and proxies at once */
void IMB_anim_index_rebuild(struct anim * anim,
IMB_Timecode_Type build_tcs,
IMB_Proxy_Size build_preview_sizes,
int build_quality,
short *stop, short *do_update, float *progress);
/**
* Return the length (in frames) of the given @a anim.
*/
int IMB_anim_get_duration(struct anim *anim);
int IMB_anim_get_duration(struct anim *anim, IMB_Timecode_Type tc);
/**
* Return the fps contained in movie files (function rval is FALSE,
* and frs_sec and frs_sec_base untouched if none available!)
*/
int IMB_anim_get_fps(struct anim * anim,
short * frs_sec, float * frs_sec_base);
/**
*
* @attention Defined in anim.c
*/
struct anim *IMB_open_anim(const char *name, int ib_flags);
struct anim *IMB_open_anim(const char *name, int ib_flags, int streamindex);
void IMB_close_anim(struct anim *anim);
/**
*
* @attention Defined in anim.c
@ -218,7 +272,10 @@ int IMB_anim_get_preseek(struct anim *anim);
* @attention Defined in anim.c
*/
struct ImBuf *IMB_anim_absolute(struct anim *anim, int position);
struct ImBuf *IMB_anim_absolute(
struct anim *anim, int position,
IMB_Timecode_Type tc /* = 1 = IMB_TC_RECORD_RUN */,
IMB_Proxy_Size preview_size /* = 0 = IMB_PROXY_NONE */);
/**
*
@ -227,12 +284,6 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, int position);
*/
struct ImBuf *IMB_anim_previewframe(struct anim *anim);
/**
*
* @attention Defined in anim.c
*/
void IMB_free_anim_ibuf(struct anim *anim);
/**
*
* @attention Defined in anim.c

@ -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

@ -0,0 +1,135 @@
/**
* IMB_indexer.h
*
* $Id: IMB_indexer.h
*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
* Contributor(s): Peter Schlaile
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef IMB_INDEXER_H
#define IMB_INDEXER_H
#ifdef WIN32
#include <io.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include "BKE_utildefines.h"
#include "IMB_anim.h"
/*
seperate animation index files to solve the following problems:
a) different timecodes within one file (like DTS/PTS, Timecode-Track,
"implicit" timecodes within DV-files and HDV-files etc.)
b) seeking difficulties within ffmpeg for files with timestamp holes
c) broken files that miss several frames / have varying framerates
d) use proxies accordingly
... we need index files, that provide us with
the binary(!) position, where we have to seek into the file *and*
the continuous frame number (ignoring the holes) starting from the
beginning of the file, so that we know, which proxy frame to serve.
This index has to be only built once for a file and is written into
the BL_proxy directory structure for later reuse in different blender files.
*/
typedef struct anim_index_entry {
int frameno;
unsigned long long seek_pos;
unsigned long long seek_pos_dts;
unsigned long long pts;
} anim_index_entry;
struct anim_index {
char name[256];
int num_entries;
struct anim_index_entry * entries;
};
struct anim_index_builder;
typedef struct anim_index_builder {
FILE * fp;
char name[FILE_MAXDIR + FILE_MAXFILE];
char temp_name[FILE_MAXDIR + FILE_MAXFILE];
void * private_data;
void (*delete_priv_data)(struct anim_index_builder * idx);
void (*proc_frame)(struct anim_index_builder * idx,
unsigned char * buffer,
int data_size,
struct anim_index_entry * entry);
} anim_index_builder;
anim_index_builder * IMB_index_builder_create(const char * name);
void IMB_index_builder_add_entry(anim_index_builder * fp,
int frameno, unsigned long long seek_pos,
unsigned long long seek_pos_dts,
unsigned long long pts);
void IMB_index_builder_proc_frame(anim_index_builder * fp,
unsigned char * buffer,
int data_size,
int frameno, unsigned long long seek_pos,
unsigned long long seek_pos_dts,
unsigned long long pts);
void IMB_index_builder_finish(anim_index_builder * fp, int rollback);
struct anim_index * IMB_indexer_open(const char * name);
unsigned long long IMB_indexer_get_seek_pos(
struct anim_index * idx, int frameno_index);
unsigned long long IMB_indexer_get_seek_pos_dts(
struct anim_index * idx, int frameno_index);
int IMB_indexer_get_frame_index(struct anim_index * idx, int frameno);
unsigned long long IMB_indexer_get_pts(struct anim_index * idx,
int frame_index);
int IMB_indexer_get_duration(struct anim_index * idx);
int IMB_indexer_can_scan(struct anim_index * idx,
int old_frame_index, int new_frame_index);
void IMB_indexer_close(struct anim_index * idx);
void IMB_free_indices(struct anim * anim);
int IMB_anim_index_get_frame_index(
struct anim * anim, IMB_Timecode_Type tc, int position);
struct anim * IMB_anim_open_proxy(
struct anim * anim, IMB_Proxy_Size preview_size);
struct anim_index * IMB_anim_open_index(
struct anim * anim, IMB_Timecode_Type tc);
int IMB_proxy_size_to_array_index(IMB_Proxy_Size pr_size);
int IMB_timecode_to_array_index(IMB_Timecode_Type tc);
#endif

@ -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;

@ -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;
int64_t pts_to_search = 0;
int pos_found = 1;
int filter_y = 0;
int seek_by_bytes= 0;
int preseek_count = 0;
/* decode one video frame and load the next packet into anim->packet,
so that we can obtain next_pts and next undecoded pts */
if (anim == 0) return (0);
static int ffmpeg_decode_video_frame(struct anim * anim)
{
int frameFinished = 0;
int rval = 0;
ibuf = IMB_allocImBuf(anim->x, anim->y, 32, IB_rect);
av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE VIDEO FRAME\n");
avpicture_fill((AVPicture*) anim->pFrameRGB,
(unsigned char*) ibuf->rect,
PIX_FMT_RGBA, anim->x, anim->y);
anim->next_undecoded_pts = -1;
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 (anim->next_packet.stream_index == anim->videoStream) {
av_log(anim->pFormatCtx, AV_LOG_DEBUG,
" DECODE: cached next packet\n");
if (frameFinished) {
anim->curposition++;
}
}
av_free_packet(&packet);
if (position == anim->curposition+1) {
break;
}
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;
}
}
/* 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
return FALSE;
}
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);
static ImBuf * ffmpeg_fetchibuf(struct anim * anim, int position,
IMB_Timecode_Type tc) {
int64_t pts_to_search = 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);
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: pos=%d\n", position);
if (tc != IMB_TC_NONE) {
tc_index = IMB_anim_open_index(anim, tc);
}
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;
}
}
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");
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 *= anim->pFormatCtx->bit_rate / frame_rate;
pos /= 8;
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);
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);
}
ret = av_seek_frame(anim->pFormatCtx, -1,
pos,
AVSEEK_FLAG_BACKWARD | (
seek_by_bytes
? AVSEEK_FLAG_ANY
| AVSEEK_FLAG_BYTE : 0));
if (ret < 0) {
fprintf(stderr, "error while seeking: %d\n", ret);
av_log(anim->pFormatCtx, AV_LOG_ERROR,
"FETCH: "
"error while seeking to DTS = %lld "
"(frameno = %d, PTS = %lld): errcode = %d\n",
pos, position, pts_to_search, ret);
}
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;
}
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 (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;
}
}
}
if(frameFinished && pos_found == 1) {
ffmpeg_postprocess(anim, ibuf, &filter_y);
av_free_packet(&packet);
break;
}
if (anim->next_packet.stream_index == anim->videoStream) {
av_free_packet(&anim->next_packet);
anim->next_packet.stream_index = -1;
}
av_free_packet(&packet);
/* memset(anim->pFrame,...) ?? */
if (ret >= 0) {
ffmpeg_decode_video_frame_scan(anim, pts_to_search);
}
} 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)

@ -1,4 +1,6 @@
/*
*
* $Id$
*
* ***** BEGIN GPL LICENSE BLOCK *****
*

File diff suppressed because it is too large Load Diff

@ -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;
}

@ -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 {

@ -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);
}
}
}

@ -71,12 +71,19 @@ typedef struct StripColorBalance {
} StripColorBalance;
typedef struct StripProxy {
char dir[160];
char file[80];
struct anim *anim;
short size;
short quality;
int pad;
char dir[160]; // custom directory for index and proxy files
// (defaults to BL_proxy)
char file[80]; // custom file
struct anim *anim; // custom proxy anim file
short tc; // time code in use
short quality; // proxy build quality
short build_size_flags;// size flags (see below) of all proxies
// to build
short build_tc_flags; // time code flags (see below) of all tc indices
// to build
} StripProxy;
typedef struct Strip {
@ -128,11 +135,12 @@ typedef struct Sequence {
int startstill, endstill;
int machine, depth; /*machine - the strip channel, depth - the depth in the sequence when dealing with metastrips */
int startdisp, enddisp; /*starting and ending points in the sequence*/
float sat, pad;
float sat;
float mul, handsize;
/* is sfra needed anymore? - it looks like its only used in one place */
int sfra; /* starting frame according to the timeline of the scene. */
int anim_preseek;
int streamindex; /* streamindex for movie or sound files with several streams */
Strip *strip;
@ -283,6 +291,19 @@ typedef struct SpeedControlVars {
#define SEQ_COLOR_BALANCE_INVERSE_GAMMA 2
#define SEQ_COLOR_BALANCE_INVERSE_LIFT 4
/* !!! has to be same as IMB_imbuf.h IMB_PROXY_... and IMB_TC_... */
#define SEQ_PROXY_IMAGE_SIZE_25 1
#define SEQ_PROXY_IMAGE_SIZE_50 2
#define SEQ_PROXY_IMAGE_SIZE_75 4
#define SEQ_PROXY_IMAGE_SIZE_100 8
#define SEQ_PROXY_TC_NONE 0
#define SEQ_PROXY_TC_RECORD_RUN 1
#define SEQ_PROXY_TC_FREE_RUN 2
#define SEQ_PROXY_TC_INTERP_REC_DATE_FREE_RUN 4
#define SEQ_PROXY_TC_ALL 7
/* seq->type WATCH IT: SEQ_EFFECT BIT is used to determine if this is an effect strip!!! */
#define SEQ_IMAGE 0
#define SEQ_META 1

@ -930,6 +930,7 @@ enum {
#define SEQ_PROXY_RENDER_SIZE_25 25
#define SEQ_PROXY_RENDER_SIZE_50 50
#define SEQ_PROXY_RENDER_SIZE_75 75
#define SEQ_PROXY_RENDER_SIZE_100 99
#define SEQ_PROXY_RENDER_SIZE_FULL 100

@ -245,6 +245,10 @@ static void rna_Sequence_use_proxy_set(PointerRNA *ptr, int value)
seq->flag |= SEQ_USE_PROXY;
if(seq->strip->proxy == NULL) {
seq->strip->proxy = MEM_callocN(sizeof(struct StripProxy), "StripProxy");
seq->strip->proxy->quality = 90;
seq->strip->proxy->build_tc_flags = SEQ_PROXY_TC_ALL;
seq->strip->proxy->build_size_flags
= SEQ_PROXY_IMAGE_SIZE_25;
}
} else {
seq->flag ^= SEQ_USE_PROXY;
@ -587,6 +591,34 @@ static void rna_Sequence_filepath_update(Main *bmain, Scene *scene, PointerRNA *
rna_Sequence_update(bmain, scene, ptr);
}
static int seqproxy_seq_cmp_cb(Sequence *seq, void *arg_pt)
{
struct { Sequence *seq; void *seq_proxy; } *data= arg_pt;
if(seq->strip && seq->strip->proxy == data->seq_proxy) {
data->seq= seq;
return -1; /* done so bail out */
}
return 1;
}
static void rna_Sequence_tcindex_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
Editing *ed= seq_give_editing(scene, FALSE);
Sequence *seq;
struct { Sequence *seq; void *seq_proxy; } data;
data.seq= NULL;
data.seq_proxy = ptr->data;
seqbase_recursive_apply(&ed->seqbase, seqproxy_seq_cmp_cb, &data);
seq= data.seq;
reload_sequence_new_file(scene, seq, FALSE);
rna_Sequence_frame_change_update(scene, seq);
}
/* do_versions? */
static float rna_Sequence_opacity_get(PointerRNA *ptr)
{
@ -789,6 +821,19 @@ static void rna_def_strip_proxy(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
static const EnumPropertyItem seq_tc_items[]= {
{SEQ_PROXY_TC_NONE, "NONE", 0, "No TC in use", ""},
{SEQ_PROXY_TC_RECORD_RUN, "RECORD_RUN", 0, "Record Run",
"use images in the order as they are recorded"},
{SEQ_PROXY_TC_FREE_RUN, "FREE_RUN", 0, "Free Run",
"use global timestamp written by recording device"},
{SEQ_PROXY_TC_INTERP_REC_DATE_FREE_RUN, "FREE_RUN_REC_DATE",
0, "Free Run (rec date)",
"interpolate a global timestamp using the "
"record date and time written by recording "
"device"},
{0, NULL, 0, NULL, NULL}};
srna = RNA_def_struct(brna, "SequenceProxy", NULL);
RNA_def_struct_ui_text(srna, "Sequence Proxy", "Proxy parameters for a sequence strip");
@ -804,6 +849,46 @@ static void rna_def_strip_proxy(BlenderRNA *brna)
RNA_def_property_string_funcs(prop, "rna_Sequence_proxy_filepath_get", "rna_Sequence_proxy_filepath_length", "rna_Sequence_proxy_filepath_set");
RNA_def_property_update(prop, NC_SCENE|ND_SEQUENCER, "rna_Sequence_update");
prop= RNA_def_property(srna, "build_25", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "build_size_flags", SEQ_PROXY_IMAGE_SIZE_25);
RNA_def_property_ui_text(prop, "25%", "Build 25% proxy resolution");
prop= RNA_def_property(srna, "build_50", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "build_size_flags", SEQ_PROXY_IMAGE_SIZE_50);
RNA_def_property_ui_text(prop, "50%", "Build 50% proxy resolution");
prop= RNA_def_property(srna, "build_75", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "build_size_flags", SEQ_PROXY_IMAGE_SIZE_75);
RNA_def_property_ui_text(prop, "75%", "Build 75% proxy resolution");
prop= RNA_def_property(srna, "build_100", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "build_size_flags", SEQ_PROXY_IMAGE_SIZE_100);
RNA_def_property_ui_text(prop, "100%", "Build 100% proxy resolution");
prop= RNA_def_property(srna, "build_record_run", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "build_tc_flags", SEQ_PROXY_TC_RECORD_RUN);
RNA_def_property_ui_text(prop, "Rec Run", "Build record run time code index");
prop= RNA_def_property(srna, "build_free_run", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "build_tc_flags", SEQ_PROXY_TC_FREE_RUN);
RNA_def_property_ui_text(prop, "Free Run", "Build free run time code index");
prop= RNA_def_property(srna, "build_free_run_rec_date", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "build_tc_flags", SEQ_PROXY_TC_INTERP_REC_DATE_FREE_RUN);
RNA_def_property_ui_text(prop, "Free Run (Rec Date)", "Build free run time code index using Record Date/Time");
prop= RNA_def_property(srna, "quality", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "quality");
RNA_def_property_ui_text(prop, "Quality", "JPEG Quality of proxies to build");
RNA_def_property_ui_range(prop, 1, 100, 1, 0);
prop= RNA_def_property(srna, "timecode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "tc");
RNA_def_property_enum_items(prop, seq_tc_items);
RNA_def_property_ui_text(prop, "Timecode", "");
RNA_def_property_update(prop, NC_SCENE|ND_SEQUENCER, "rna_Sequence_tcindex_update");
}
static void rna_def_strip_color_balance(BlenderRNA *brna)
@ -1208,7 +1293,7 @@ static void rna_def_proxy(StructRNA *srna)
prop= RNA_def_property(srna, "use_proxy", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_PROXY);
RNA_def_property_ui_text(prop, "Use Proxy", "Use a preview proxy for this strip");
RNA_def_property_ui_text(prop, "Use Proxy / Timecode", "Use a preview proxy and/or timecode index for this strip");
RNA_def_property_boolean_funcs(prop, NULL, "rna_Sequence_use_proxy_set");
prop= RNA_def_property(srna, "proxy", PROP_POINTER, PROP_NONE);
@ -1330,6 +1415,12 @@ static void rna_def_movie(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "MPEG Preseek", "For MPEG movies, preseek this many frames");
RNA_def_property_update(prop, NC_SCENE|ND_SEQUENCER, "rna_Sequence_update");
prop= RNA_def_property(srna, "streamindex", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "streamindex");
RNA_def_property_range(prop, 0, 20);
RNA_def_property_ui_text(prop, "Streamindex", "For files with several movie streams, use the stream with the given index");
RNA_def_property_update(prop, NC_SCENE|ND_SEQUENCER, "rna_Sequence_update_reopen_files");
prop= RNA_def_property(srna, "elements", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "strip->stripdata", NULL);
RNA_def_property_struct_type(prop, "SequenceElement");

@ -1674,6 +1674,7 @@ static void rna_def_space_sequencer(BlenderRNA *brna)
{SEQ_PROXY_RENDER_SIZE_25, "PROXY_25", 0, "Proxy size 25%", ""},
{SEQ_PROXY_RENDER_SIZE_50, "PROXY_50", 0, "Proxy size 50%", ""},
{SEQ_PROXY_RENDER_SIZE_75, "PROXY_75", 0, "Proxy size 75%", ""},
{SEQ_PROXY_RENDER_SIZE_100, "PROXY_100", 0, "Proxy size 100%", ""},
{SEQ_PROXY_RENDER_SIZE_FULL, "FULL", 0, "No proxy, full render", ""},
{0, NULL, 0, NULL, NULL}};

@ -300,6 +300,8 @@ int WM_jobs_test(struct wmWindowManager *wm, void *owner);
float WM_jobs_progress(struct wmWindowManager *wm, void *owner);
char *WM_jobs_name(struct wmWindowManager *wm, void *owner);
int WM_jobs_is_running(struct wmJob *);
void* WM_jobs_get_customdata(struct wmJob *);
void WM_jobs_customdata(struct wmJob *, void *customdata, void (*free)(void *));
void WM_jobs_timer(struct wmJob *, double timestep, unsigned int note, unsigned int endnote);
void WM_jobs_callbacks(struct wmJob *,

@ -202,6 +202,20 @@ char *WM_jobs_name(wmWindowManager *wm, void *owner)
return NULL;
}
int WM_jobs_is_running(wmJob *steve)
{
return steve->running;
}
void* WM_jobs_get_customdata(wmJob * steve)
{
if (!steve->customdata) {
return steve->run_customdata;
} else {
return steve->customdata;
}
}
void WM_jobs_customdata(wmJob *steve, void *customdata, void (*free)(void *))
{
/* pending job? just free */