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

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1372 lines
36 KiB
C
Raw Normal View History

/*
* 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
*/
/** \file
* \ingroup imbuf
2013-09-12 03:02:50 +00:00
*/
#include <stdlib.h>
#include "MEM_guardedalloc.h"
#include "BLI_endian_switch.h"
#include "BLI_fileops.h"
#include "BLI_ghash.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#ifdef _WIN32
# include "BLI_winstuff.h"
#endif
#include "IMB_anim.h"
#include "IMB_indexer.h"
#include "imbuf.h"
#include "BKE_global.h"
#ifdef WITH_AVI
# include "AVI_avi.h"
#endif
#ifdef WITH_FFMPEG
# include "ffmpeg_compat.h"
#endif
static const char magic[] = "BlenMIdx";
static const char temp_ext[] = "_part";
static const int proxy_sizes[] = {IMB_PROXY_25, IMB_PROXY_50, IMB_PROXY_75, IMB_PROXY_100};
static const 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
/* ----------------------------------------------------------------------
* - time code index functions
* ---------------------------------------------------------------------- */
2012-05-16 09:26:37 +00:00
anim_index_builder *IMB_index_builder_create(const char *name)
{
2012-05-16 09:26:37 +00:00
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 = BLI_fopen(rv->temp_name, "wb");
if (!rv->fp) {
fprintf(stderr,
"Couldn't open index target: %s! "
2012-05-16 09:26:37 +00:00
"Index build broken!\n",
rv->temp_name);
MEM_freeN(rv);
return NULL;
}
2012-05-16 09:26:37 +00:00
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, uint64_t seek_pos, uint64_t seek_pos_dts, uint64_t pts)
{
fwrite(&frameno, sizeof(int), 1, fp->fp);
fwrite(&seek_pos, sizeof(uint64_t), 1, fp->fp);
fwrite(&seek_pos_dts, sizeof(uint64_t), 1, fp->fp);
fwrite(&pts, sizeof(uint64_t), 1, fp->fp);
}
2012-05-16 09:26:37 +00:00
void IMB_index_builder_proc_frame(anim_index_builder *fp,
uchar *buffer,
2012-05-16 09:26:37 +00:00
int data_size,
int frameno,
uint64_t seek_pos,
uint64_t seek_pos_dts,
uint64_t 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);
}
}
2012-05-16 09:26:37 +00:00
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 {
unlink(fp->name);
BLI_rename(fp->temp_name, fp->name);
}
MEM_freeN(fp);
}
2012-05-16 09:26:37 +00:00
struct anim_index *IMB_indexer_open(const char *name)
{
char header[13];
2012-05-16 09:26:37 +00:00
struct anim_index *idx;
FILE *fp = BLI_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;
}
2012-05-16 09:26:37 +00:00
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));
2011-09-25 12:31:21 +00:00
fseek(fp, 0, SEEK_END);
idx->num_entries = (ftell(fp) - 12) / (sizeof(int) + /* framepos */
sizeof(uint64_t) + /* seek_pos */
sizeof(uint64_t) + /* seek_pos_dts */
sizeof(uint64_t) /* pts */
2012-05-16 09:26:37 +00:00
);
fseek(fp, 12, SEEK_SET);
2012-05-09 09:24:15 +00:00
idx->entries = MEM_callocN(sizeof(struct anim_index_entry) * idx->num_entries,
"anim_index_entries");
size_t items_read = 0;
for (i = 0; i < idx->num_entries; i++) {
ImBuf: Add error handling to IMB_indexer_open Handle return value of `fread()` by printing an error and closing the file when it cannot be read from. Not only is error handing a good idea, it also prevents GCC from warning that the return value of `fread()` should not be ignored: ``` .../blender/source/blender/imbuf/intern/indexer.c: In function ‘IMB_indexer_open’: .../blender/source/blender/imbuf/intern/indexer.c:201:5: warning: ignoring return value of ‘fread’, declared with attribute warn_unused_result [-Wunused-result] 201 | fread(&idx->entries[i].frameno, sizeof(int), 1, fp); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .../blender/source/blender/imbuf/intern/indexer.c:202:5: warning: ignoring return value of ‘fread’, declared with attribute warn_unused_result [-Wunused-result] 202 | fread(&idx->entries[i].seek_pos, sizeof(unsigned long long), 1, fp); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .../blender/source/blender/imbuf/intern/indexer.c:203:5: warning: ignoring return value of ‘fread’, declared with attribute warn_unused_result [-Wunused-result] 203 | fread(&idx->entries[i].seek_pos_dts, sizeof(unsigned long long), 1, fp); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .../blender/source/blender/imbuf/intern/indexer.c:204:5: warning: ignoring return value of ‘fread’, declared with attribute warn_unused_result [-Wunused-result] 204 | fread(&idx->entries[i].pts, sizeof(unsigned long long), 1, fp); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` Differential Revision: https://developer.blender.org/D9916 Reviewed by: campbellbarton
2021-01-11 17:29:21 +01:00
items_read += fread(&idx->entries[i].frameno, sizeof(int), 1, fp);
items_read += fread(&idx->entries[i].seek_pos, sizeof(uint64_t), 1, fp);
items_read += fread(&idx->entries[i].seek_pos_dts, sizeof(uint64_t), 1, fp);
items_read += fread(&idx->entries[i].pts, sizeof(uint64_t), 1, fp);
}
ImBuf: Add error handling to IMB_indexer_open Handle return value of `fread()` by printing an error and closing the file when it cannot be read from. Not only is error handing a good idea, it also prevents GCC from warning that the return value of `fread()` should not be ignored: ``` .../blender/source/blender/imbuf/intern/indexer.c: In function ‘IMB_indexer_open’: .../blender/source/blender/imbuf/intern/indexer.c:201:5: warning: ignoring return value of ‘fread’, declared with attribute warn_unused_result [-Wunused-result] 201 | fread(&idx->entries[i].frameno, sizeof(int), 1, fp); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .../blender/source/blender/imbuf/intern/indexer.c:202:5: warning: ignoring return value of ‘fread’, declared with attribute warn_unused_result [-Wunused-result] 202 | fread(&idx->entries[i].seek_pos, sizeof(unsigned long long), 1, fp); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .../blender/source/blender/imbuf/intern/indexer.c:203:5: warning: ignoring return value of ‘fread’, declared with attribute warn_unused_result [-Wunused-result] 203 | fread(&idx->entries[i].seek_pos_dts, sizeof(unsigned long long), 1, fp); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .../blender/source/blender/imbuf/intern/indexer.c:204:5: warning: ignoring return value of ‘fread’, declared with attribute warn_unused_result [-Wunused-result] 204 | fread(&idx->entries[i].pts, sizeof(unsigned long long), 1, fp); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` Differential Revision: https://developer.blender.org/D9916 Reviewed by: campbellbarton
2021-01-11 17:29:21 +01:00
if (UNLIKELY(items_read != idx->num_entries * 4)) {
perror("error reading animation index file");
MEM_freeN(idx->entries);
MEM_freeN(idx);
fclose(fp);
return NULL;
}
if (((ENDIAN_ORDER == B_ENDIAN) != (header[8] == 'V'))) {
for (i = 0; i < idx->num_entries; i++) {
BLI_endian_switch_int32(&idx->entries[i].frameno);
BLI_endian_switch_uint64(&idx->entries[i].seek_pos);
BLI_endian_switch_uint64(&idx->entries[i].seek_pos_dts);
BLI_endian_switch_uint64(&idx->entries[i].pts);
}
}
fclose(fp);
return idx;
}
uint64_t 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;
}
uint64_t 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;
}
2012-05-16 09:26:37 +00:00
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;
2012-05-09 09:24:15 +00:00
first++;
len = len - half - 1;
}
else {
len = half;
}
}
if (first == idx->num_entries) {
return idx->num_entries - 1;
}
return first;
}
uint64_t 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;
}
2012-05-16 09:26:37 +00:00
int IMB_indexer_get_duration(struct anim_index *idx)
{
if (idx->num_entries == 0) {
return 0;
}
2012-05-16 09:26:37 +00:00
return idx->entries[idx->num_entries - 1].frameno + 1;
}
2012-05-16 09:26:37 +00:00
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
2012-04-22 11:54:53 +00:00
* trying to run backwards in time... */
2012-05-09 09:24:15 +00:00
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);
}
2012-05-16 09:26:37 +00:00
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) {
2019-04-18 07:21:26 +02:00
case IMB_PROXY_NONE:
/* if we got here, something is broken anyways, so sane defaults... */
2012-05-16 09:26:37 +00:00
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;
2013-01-19 06:12:25 +00:00
}
}
int IMB_timecode_to_array_index(IMB_Timecode_Type tc)
{
switch (tc) {
2012-05-16 09:26:37 +00:00
case IMB_TC_NONE: /* if we got here, something is broken anyways,
2012-06-30 22:49:33 +00:00
* so sane defaults... */
2012-05-16 09:26:37 +00:00
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;
2013-01-19 06:12:25 +00:00
}
}
/* ----------------------------------------------------------------------
* - rebuild helper functions
* ---------------------------------------------------------------------- */
static void get_index_dir(struct anim *anim, char *index_dir, size_t index_dir_len)
{
if (!anim->index_dir[0]) {
char fname[FILE_MAXFILE];
BLI_split_dirfile(anim->name, index_dir, fname, index_dir_len, sizeof(fname));
BLI_path_append(index_dir, index_dir_len, "BL_proxy");
BLI_path_append(index_dir, index_dir_len, fname);
}
else {
BLI_strncpy(index_dir, anim->index_dir, index_dir_len);
}
}
void IMB_anim_get_fname(struct anim *anim, char *file, int size)
{
char fname[FILE_MAXFILE];
BLI_split_dirfile(anim->name, file, fname, size, sizeof(fname));
BLI_strncpy(file, fname, size);
}
static bool get_proxy_filename(struct anim *anim,
2012-05-16 09:26:37 +00:00
IMB_Proxy_Size preview_size,
char *fname,
bool temp)
{
char index_dir[FILE_MAXDIR];
int i = IMB_proxy_size_to_array_index(preview_size);
char proxy_name[256];
char stream_suffix[20];
const char *name = (temp) ? "proxy_%d%s_part.avi" : "proxy_%d%s.avi";
stream_suffix[0] = 0;
if (anim->streamindex > 0) {
BLI_snprintf(stream_suffix, sizeof(stream_suffix), "_st%d", anim->streamindex);
}
BLI_snprintf(proxy_name,
sizeof(proxy_name),
name,
Multi-View and Stereo 3D Official Documentation: http://www.blender.org/manual/render/workflows/multiview.html Implemented Features ==================== Builtin Stereo Camera * Convergence Mode * Interocular Distance * Convergence Distance * Pivot Mode Viewport * Cameras * Plane * Volume Compositor * View Switch Node * Image Node Multi-View OpenEXR support Sequencer * Image/Movie Strips 'Use Multiview' UV/Image Editor * Option to see Multi-View images in Stereo-3D or its individual images * Save/Open Multi-View (OpenEXR, Stereo3D, individual views) images I/O * Save/Open Multi-View (OpenEXR, Stereo3D, individual views) images Scene Render Views * Ability to have an arbitrary number of views in the scene Missing Bits ============ First rule of Multi-View bug report: If something is not working as it should *when Views is off* this is a severe bug, do mention this in the report. Second rule is, if something works *when Views is off* but doesn't (or crashes) when *Views is on*, this is a important bug. Do mention this in the report. Everything else is likely small todos, and may wait until we are sure none of the above is happening. Apart from that there are those known issues: * Compositor Image Node poorly working for Multi-View OpenEXR (this was working prefectly before the 'Use Multi-View' functionality) * Selecting camera from Multi-View when looking from camera is problematic * Animation Playback (ctrl+F11) doesn't support stereo formats * Wrong filepath when trying to play back animated scene * Viewport Rendering doesn't support Multi-View * Overscan Rendering * Fullscreen display modes need to warn the user * Object copy should be aware of views suffix Acknowledgments =============== * Francesco Siddi for the help with the original feature specs and design * Brecht Van Lommel for the original review of the code and design early on * Blender Foundation for the Development Fund to support the project wrap up Final patch reviewers: * Antony Riakiotakis (psy-fi) * Campbell Barton (ideasman42) * Julian Eisel (Severin) * Sergey Sharybin (nazgul) * Thomas Dinged (dingto) Code contributors of the original branch in github: * Alexey Akishin * Gabriel Caraballo
2015-04-06 10:40:12 -03:00
(int)(proxy_fac[i] * 100),
stream_suffix,
anim->suffix);
get_index_dir(anim, index_dir, sizeof(index_dir));
if (BLI_path_ncmp(anim->name, index_dir, FILE_MAXDIR) == 0) {
return false;
}
BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR, index_dir, proxy_name);
return true;
}
2012-05-16 09:26:37 +00:00
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);
2012-05-16 09:26:37 +00:00
const char *index_names[] = {
Multi-View and Stereo 3D Official Documentation: http://www.blender.org/manual/render/workflows/multiview.html Implemented Features ==================== Builtin Stereo Camera * Convergence Mode * Interocular Distance * Convergence Distance * Pivot Mode Viewport * Cameras * Plane * Volume Compositor * View Switch Node * Image Node Multi-View OpenEXR support Sequencer * Image/Movie Strips 'Use Multiview' UV/Image Editor * Option to see Multi-View images in Stereo-3D or its individual images * Save/Open Multi-View (OpenEXR, Stereo3D, individual views) images I/O * Save/Open Multi-View (OpenEXR, Stereo3D, individual views) images Scene Render Views * Ability to have an arbitrary number of views in the scene Missing Bits ============ First rule of Multi-View bug report: If something is not working as it should *when Views is off* this is a severe bug, do mention this in the report. Second rule is, if something works *when Views is off* but doesn't (or crashes) when *Views is on*, this is a important bug. Do mention this in the report. Everything else is likely small todos, and may wait until we are sure none of the above is happening. Apart from that there are those known issues: * Compositor Image Node poorly working for Multi-View OpenEXR (this was working prefectly before the 'Use Multi-View' functionality) * Selecting camera from Multi-View when looking from camera is problematic * Animation Playback (ctrl+F11) doesn't support stereo formats * Wrong filepath when trying to play back animated scene * Viewport Rendering doesn't support Multi-View * Overscan Rendering * Fullscreen display modes need to warn the user * Object copy should be aware of views suffix Acknowledgments =============== * Francesco Siddi for the help with the original feature specs and design * Brecht Van Lommel for the original review of the code and design early on * Blender Foundation for the Development Fund to support the project wrap up Final patch reviewers: * Antony Riakiotakis (psy-fi) * Campbell Barton (ideasman42) * Julian Eisel (Severin) * Sergey Sharybin (nazgul) * Thomas Dinged (dingto) Code contributors of the original branch in github: * Alexey Akishin * Gabriel Caraballo
2015-04-06 10:40:12 -03:00
"record_run%s%s.blen_tc",
"free_run%s%s.blen_tc",
"interp_free_run%s%s.blen_tc",
"record_run_no_gaps%s%s.blen_tc",
2012-05-16 09:26:37 +00:00
};
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);
}
Multi-View and Stereo 3D Official Documentation: http://www.blender.org/manual/render/workflows/multiview.html Implemented Features ==================== Builtin Stereo Camera * Convergence Mode * Interocular Distance * Convergence Distance * Pivot Mode Viewport * Cameras * Plane * Volume Compositor * View Switch Node * Image Node Multi-View OpenEXR support Sequencer * Image/Movie Strips 'Use Multiview' UV/Image Editor * Option to see Multi-View images in Stereo-3D or its individual images * Save/Open Multi-View (OpenEXR, Stereo3D, individual views) images I/O * Save/Open Multi-View (OpenEXR, Stereo3D, individual views) images Scene Render Views * Ability to have an arbitrary number of views in the scene Missing Bits ============ First rule of Multi-View bug report: If something is not working as it should *when Views is off* this is a severe bug, do mention this in the report. Second rule is, if something works *when Views is off* but doesn't (or crashes) when *Views is on*, this is a important bug. Do mention this in the report. Everything else is likely small todos, and may wait until we are sure none of the above is happening. Apart from that there are those known issues: * Compositor Image Node poorly working for Multi-View OpenEXR (this was working prefectly before the 'Use Multi-View' functionality) * Selecting camera from Multi-View when looking from camera is problematic * Animation Playback (ctrl+F11) doesn't support stereo formats * Wrong filepath when trying to play back animated scene * Viewport Rendering doesn't support Multi-View * Overscan Rendering * Fullscreen display modes need to warn the user * Object copy should be aware of views suffix Acknowledgments =============== * Francesco Siddi for the help with the original feature specs and design * Brecht Van Lommel for the original review of the code and design early on * Blender Foundation for the Development Fund to support the project wrap up Final patch reviewers: * Antony Riakiotakis (psy-fi) * Campbell Barton (ideasman42) * Julian Eisel (Severin) * Sergey Sharybin (nazgul) * Thomas Dinged (dingto) Code contributors of the original branch in github: * Alexey Akishin * Gabriel Caraballo
2015-04-06 10:40:12 -03:00
BLI_snprintf(index_name, 256, index_names[i], stream_suffix, anim->suffix);
get_index_dir(anim, index_dir, sizeof(index_dir));
BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR, index_dir, index_name);
}
/* ----------------------------------------------------------------------
* - common rebuilder structures
* ---------------------------------------------------------------------- */
typedef struct IndexBuildContext {
int anim_type;
} IndexBuildContext;
/* ----------------------------------------------------------------------
* - ffmpeg rebuilder
* ---------------------------------------------------------------------- */
#ifdef WITH_FFMPEG
struct proxy_output_ctx {
2012-05-16 09:26:37 +00:00
AVFormatContext *of;
AVStream *st;
AVCodecContext *c;
AVCodec *codec;
struct SwsContext *sws_ctx;
AVFrame *frame;
int cfra;
int proxy_size;
int orig_height;
2012-05-16 09:26:37 +00:00
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);
}
2012-05-16 09:26:37 +00:00
static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
struct anim *anim, AVStream *st, int proxy_size, int width, int height, int quality)
{
2012-05-16 09:26:37 +00:00
struct proxy_output_ctx *rv = MEM_callocN(sizeof(struct proxy_output_ctx), "alloc_proxy_output");
char fname[FILE_MAX];
int ffmpeg_quality;
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_strncpy(rv->of->filename, fname, sizeof(rv->of->filename));
fprintf(stderr, "Starting work on proxy: %s\n", rv->of->filename);
rv->st = avformat_new_stream(rv->of, NULL);
rv->st->id = 0;
rv->c = rv->st->codec;
rv->c->codec_type = AVMEDIA_TYPE_VIDEO;
rv->c->codec_id = AV_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? "
2012-05-16 09:26:37 +00:00
"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 = AV_PIX_FMT_YUVJ420P;
}
2012-05-09 09:24:15 +00:00
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;
/* there's no way to set JPEG quality in the same way as in AVI JPEG and image sequence,
* but this seems to be giving expected quality result */
ffmpeg_quality = (int)(1.0f + 30.0f * (1.0f - (float)quality / 100.0f) + 0.5f);
av_opt_set_int(rv->c, "qmin", ffmpeg_quality, 0);
av_opt_set_int(rv->c, "qmax", ffmpeg_quality, 0);
if (rv->of->flags & AVFMT_GLOBALHEADER) {
rv->c->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
if (avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE) < 0) {
fprintf(stderr,
"Couldn't open outputfile! "
2012-05-16 09:26:37 +00:00
"Proxy not built!\n");
av_free(rv->of);
return 0;
}
avcodec_open2(rv->c, rv->codec, NULL);
rv->orig_height = av_get_cropped_height_from_codec(st->codec);
if (st->codec->width != width || st->codec->height != height ||
2012-05-16 09:26:37 +00:00
st->codec->pix_fmt != rv->c->pix_fmt) {
rv->frame = av_frame_alloc();
2012-05-16 09:26:37 +00:00
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,
rv->orig_height,
2012-05-16 09:26:37 +00:00
st->codec->pix_fmt,
width,
height,
rv->c->pix_fmt,
SWS_FAST_BILINEAR | SWS_PRINT_INFO,
NULL,
NULL,
NULL);
}
if (avformat_write_header(rv->of, NULL) < 0) {
fprintf(stderr,
"Couldn't set output parameters? "
"Proxy not built!\n");
av_free(rv->of);
return 0;
}
return rv;
}
static int add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *frame)
{
AVPacket packet = {0};
int ret, got_output;
av_init_packet(&packet);
if (!ctx) {
return 0;
}
2012-05-16 09:26:37 +00:00
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++;
}
ret = avcodec_encode_video2(ctx->c, &packet, frame, &got_output);
if (ret < 0) {
fprintf(stderr, "Error encoding proxy frame %d for '%s'\n", ctx->cfra - 1, ctx->of->filename);
return 0;
}
if (got_output) {
if (packet.pts != AV_NOPTS_VALUE) {
packet.pts = av_rescale_q(packet.pts, ctx->c->time_base, ctx->st->time_base);
}
if (packet.dts != AV_NOPTS_VALUE) {
packet.dts = av_rescale_q(packet.dts, ctx->c->time_base, ctx->st->time_base);
}
packet.stream_index = ctx->st->index;
if (av_interleaved_write_frame(ctx->of, &packet) != 0) {
fprintf(stderr,
"Error writing proxy frame %d "
2012-05-16 09:26:37 +00:00
"into '%s'\n",
ctx->cfra - 1,
ctx->of->filename);
return 0;
}
return 1;
}
return 0;
}
2012-05-16 09:26:37 +00:00
static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback)
{
char fname[FILE_MAX];
char fname_tmp[FILE_MAX];
if (!ctx) {
return;
}
if (!rollback) {
2012-02-27 10:35:39 +00:00
while (add_to_proxy_output_ffmpeg(ctx, NULL)) {
}
}
avcodec_flush_buffers(ctx->c);
av_write_trailer(ctx->of);
avcodec_close(ctx->c);
if (ctx->of->oformat) {
if (!(ctx->of->oformat->flags & AVFMT_NOFILE)) {
avio_close(ctx->of->pb);
}
}
avformat_free_context(ctx->of);
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);
unlink(fname);
BLI_rename(fname_tmp, fname);
}
MEM_freeN(ctx);
}
typedef struct FFmpegIndexBuilderContext {
int anim_type;
AVFormatContext *iFormatCtx;
AVCodecContext *iCodecCtx;
AVCodec *iCodec;
AVStream *iStream;
int videoStream;
int num_proxy_sizes;
int num_indexers;
2012-05-16 09:26:37 +00:00
struct proxy_output_ctx *proxy_ctx[IMB_PROXY_MAX_SLOT];
anim_index_builder *indexer[IMB_TC_MAX_SLOT];
IMB_Timecode_Type tcs_in_use;
IMB_Proxy_Size proxy_sizes_in_use;
uint64_t seek_pos;
uint64_t last_seek_pos;
uint64_t seek_pos_dts;
uint64_t seek_pos_pts;
uint64_t last_seek_pos_dts;
uint64_t start_pts;
double frame_rate;
double pts_time_base;
int frameno, frameno_gapless;
int start_pts_set;
} FFmpegIndexBuilderContext;
static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim,
IMB_Timecode_Type tcs_in_use,
IMB_Proxy_Size proxy_sizes_in_use,
int quality)
{
FFmpegIndexBuilderContext *context = MEM_callocN(sizeof(FFmpegIndexBuilderContext),
"FFmpeg index builder context");
int num_proxy_sizes = IMB_PROXY_MAX_SLOT;
int num_indexers = IMB_TC_MAX_SLOT;
int i, streamcount;
context->tcs_in_use = tcs_in_use;
context->proxy_sizes_in_use = proxy_sizes_in_use;
context->num_proxy_sizes = IMB_PROXY_MAX_SLOT;
context->num_indexers = IMB_TC_MAX_SLOT;
memset(context->proxy_ctx, 0, sizeof(context->proxy_ctx));
memset(context->indexer, 0, sizeof(context->indexer));
if (avformat_open_input(&context->iFormatCtx, anim->name, NULL, NULL) != 0) {
MEM_freeN(context);
return NULL;
}
if (avformat_find_stream_info(context->iFormatCtx, NULL) < 0) {
avformat_close_input(&context->iFormatCtx);
MEM_freeN(context);
return NULL;
}
streamcount = anim->streamindex;
/* Find the video stream */
context->videoStream = -1;
for (i = 0; i < context->iFormatCtx->nb_streams; i++) {
2012-05-09 09:24:15 +00:00
if (context->iFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
if (streamcount > 0) {
streamcount--;
continue;
}
context->videoStream = i;
break;
}
}
if (context->videoStream == -1) {
avformat_close_input(&context->iFormatCtx);
MEM_freeN(context);
return NULL;
}
context->iStream = context->iFormatCtx->streams[context->videoStream];
context->iCodecCtx = context->iStream->codec;
context->iCodec = avcodec_find_decoder(context->iCodecCtx->codec_id);
if (context->iCodec == NULL) {
avformat_close_input(&context->iFormatCtx);
MEM_freeN(context);
return NULL;
}
context->iCodecCtx->workaround_bugs = 1;
if (avcodec_open2(context->iCodecCtx, context->iCodec, NULL) < 0) {
avformat_close_input(&context->iFormatCtx);
MEM_freeN(context);
return NULL;
}
for (i = 0; i < num_proxy_sizes; i++) {
if (proxy_sizes_in_use & proxy_sizes[i]) {
context->proxy_ctx[i] = alloc_proxy_output_ffmpeg(
2012-05-16 09:26:37 +00:00
anim,
context->iStream,
proxy_sizes[i],
context->iCodecCtx->width * proxy_fac[i],
av_get_cropped_height_from_codec(context->iCodecCtx) * proxy_fac[i],
2012-05-16 09:26:37 +00:00
quality);
if (!context->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);
context->indexer[i] = IMB_index_builder_create(fname);
if (!context->indexer[i]) {
tcs_in_use &= ~tc_types[i];
}
}
}
return (IndexBuildContext *)context;
}
static void index_rebuild_ffmpeg_finish(FFmpegIndexBuilderContext *context, int stop)
{
int i;
for (i = 0; i < context->num_indexers; i++) {
if (context->tcs_in_use & tc_types[i]) {
IMB_index_builder_finish(context->indexer[i], stop);
}
}
for (i = 0; i < context->num_proxy_sizes; i++) {
if (context->proxy_sizes_in_use & proxy_sizes[i]) {
free_proxy_output_ffmpeg(context->proxy_ctx[i], stop);
}
}
avcodec_close(context->iCodecCtx);
avformat_close_input(&context->iFormatCtx);
MEM_freeN(context);
}
static void index_rebuild_ffmpeg_proc_decoded_frame(FFmpegIndexBuilderContext *context,
2017-11-18 17:22:54 +11:00
AVPacket *curr_packet,
AVFrame *in_frame)
{
int i;
uint64_t s_pos = context->seek_pos;
uint64_t s_dts = context->seek_pos_dts;
uint64_t pts = av_get_pts_from_frame(context->iFormatCtx, in_frame);
for (i = 0; i < context->num_proxy_sizes; i++) {
add_to_proxy_output_ffmpeg(context->proxy_ctx[i], in_frame);
}
if (!context->start_pts_set) {
context->start_pts = pts;
context->start_pts_set = true;
}
2012-11-01 09:54:00 +00:00
context->frameno = floor(
(pts - context->start_pts) * context->pts_time_base * context->frame_rate + 0.5);
/* 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 < context->seek_pos_pts) {
s_pos = context->last_seek_pos;
s_dts = context->last_seek_pos_dts;
}
for (i = 0; i < context->num_indexers; i++) {
if (context->tcs_in_use & tc_types[i]) {
int tc_frameno = context->frameno;
2019-04-23 11:01:30 +10:00
if (tc_types[i] == IMB_TC_RECORD_RUN_NO_GAPS) {
tc_frameno = context->frameno_gapless;
2019-04-23 11:01:30 +10:00
}
IMB_index_builder_proc_frame(context->indexer[i],
curr_packet->data,
curr_packet->size,
tc_frameno,
s_pos,
s_dts,
pts);
}
}
context->frameno_gapless++;
}
static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context,
const short *stop,
short *do_update,
float *progress)
{
2012-05-16 09:26:37 +00:00
AVFrame *in_frame = 0;
AVPacket next_packet;
uint64_t stream_size;
memset(&next_packet, 0, sizeof(AVPacket));
in_frame = av_frame_alloc();
stream_size = avio_size(context->iFormatCtx->pb);
context->frame_rate = av_q2d(av_guess_frame_rate(context->iFormatCtx, context->iStream, NULL));
context->pts_time_base = av_q2d(context->iStream->time_base);
while (av_read_frame(context->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 = true;
}
if (*stop) {
av_free_packet(&next_packet);
break;
}
if (next_packet.stream_index == context->videoStream) {
if (next_packet.flags & AV_PKT_FLAG_KEY) {
context->last_seek_pos = context->seek_pos;
context->last_seek_pos_dts = context->seek_pos_dts;
context->seek_pos = next_packet.pos;
context->seek_pos_dts = next_packet.dts;
context->seek_pos_pts = next_packet.pts;
}
2012-05-16 09:26:37 +00:00
avcodec_decode_video2(context->iCodecCtx, in_frame, &frame_finished, &next_packet);
}
if (frame_finished) {
index_rebuild_ffmpeg_proc_decoded_frame(context, &next_packet, in_frame);
}
av_free_packet(&next_packet);
}
/* process pictures still stuck in decoder engine after EOF
2012-10-04 13:26:15 +00:00
* according to ffmpeg docs using 0-size packets.
*
* At least, if we haven't already stopped... */
/* this creates the 0-size packet and prevents a memory leak. */
av_free_packet(&next_packet);
if (!*stop) {
int frame_finished;
do {
frame_finished = 0;
avcodec_decode_video2(context->iCodecCtx, in_frame, &frame_finished, &next_packet);
if (frame_finished) {
index_rebuild_ffmpeg_proc_decoded_frame(context, &next_packet, in_frame);
}
} while (frame_finished);
}
av_free(in_frame);
return 1;
}
#endif
/* ----------------------------------------------------------------------
* - internal AVI (fallback) rebuilder
* ---------------------------------------------------------------------- */
#ifdef WITH_AVI
typedef struct FallbackIndexBuilderContext {
int anim_type;
struct anim *anim;
AviMovie *proxy_ctx[IMB_PROXY_MAX_SLOT];
IMB_Proxy_Size proxy_sizes_in_use;
} FallbackIndexBuilderContext;
2012-05-16 09:26:37 +00:00
static AviMovie *alloc_proxy_output_avi(
struct anim *anim, char *filename, int width, int height, int quality)
{
int x, y;
AviFormat format;
double framerate;
2012-05-16 09:26:37 +00:00
AviMovie *avi;
2019-04-18 07:21:26 +02:00
/* it doesn't really matter for proxies, but sane defaults help anyways...*/
short frs_sec = 25;
float frs_sec_base = 1.0;
IMB_anim_get_fps(anim, &frs_sec, &frs_sec_base, false);
x = width;
y = height;
2012-05-16 09:26:37 +00:00
framerate = (double)frs_sec / (double)frs_sec_base;
2012-05-16 09:26:37 +00:00
avi = MEM_mallocN(sizeof(AviMovie), "avimovie");
format = AVI_FORMAT_MJPEG;
2012-05-16 09:26:37 +00:00
if (AVI_open_compress(filename, avi, 1, format) != AVI_ERROR_NONE) {
MEM_freeN(avi);
return NULL;
}
2012-05-16 09:26:37 +00:00
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);
2012-05-16 09:26:37 +00:00
avi->interlace = 0;
avi->odd_fields = 0;
return avi;
}
static IndexBuildContext *index_fallback_create_context(struct anim *anim,
IMB_Timecode_Type UNUSED(tcs_in_use),
IMB_Proxy_Size proxy_sizes_in_use,
int quality)
{
FallbackIndexBuilderContext *context;
int i;
/* since timecode indices only work with ffmpeg right now,
2012-05-16 09:26:37 +00:00
* don't know a sensible fallback here...
*
* so no proxies...
2012-05-16 09:26:37 +00:00
*/
if (proxy_sizes_in_use == IMB_PROXY_NONE) {
return NULL;
}
context = MEM_callocN(sizeof(FallbackIndexBuilderContext), "fallback index builder context");
context->anim = anim;
context->proxy_sizes_in_use = proxy_sizes_in_use;
memset(context->proxy_ctx, 0, sizeof(context->proxy_ctx));
for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
if (context->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);
context->proxy_ctx[i] = alloc_proxy_output_avi(
2012-05-16 09:26:37 +00:00
anim, fname, anim->x * proxy_fac[i], anim->y * proxy_fac[i], quality);
}
}
return (IndexBuildContext *)context;
}
static void index_rebuild_fallback_finish(FallbackIndexBuilderContext *context, int stop)
{
struct anim *anim = context->anim;
char fname[FILE_MAX];
char fname_tmp[FILE_MAX];
int i;
for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
if (context->proxy_sizes_in_use & proxy_sizes[i]) {
AVI_close_compress(context->proxy_ctx[i]);
MEM_freeN(context->proxy_ctx[i]);
get_proxy_filename(anim, proxy_sizes[i], fname_tmp, true);
get_proxy_filename(anim, proxy_sizes[i], fname, false);
if (stop) {
unlink(fname_tmp);
}
else {
unlink(fname);
rename(fname_tmp, fname);
}
}
}
}
static void index_rebuild_fallback(FallbackIndexBuilderContext *context,
const short *stop,
short *do_update,
float *progress)
{
int cnt = IMB_anim_get_duration(context->anim, IMB_TC_NONE);
int i, pos;
struct anim *anim = context->anim;
for (pos = 0; pos < cnt; pos++) {
struct ImBuf *ibuf = IMB_anim_absolute(anim, pos, IMB_TC_NONE, IMB_PROXY_NONE);
struct ImBuf *tmp_ibuf = IMB_dupImBuf(ibuf);
float next_progress = (float)pos / (float)cnt;
if (*progress != next_progress) {
*progress = next_progress;
*do_update = true;
}
if (*stop) {
break;
}
IMB_flipy(tmp_ibuf);
for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
if (context->proxy_sizes_in_use & proxy_sizes[i]) {
int x = anim->x * proxy_fac[i];
int y = anim->y * proxy_fac[i];
2012-05-16 09:26:37 +00:00
struct ImBuf *s_ibuf = IMB_dupImBuf(tmp_ibuf);
IMB_scalefastImBuf(s_ibuf, x, y);
IMB_convert_rgba_to_abgr(s_ibuf);
2012-05-16 09:26:37 +00:00
AVI_write_frame(context->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);
}
}
IMB_freeImBuf(tmp_ibuf);
IMB_freeImBuf(ibuf);
}
}
#endif /* WITH_AVI */
/* ----------------------------------------------------------------------
* - public API
* ---------------------------------------------------------------------- */
IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim,
IMB_Timecode_Type tcs_in_use,
IMB_Proxy_Size proxy_sizes_in_use,
int quality,
const bool overwrite,
GSet *file_list)
{
IndexBuildContext *context = NULL;
IMB_Proxy_Size proxy_sizes_to_build = proxy_sizes_in_use;
int i;
/* Don't generate the same file twice! */
if (file_list) {
for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
IMB_Proxy_Size proxy_size = proxy_sizes[i];
if (proxy_size & proxy_sizes_to_build) {
char filename[FILE_MAX];
if (get_proxy_filename(anim, proxy_size, filename, false) == false) {
return NULL;
}
void **filename_key_p;
if (!BLI_gset_ensure_p_ex(file_list, filename, &filename_key_p)) {
*filename_key_p = BLI_strdup(filename);
}
else {
proxy_sizes_to_build &= ~proxy_size;
printf("Proxy: %s already registered for generation, skipping\n", filename);
}
}
}
}
if (!overwrite) {
IMB_Proxy_Size built_proxies = IMB_anim_proxy_get_existing(anim);
if (built_proxies != 0) {
for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
IMB_Proxy_Size proxy_size = proxy_sizes[i];
if (proxy_size & built_proxies) {
char filename[FILE_MAX];
if (get_proxy_filename(anim, proxy_size, filename, false) == false) {
return NULL;
}
printf("Skipping proxy: %s\n", filename);
}
}
}
proxy_sizes_to_build &= ~built_proxies;
}
fflush(stdout);
if (proxy_sizes_to_build == 0) {
return NULL;
}
switch (anim->curtype) {
#ifdef WITH_FFMPEG
2012-05-16 09:26:37 +00:00
case ANIM_FFMPEG:
context = index_ffmpeg_create_context(anim, tcs_in_use, proxy_sizes_to_build, quality);
2012-05-16 09:26:37 +00:00
break;
#endif
#ifdef WITH_AVI
2012-05-16 09:26:37 +00:00
default:
context = index_fallback_create_context(anim, tcs_in_use, proxy_sizes_to_build, quality);
2012-05-16 09:26:37 +00:00
break;
#endif
}
2019-04-23 11:01:30 +10:00
if (context) {
context->anim_type = anim->curtype;
2019-04-23 11:01:30 +10:00
}
return context;
UNUSED_VARS(tcs_in_use, proxy_sizes_in_use, quality);
}
void IMB_anim_index_rebuild(struct IndexBuildContext *context,
/* NOLINTNEXTLINE: readability-non-const-parameter. */
short *stop,
/* NOLINTNEXTLINE: readability-non-const-parameter. */
short *do_update,
/* NOLINTNEXTLINE: readability-non-const-parameter. */
float *progress)
{
switch (context->anim_type) {
#ifdef WITH_FFMPEG
2012-05-16 09:26:37 +00:00
case ANIM_FFMPEG:
index_rebuild_ffmpeg((FFmpegIndexBuilderContext *)context, stop, do_update, progress);
break;
#endif
#ifdef WITH_AVI
2012-05-16 09:26:37 +00:00
default:
index_rebuild_fallback((FallbackIndexBuilderContext *)context, stop, do_update, progress);
break;
#endif
}
UNUSED_VARS(stop, do_update, progress);
}
void IMB_anim_index_rebuild_finish(IndexBuildContext *context, short stop)
{
switch (context->anim_type) {
#ifdef WITH_FFMPEG
2012-05-16 09:26:37 +00:00
case ANIM_FFMPEG:
index_rebuild_ffmpeg_finish((FFmpegIndexBuilderContext *)context, stop);
break;
#endif
#ifdef WITH_AVI
2012-05-16 09:26:37 +00:00
default:
index_rebuild_fallback_finish((FallbackIndexBuilderContext *)context, stop);
break;
#endif
}
/* static defined at top of the file */
UNUSED_VARS(stop, proxy_sizes);
}
2012-05-16 09:26:37 +00:00
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;
}
2012-05-16 09:26:37 +00:00
void IMB_anim_set_index_dir(struct anim *anim, const char *dir)
{
if (STREQ(anim->index_dir, dir)) {
return;
}
BLI_strncpy(anim->index_dir, dir, sizeof(anim->index_dir));
IMB_free_indices(anim);
}
2012-05-16 09:26:37 +00:00
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);
/* proxies are generated in the same color space as animation itself */
anim->proxy_anim[i] = IMB_open_anim(fname, 0, 0, anim->colorspace);
anim->proxies_tried |= preview_size;
return anim->proxy_anim[i];
}
2012-05-16 09:26:37 +00:00
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];
}
2012-05-16 09:26:37 +00:00
int IMB_anim_index_get_frame_index(struct anim *anim, IMB_Timecode_Type tc, int position)
{
2012-05-16 09:26:37 +00:00
struct anim_index *idx = IMB_anim_open_index(anim, tc);
if (!idx) {
return position;
}
return IMB_indexer_get_frame_index(idx, position);
}
IMB_Proxy_Size IMB_anim_proxy_get_existing(struct anim *anim)
{
const int num_proxy_sizes = IMB_PROXY_MAX_SLOT;
IMB_Proxy_Size existing = 0;
int i;
for (i = 0; i < num_proxy_sizes; i++) {
IMB_Proxy_Size proxy_size = proxy_sizes[i];
char filename[FILE_MAX];
get_proxy_filename(anim, proxy_size, filename, false);
if (BLI_exists(filename)) {
existing |= proxy_size;
}
}
return existing;
}