This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/imbuf/intern/indexer.c
Sergey Sharybin 3f81d010e3 Free run no gaps time code implementation
This commit adds new timecode type which counts frames in gapless mode (counting
actually decoded frames instead of using pts to find frame number) which might
resolve issues with files which have got broken or incorrect base time value stored
in the header.

This timecode allows to deal with movies from #29388: Abnormal frame length on MP4 files
2011-12-15 12:18:09 +00:00

1159 lines
26 KiB
C

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