diff options
Diffstat (limited to 'source/blender/imbuf/intern/anim_movie.c')
-rw-r--r-- | source/blender/imbuf/intern/anim_movie.c | 568 |
1 files changed, 430 insertions, 138 deletions
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 919b0eb0c29..c4fe1523e90 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -57,6 +57,7 @@ #include <ctype.h> #include <stdlib.h> #include <stdio.h> +#include <math.h> #ifndef _WIN32 #include <dirent.h> #else @@ -66,6 +67,7 @@ #include "BLI_blenlib.h" /* BLI_remlink BLI_filesize BLI_addtail BLI_countlist BLI_stringdec */ #include "BLI_utildefines.h" +#include "BLI_math_base.h" #include "MEM_guardedalloc.h" @@ -90,6 +92,7 @@ #include "IMB_allocimbuf.h" #include "IMB_anim.h" +#include "IMB_indexer.h" #ifdef WITH_FFMPEG #include <libavformat/avformat.h> @@ -304,15 +307,6 @@ static void free_anim_avi (struct anim *anim) { anim->duration = 0; } -void IMB_free_anim_ibuf(struct anim * anim) { - if (anim == NULL) return; - - if (anim->ibuf1) IMB_freeImBuf(anim->ibuf1); - if (anim->ibuf2) IMB_freeImBuf(anim->ibuf2); - - anim->ibuf1 = anim->ibuf2 = NULL; -} - #ifdef WITH_FFMPEG static void free_anim_ffmpeg(struct anim * anim); #endif @@ -326,7 +320,6 @@ void IMB_free_anim(struct anim * anim) { return; } - IMB_free_anim_ibuf(anim); free_anim_movie(anim); free_anim_avi(anim); @@ -339,6 +332,7 @@ void IMB_free_anim(struct anim * anim) { #ifdef WITH_REDCODE free_anim_redcode(anim); #endif + IMB_free_indices(anim); MEM_freeN(anim); } @@ -350,13 +344,14 @@ void IMB_close_anim(struct anim * anim) { } -struct anim * IMB_open_anim( const char * name, int ib_flags) { +struct anim * IMB_open_anim( const char * name, int ib_flags, int streamindex) { struct anim * anim; anim = (struct anim*)MEM_callocN(sizeof(struct anim), "anim struct"); if (anim != NULL) { - strcpy(anim->name, name); /* fixme: possible buffer overflow here? */ + BLI_strncpy(anim->name, name, sizeof(anim->name)); anim->ib_flags = ib_flags; + anim->streamindex = streamindex; } return(anim); } @@ -368,10 +363,13 @@ static int startavi (struct anim *anim) { #if defined(_WIN32) && !defined(FREE_WINDOWS) HRESULT hr; int i, firstvideo = -1; + int streamcount; BYTE abFormat[1024]; LONG l; LPBITMAPINFOHEADER lpbi; AVISTREAMINFO avis; + + streamcount = anim->streamindex; #endif anim->avi = MEM_callocN (sizeof(AviMovie),"animavi"); @@ -396,6 +394,10 @@ static int startavi (struct anim *anim) { AVIStreamInfo(anim->pavi[i], &avis, sizeof(avis)); if ((avis.fccType == streamtypeVIDEO) && (firstvideo == -1)) { + if (streamcount > 0) { + streamcount--; + continue; + } anim->pgf = AVIStreamGetFrameOpen(anim->pavi[i], NULL); if (anim->pgf) { firstvideo = i; @@ -496,14 +498,14 @@ static ImBuf * avi_fetchibuf (struct anim *anim, int position) { for (y=0; y < anim->y; y++) { memcpy (&(ibuf->rect)[((anim->y-y)-1)*anim->x], &tmp[y*anim->x], - anim->x * 4); + anim->x * 4); } MEM_freeN (tmp); } - + ibuf->profile = IB_PROFILE_SRGB; - + return ibuf; } @@ -517,6 +519,9 @@ static int startffmpeg(struct anim * anim) { AVCodec *pCodec; AVFormatContext *pFormatCtx; AVCodecContext *pCodecCtx; + int frs_num; + double frs_den; + int streamcount; #ifdef FFMPEG_SWSCALE_COLOR_SPACE_SUPPORT /* The following for color space determination */ @@ -527,6 +532,8 @@ static int startffmpeg(struct anim * anim) { if (anim == 0) return(-1); + streamcount = anim->streamindex; + do_init_ffmpeg(); if(av_open_input_file(&pFormatCtx, anim->name, NULL, 0, NULL)!=0) { @@ -541,12 +548,17 @@ static int startffmpeg(struct anim * anim) { av_dump_format(pFormatCtx, 0, anim->name, 0); - /* Find the first video stream */ - videoStream=-1; - for(i=0; i<pFormatCtx->nb_streams; i++) - if(pFormatCtx->streams[i]->codec->codec_type + /* Find the video stream */ + videoStream = -1; + + for(i = 0; i < pFormatCtx->nb_streams; i++) + if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { - videoStream=i; + if (streamcount > 0) { + streamcount--; + continue; + } + videoStream = i; break; } @@ -557,16 +569,16 @@ static int startffmpeg(struct anim * anim) { pCodecCtx = pFormatCtx->streams[videoStream]->codec; - /* Find the decoder for the video stream */ - pCodec=avcodec_find_decoder(pCodecCtx->codec_id); - if(pCodec==NULL) { + /* Find the decoder for the video stream */ + pCodec = avcodec_find_decoder(pCodecCtx->codec_id); + if(pCodec == NULL) { av_close_input_file(pFormatCtx); return -1; } pCodecCtx->workaround_bugs = 1; - if(avcodec_open(pCodecCtx, pCodec)<0) { + if(avcodec_open(pCodecCtx, pCodec) < 0) { av_close_input_file(pFormatCtx); return -1; } @@ -575,6 +587,19 @@ static int startffmpeg(struct anim * anim) { * av_q2d(pFormatCtx->streams[videoStream]->r_frame_rate) / AV_TIME_BASE); + frs_num = pFormatCtx->streams[videoStream]->r_frame_rate.num; + frs_den = pFormatCtx->streams[videoStream]->r_frame_rate.den; + + frs_den *= AV_TIME_BASE; + + while (frs_num % 10 == 0 && frs_den >= 2.0 && frs_num > 10) { + frs_num /= 10; + frs_den /= 10; + } + + anim->frs_sec = frs_num; + anim->frs_sec_base = frs_den; + anim->params = 0; anim->x = pCodecCtx->width; @@ -584,6 +609,11 @@ static int startffmpeg(struct anim * anim) { anim->framesize = anim->x * anim->y * 4; anim->curposition = -1; + anim->last_frame = 0; + anim->last_pts = -1; + anim->next_pts = -1; + anim->next_undecoded_pts = -1; + anim->next_packet.stream_index = -1; anim->pFormatCtx = pFormatCtx; anim->pCodecCtx = pCodecCtx; @@ -666,10 +696,19 @@ static int startffmpeg(struct anim * anim) { return (0); } -static void ffmpeg_postprocess(struct anim * anim, ImBuf * ibuf, - int * filter_y) +/* postprocess the image in anim->pFrame and do color conversion + and deinterlacing stuff. + + Output is anim->last_frame +*/ + +static void ffmpeg_postprocess(struct anim * anim) { AVFrame * input = anim->pFrame; + ImBuf * ibuf = anim->last_frame; + int filter_y = 0; + + ibuf->profile = IB_PROFILE_SRGB; /* This means the data wasnt read properly, this check stops crashing */ @@ -690,12 +729,16 @@ static void ffmpeg_postprocess(struct anim * anim, ImBuf * ibuf, anim->pCodecCtx->width, anim->pCodecCtx->height) < 0) { - *filter_y = 1; + filter_y = TRUE; } else { input = anim->pFrameDeinterlaced; } } + avpicture_fill((AVPicture*) anim->pFrameRGB, + (unsigned char*) ibuf->rect, + PIX_FMT_RGBA, anim->x, anim->y); + if (ENDIAN_ORDER == B_ENDIAN) { int * dstStride = anim->pFrameRGB->linesize; uint8_t** dst = anim->pFrameRGB->data; @@ -774,150 +817,359 @@ static void ffmpeg_postprocess(struct anim * anim, ImBuf * ibuf, } } } + + if (filter_y) { + IMB_filtery(ibuf); + } } -static ImBuf * ffmpeg_fetchibuf(struct anim * anim, int position) { - ImBuf * ibuf; - int frameFinished; - AVPacket packet; +/* decode one video frame and load the next packet into anim->packet, + so that we can obtain next_pts and next undecoded pts */ + +static int ffmpeg_decode_video_frame(struct anim * anim) +{ + int frameFinished = 0; + int rval = 0; + + av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE VIDEO FRAME\n"); + + anim->next_undecoded_pts = -1; + + if (anim->next_packet.stream_index == anim->videoStream) { + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + " DECODE: cached next packet\n"); + + avcodec_decode_video2(anim->pCodecCtx, + anim->pFrame, &frameFinished, + &anim->next_packet); + + if (frameFinished) { + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, + " FRAME DONE: " + "next_pts=%lld pkt_pts=%lld\n", + (anim->pFrame->pts == AV_NOPTS_VALUE) ? + -1 : anim->pFrame->pts, + (anim->pFrame->pkt_pts == AV_NOPTS_VALUE) ? + -1 : anim->pFrame->pkt_pts); + anim->next_pts = + av_get_pts_from_frame(anim->pFormatCtx, + anim->pFrame); + } + + av_free_packet(&anim->next_packet); + anim->next_packet.stream_index = -1; + } + + while((rval = av_read_frame(anim->pFormatCtx, &anim->next_packet)) >= 0) { + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, + "%sREAD: strID=%d (VID: %d) dts=%lld pts=%lld " + "%s\n", + (anim->next_packet.stream_index == anim->videoStream) + ? "->" : " ", + anim->next_packet.stream_index, + anim->videoStream, + (anim->next_packet.dts == AV_NOPTS_VALUE) ? -1: + anim->next_packet.dts, + (anim->next_packet.pts == AV_NOPTS_VALUE) ? -1: + anim->next_packet.pts, + (anim->next_packet.flags & AV_PKT_FLAG_KEY) ? + " KEY" : ""); + if (anim->next_packet.stream_index == anim->videoStream) { + if (frameFinished) { + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, + " FRAME finished, we leave\n"); + anim->next_undecoded_pts + = anim->next_packet.dts; + break; + } + + avcodec_decode_video2( + anim->pCodecCtx, + anim->pFrame, &frameFinished, + &anim->next_packet); + + if (frameFinished) { + anim->next_pts = av_get_pts_from_frame( + anim->pFormatCtx, anim->pFrame); + + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, + " FRAME DONE: next_pts=%lld " + "pkt_pts=%lld, guessed_pts=%lld\n", + (anim->pFrame->pts == AV_NOPTS_VALUE) ? + -1 : anim->pFrame->pts, + (anim->pFrame->pkt_pts + == AV_NOPTS_VALUE) ? + -1 : anim->pFrame->pkt_pts, + anim->next_pts); + } + } + av_free_packet(&anim->next_packet); + anim->next_packet.stream_index = -1; + } + + if (rval < 0) { + av_log(anim->pFormatCtx, + AV_LOG_ERROR, " DECODE READ FAILED: av_read_frame() " + "returned error: %d\n", rval); + } + return (rval >= 0); +} + +static void ffmpeg_decode_video_frame_scan( + struct anim * anim, int64_t pts_to_search) +{ + /* there seem to exist *very* silly GOP lengths out in the wild... */ + int count = 1000; + + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, + "SCAN start: considering pts=%lld in search of %lld\n", + anim->next_pts, pts_to_search); + + while (count > 0 && anim->next_pts < pts_to_search) { + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, + " WHILE: pts=%lld in search of %lld\n", + anim->next_pts, pts_to_search); + if (!ffmpeg_decode_video_frame(anim)) { + break; + } + count--; + } + if (count == 0) { + av_log(anim->pFormatCtx, + AV_LOG_ERROR, + "SCAN failed: completely lost in stream, " + "bailing out at PTS=%lld, searching for PTS=%lld\n", + anim->next_pts, pts_to_search); + } + if (anim->next_pts == pts_to_search) { + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, "SCAN HAPPY: we found our PTS!\n"); + } else { + av_log(anim->pFormatCtx, + AV_LOG_ERROR, "SCAN UNHAPPY: PTS not matched!\n"); + } +} + +static int match_format(const char *name, AVFormatContext * pFormatCtx) +{ + const char *p; + int len, namelen; + + const char *names = pFormatCtx->iformat->name; + + if (!name || !names) + return 0; + + namelen = strlen(name); + while ((p = strchr(names, ','))) { + len = MAX2(p - names, namelen); + if (!BLI_strncasecmp(name, names, len)) + return 1; + names = p+1; + } + return !BLI_strcasecmp(name, names); +} + +static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx) +{ + static const char * byte_seek_list [] = { "dv", "mpegts", 0 }; + const char ** p; + + if (pFormatCtx->iformat->flags & AVFMT_TS_DISCONT) { + return TRUE; + } + + p = byte_seek_list; + + while (*p) { + if (match_format(*p++, pFormatCtx)) { + return TRUE; + } + } + + return FALSE; +} + +static ImBuf * ffmpeg_fetchibuf(struct anim * anim, int position, + IMB_Timecode_Type tc) { int64_t pts_to_search = 0; - int pos_found = 1; - int filter_y = 0; - int seek_by_bytes= 0; - int preseek_count = 0; + double frame_rate; + double pts_time_base; + long long st_time; + struct anim_index * tc_index = 0; + AVStream * v_st; + int new_frame_index; + int old_frame_index; if (anim == 0) return (0); - ibuf = IMB_allocImBuf(anim->x, anim->y, 32, IB_rect); + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: pos=%d\n", position); - avpicture_fill((AVPicture*) anim->pFrameRGB, - (unsigned char*) ibuf->rect, - PIX_FMT_RGBA, anim->x, anim->y); - - if (position != anim->curposition + 1) { - if (position > anim->curposition + 1 - && anim->preseek - && position - (anim->curposition + 1) < anim->preseek) { - while(av_read_frame(anim->pFormatCtx, &packet)>=0) { - if (packet.stream_index == anim->videoStream) { - avcodec_decode_video2( - anim->pCodecCtx, - anim->pFrame, &frameFinished, - &packet); - - if (frameFinished) { - anim->curposition++; - } - } - av_free_packet(&packet); - if (position == anim->curposition+1) { - break; - } - } + if (tc != IMB_TC_NONE) { + tc_index = IMB_anim_open_index(anim, tc); + } + + v_st = anim->pFormatCtx->streams[anim->videoStream]; + + frame_rate = av_q2d(v_st->r_frame_rate); + + st_time = anim->pFormatCtx->start_time; + pts_time_base = av_q2d(v_st->time_base); + + if (tc_index) { + new_frame_index = IMB_indexer_get_frame_index( + tc_index, position); + old_frame_index = IMB_indexer_get_frame_index( + tc_index, anim->curposition); + pts_to_search = IMB_indexer_get_pts( + tc_index, new_frame_index); + } else { + pts_to_search = (long long) + floor(((double) position) / pts_time_base / frame_rate + 0.5); + + if (st_time != AV_NOPTS_VALUE) { + pts_to_search += st_time / pts_time_base + / AV_TIME_BASE; } } -/* disable seek_by_bytes for now, since bitrates are guessed wrong! - also: MPEG2TS-seeking was fixed in later versions of ffmpeg, so problem - is somewhat fixed by now (until we add correct timecode management code...) -*/ -#if 0 - seek_by_bytes = !!(anim->pFormatCtx->iformat->flags & AVFMT_TS_DISCONT); -#else - seek_by_bytes = FALSE; -#endif + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + "FETCH: looking for PTS=%lld " + "(pts_timebase=%g, frame_rate=%g, st_time=%lld)\n", + pts_to_search, pts_time_base, frame_rate, st_time); + + if (anim->last_frame && + anim->last_pts <= pts_to_search && anim->next_pts > pts_to_search){ + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + "FETCH: frame repeat: last: %lld next: %lld\n", + anim->last_pts, anim->next_pts); + IMB_refImBuf(anim->last_frame); + anim->curposition = position; + return anim->last_frame; + } + + IMB_freeImBuf(anim->last_frame); + + if (anim->next_pts <= pts_to_search && + anim->next_undecoded_pts > pts_to_search) { + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + "FETCH: no seek necessary: " + "next: %lld next undecoded: %lld\n", + anim->next_pts, anim->next_undecoded_pts); + + /* we are already done :) */ + + } else if (position > anim->curposition + 1 + && anim->preseek + && !tc_index + && position - (anim->curposition + 1) < anim->preseek) { + + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + "FETCH: within preseek interval (no index)\n"); + + ffmpeg_decode_video_frame_scan(anim, pts_to_search); + } else if (tc_index && + IMB_indexer_can_scan(tc_index, old_frame_index, + new_frame_index)) { + + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + "FETCH: within preseek interval " + "(index tells us)\n"); - if (position != anim->curposition + 1) { - double frame_rate = - av_q2d(anim->pFormatCtx->streams[anim->videoStream] - ->r_frame_rate); - double pts_time_base = av_q2d(anim->pFormatCtx->streams[anim->videoStream]->time_base); + ffmpeg_decode_video_frame_scan(anim, pts_to_search); + } else if (position != anim->curposition + 1) { long long pos; - long long st_time = anim->pFormatCtx->start_time; int ret; - if (seek_by_bytes) { - pos = position - anim->preseek; - if (pos < 0) { - pos = 0; - } - preseek_count = position - pos; + if (tc_index) { + unsigned long long dts; + + pos = IMB_indexer_get_seek_pos( + tc_index, new_frame_index); + dts = IMB_indexer_get_seek_pos_dts( + tc_index, new_frame_index); + + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + "TC INDEX seek pos = %lld\n", pos); + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + "TC INDEX seek dts = %lld\n", dts); - pos *= anim->pFormatCtx->bit_rate / frame_rate; - pos /= 8; + if (ffmpeg_seek_by_byte(anim->pFormatCtx)) { + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + "... using BYTE pos\n"); + + ret = av_seek_frame(anim->pFormatCtx, + -1, + pos, AVSEEK_FLAG_BYTE); + av_update_cur_dts(anim->pFormatCtx, v_st, dts); + } else { + av_log(anim->pFormatCtx, AV_LOG_DEBUG, + "... using DTS pos\n"); + ret = av_seek_frame(anim->pFormatCtx, + anim->videoStream, + dts, AVSEEK_FLAG_BACKWARD); + } } else { pos = (long long) (position - anim->preseek) * AV_TIME_BASE / frame_rate; if (pos < 0) { pos = 0; } - + if (st_time != AV_NOPTS_VALUE) { pos += st_time; } - } - ret = av_seek_frame(anim->pFormatCtx, -1, - pos, - AVSEEK_FLAG_BACKWARD | ( - seek_by_bytes - ? AVSEEK_FLAG_ANY - | AVSEEK_FLAG_BYTE : 0)); - if (ret < 0) { - fprintf(stderr, "error while seeking: %d\n", ret); + ret = av_seek_frame(anim->pFormatCtx, -1, + pos, AVSEEK_FLAG_BACKWARD); } - pts_to_search = (long long) - (((double) position) / pts_time_base / frame_rate); - if (st_time != AV_NOPTS_VALUE) { - pts_to_search += st_time / pts_time_base/ AV_TIME_BASE; + if (ret < 0) { + av_log(anim->pFormatCtx, AV_LOG_ERROR, + "FETCH: " + "error while seeking to DTS = %lld " + "(frameno = %d, PTS = %lld): errcode = %d\n", + pos, position, pts_to_search, ret); } - pos_found = 0; avcodec_flush_buffers(anim->pCodecCtx); - } - while(av_read_frame(anim->pFormatCtx, &packet)>=0) { - if(packet.stream_index == anim->videoStream) { - avcodec_decode_video2(anim->pCodecCtx, - anim->pFrame, &frameFinished, - &packet); + anim->next_pts = -1; - if (seek_by_bytes && preseek_count > 0) { - preseek_count--; - } + if (anim->next_packet.stream_index == anim->videoStream) { + av_free_packet(&anim->next_packet); + anim->next_packet.stream_index = -1; + } - if (frameFinished && !pos_found) { - if (seek_by_bytes) { - if (!preseek_count) { - pos_found = 1; - anim->curposition = position; - } - } else { - if (packet.dts >= pts_to_search) { - pos_found = 1; - anim->curposition = position; - } - } - } + /* memset(anim->pFrame,...) ?? */ - if(frameFinished && pos_found == 1) { - ffmpeg_postprocess(anim, ibuf, &filter_y); - av_free_packet(&packet); - break; - } + if (ret >= 0) { + ffmpeg_decode_video_frame_scan(anim, pts_to_search); } - - av_free_packet(&packet); + } else if (position == 0 && anim->curposition == -1) { + /* first frame without seeking special case... */ + ffmpeg_decode_video_frame(anim); } - if (filter_y && ibuf) { - IMB_filtery(ibuf); - } + anim->last_frame = IMB_allocImBuf(anim->x, anim->y, 32, IB_rect); - ibuf->profile = IB_PROFILE_SRGB; + ffmpeg_postprocess(anim); - return(ibuf); + anim->last_pts = anim->next_pts; + + ffmpeg_decode_video_frame(anim); + + anim->curposition = position; + + IMB_refImBuf(anim->last_frame); + + return anim->last_frame; } static void free_anim_ffmpeg(struct anim * anim) { @@ -934,6 +1186,10 @@ static void free_anim_ffmpeg(struct anim * anim) { } av_free(anim->pFrameDeinterlaced); sws_freeContext(anim->img_convert_ctx); + IMB_freeImBuf(anim->last_frame); + if (anim->next_packet.stream_index != -1) { + av_free_packet(&anim->next_packet); + } } anim->duration = 0; } @@ -1063,16 +1319,19 @@ struct ImBuf * IMB_anim_previewframe(struct anim * anim) { struct ImBuf * ibuf = NULL; int position = 0; - ibuf = IMB_anim_absolute(anim, 0); + ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE); if (ibuf) { IMB_freeImBuf(ibuf); position = anim->duration / 2; - ibuf = IMB_anim_absolute(anim, position); + ibuf = IMB_anim_absolute(anim, position, IMB_TC_NONE, + IMB_PROXY_NONE); } return ibuf; } -struct ImBuf * IMB_anim_absolute(struct anim * anim, int position) { +struct ImBuf * IMB_anim_absolute(struct anim * anim, int position, + IMB_Timecode_Type tc, + IMB_Proxy_Size preview_size) { struct ImBuf * ibuf = NULL; char head[256], tail[256]; unsigned short digits; @@ -1095,6 +1354,18 @@ struct ImBuf * IMB_anim_absolute(struct anim * anim, int position) { if (position < 0) return(NULL); if (position >= anim->duration) return(NULL); + if (preview_size != IMB_PROXY_NONE) { + struct anim * proxy = IMB_anim_open_proxy(anim, preview_size); + + if (proxy) { + position = IMB_anim_index_get_frame_index( + anim, tc, position); + return IMB_anim_absolute( + proxy, position, + IMB_TC_NONE, IMB_PROXY_NONE); + } + } + switch(anim->curtype) { case ANIM_SEQUENCE: pic = an_stringdec(anim->first, head, tail, &digits); @@ -1127,7 +1398,7 @@ struct ImBuf * IMB_anim_absolute(struct anim * anim, int position) { #endif #ifdef WITH_FFMPEG case ANIM_FFMPEG: - ibuf = ffmpeg_fetchibuf(anim, position); + ibuf = ffmpeg_fetchibuf(anim, position, tc); if (ibuf) anim->curposition = position; filter_y = 0; /* done internally */ @@ -1151,8 +1422,29 @@ struct ImBuf * IMB_anim_absolute(struct anim * anim, int position) { /***/ -int IMB_anim_get_duration(struct anim *anim) { - return anim->duration; +int IMB_anim_get_duration(struct anim *anim, IMB_Timecode_Type tc) { + struct anim_index * idx; + if (tc == IMB_TC_NONE) { + return anim->duration; + } + + idx = IMB_anim_open_index(anim, tc); + if (!idx) { + return anim->duration; + } + + return IMB_indexer_get_duration(idx); +} + +int IMB_anim_get_fps(struct anim * anim, + short * frs_sec, float * frs_sec_base) +{ + if (anim->frs_sec) { + *frs_sec = anim->frs_sec; + *frs_sec_base = anim->frs_sec_base; + return TRUE; + } + return FALSE; } void IMB_anim_set_preseek(struct anim * anim, int preseek) |