VSE: Reduce playback stalls when new video clips start playing #118503

Merged
Aras Pranckevicius merged 7 commits from aras_p/blender:speedup_isffmpeg into main 2024-02-22 09:24:45 +01:00
5 changed files with 66 additions and 248 deletions

View File

@ -333,7 +333,6 @@ void IMB_close_anim(ImBufAnim *anim);
void IMB_close_anim_proxies(ImBufAnim *anim);
bool IMB_anim_can_produce_frames(const ImBufAnim *anim);
int ismovie(const char *filepath);
int IMB_anim_get_image_width(ImBufAnim *anim);
int IMB_anim_get_image_height(ImBufAnim *anim);
bool IMB_get_gop_decode_time(ImBufAnim *anim);
@ -397,12 +396,12 @@ bool IMB_ispic_type_matches(const char *filepath, int filetype);
int IMB_ispic_type_from_memory(const unsigned char *buf, size_t buf_size);
int IMB_ispic_type(const char *filepath);
enum class ImbAnimType { NotAnim, Sequence, Movie, Ffmpeg };
/**
* Test if the file is a video file (known format, has a video stream and
* supported video codec).
*/
bool IMB_isanim(const char *filepath);
ImbAnimType imb_get_anim_type(const char *filepath);
/**
* Test if color-space conversions of pixels in buffer need to take into account alpha.
*/

View File

@ -8,36 +8,26 @@
#pragma once
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <cstdint>
#ifdef _WIN32
# include <io.h>
#else
# include <dirent.h>
#endif
#include "imbuf.hh"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "IMB_imbuf_enums.h"
#ifdef WITH_FFMPEG
extern "C" {
# include <libavcodec/avcodec.h>
# include <libavformat/avformat.h>
# include <libswscale/swscale.h>
}
struct AVFormatContext;
struct AVCodecContext;
struct AVCodec;
struct AVFrame;
struct AVPacket;
struct SwsContext;
#endif
struct IDProperty;
struct ImBufAnimIndex;
struct ImBufAnim {
enum class State { Uninitialized, Failed, Valid };
int ib_flags;
ImbAnimType curtype;
State state;
int cur_position; /* index 0 = 1e, 1 = 2e, enz. */
int duration_in_frames;
int frs_sec;
@ -47,16 +37,7 @@ struct ImBufAnim {
/* for number */
char filepath[1024];
/* for sequence */
char filepath_first[1024];
/* movie */
void *movie;
void *track;
void *params;
int orientation;
size_t framesize;
int interlacing;
int streamindex;
#ifdef WITH_FFMPEG
@ -65,7 +46,7 @@ struct ImBufAnim {
const AVCodec *pCodec;
AVFrame *pFrameRGB;
AVFrame *pFrameDeinterlaced;
struct SwsContext *img_convert_ctx;
SwsContext *img_convert_ctx;
int videoStream;
AVFrame *pFrame;
@ -85,11 +66,11 @@ struct ImBufAnim {
int proxies_tried;
int indices_tried;
struct ImBufAnim *proxy_anim[IMB_PROXY_MAX_SLOT];
struct ImBufAnimIndex *curr_idx[IMB_TC_MAX_SLOT];
ImBufAnim *proxy_anim[IMB_PROXY_MAX_SLOT];
ImBufAnimIndex *curr_idx[IMB_TC_MAX_SLOT];
char colorspace[64];
char suffix[64]; /* MAX_NAME - multiview */
struct IDProperty *metadata;
IDProperty *metadata;
};

View File

@ -52,25 +52,6 @@ extern "C" {
#endif /* WITH_FFMPEG */
int ismovie(const char * /*filepath*/)
{
return 0;
}
/* never called, just keep the linker happy */
static int startmovie(ImBufAnim * /*anim*/)
{
return 1;
}
static ImBuf *movie_fetchibuf(ImBufAnim * /*anim*/, int /*position*/)
{
return nullptr;
}
static void free_anim_movie(ImBufAnim * /*anim*/)
{
/* pass */
}
#ifdef WITH_FFMPEG
static void free_anim_ffmpeg(ImBufAnim *anim);
#endif
@ -82,8 +63,6 @@ void IMB_free_anim(ImBufAnim *anim)
return;
}
free_anim_movie(anim);
#ifdef WITH_FFMPEG
free_anim_ffmpeg(anim);
#endif
@ -113,34 +92,23 @@ void IMB_close_anim_proxies(ImBufAnim *anim)
IDProperty *IMB_anim_load_metadata(ImBufAnim *anim)
{
switch (anim->curtype) {
case ImbAnimType::Ffmpeg: {
if (anim->state == ImBufAnim::State::Valid) {
#ifdef WITH_FFMPEG
AVDictionaryEntry *entry = nullptr;
BLI_assert(anim->pFormatCtx != nullptr);
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "METADATA FETCH\n");
BLI_assert(anim->pFormatCtx != nullptr);
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "METADATA FETCH\n");
while (true) {
entry = av_dict_get(anim->pFormatCtx->metadata, "", entry, AV_DICT_IGNORE_SUFFIX);
if (entry == nullptr) {
break;
}
/* Delay creation of the property group until there is actual metadata to put in there. */
IMB_metadata_ensure(&anim->metadata);
IMB_metadata_set_field(anim->metadata, entry->key, entry->value);
AVDictionaryEntry *entry = nullptr;
while (true) {
entry = av_dict_get(anim->pFormatCtx->metadata, "", entry, AV_DICT_IGNORE_SUFFIX);
if (entry == nullptr) {
break;
}
#endif
break;
/* Delay creation of the property group until there is actual metadata to put in there. */
IMB_metadata_ensure(&anim->metadata);
IMB_metadata_set_field(anim->metadata, entry->key, entry->value);
}
case ImbAnimType::Sequence:
case ImbAnimType::Movie:
/* TODO */
break;
case ImbAnimType::NotAnim:
default:
break;
#endif
}
return anim->metadata;
}
@ -376,8 +344,6 @@ static int startffmpeg(ImBufAnim *anim)
* starts. */
anim->start_offset = video_start;
anim->params = nullptr;
anim->x = pCodecCtx->width;
anim->y = pCodecCtx->height;
@ -386,10 +352,6 @@ static int startffmpeg(ImBufAnim *anim)
anim->pCodec = pCodec;
anim->videoStream = video_stream_index;
anim->interlacing = 0;
anim->orientation = 0;
anim->framesize = anim->x * anim->y * 4;
anim->cur_position = 0;
anim->cur_pts = -1;
anim->cur_key_frame_pts = -1;
@ -1234,48 +1196,21 @@ static void free_anim_ffmpeg(ImBufAnim *anim)
*/
static bool anim_getnew(ImBufAnim *anim)
{
BLI_assert(anim->curtype == ImbAnimType::NotAnim);
if (anim == nullptr) {
/* Nothing to initialize. */
return false;
}
free_anim_movie(anim);
BLI_assert(anim->state == ImBufAnim::State::Uninitialized);
#ifdef WITH_FFMPEG
free_anim_ffmpeg(anim);
#endif
anim->curtype = imb_get_anim_type(anim->filepath);
switch (anim->curtype) {
case ImbAnimType::Sequence: {
ImBuf *ibuf = IMB_loadiffname(anim->filepath, anim->ib_flags, anim->colorspace);
if (ibuf) {
STRNCPY(anim->filepath_first, anim->filepath);
anim->duration_in_frames = 1;
IMB_freeImBuf(ibuf);
}
else {
return false;
}
break;
}
case ImbAnimType::Movie:
if (startmovie(anim)) {
return false;
}
break;
#ifdef WITH_FFMPEG
case ImbAnimType::Ffmpeg:
if (startffmpeg(anim)) {
return false;
}
break;
#endif
default:
break;
if (startffmpeg(anim)) {
anim->state = ImBufAnim::State::Failed;
return false;
}
#endif
anim->state = ImBufAnim::State::Valid;
return true;
}
@ -1300,7 +1235,7 @@ ImBuf *IMB_anim_previewframe(ImBufAnim *anim)
IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Frames", value);
#ifdef WITH_FFMPEG
if (anim->pFormatCtx && anim->curtype == ImbAnimType::Ffmpeg) {
if (anim->pFormatCtx) {
AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
AVRational frame_rate = av_guess_frame_rate(anim->pFormatCtx, v_st, nullptr);
if (frame_rate.num != 0) {
@ -1323,15 +1258,12 @@ ImBuf *IMB_anim_absolute(ImBufAnim *anim,
IMB_Proxy_Size preview_size)
{
ImBuf *ibuf = nullptr;
int filter_y;
if (anim == nullptr) {
return nullptr;
}
filter_y = (anim->ib_flags & IB_animdeinterlace);
if (preview_size == IMB_PROXY_NONE) {
if (anim->curtype == ImbAnimType::NotAnim) {
if (anim->state == ImBufAnim::State::Uninitialized) {
if (!anim_getnew(anim)) {
return nullptr;
}
@ -1354,45 +1286,16 @@ ImBuf *IMB_anim_absolute(ImBufAnim *anim,
}
}
switch (anim->curtype) {
case ImbAnimType::Sequence: {
constexpr size_t filepath_size = BOUNDED_ARRAY_TYPE_SIZE<decltype(anim->filepath_first)>();
char head[filepath_size], tail[filepath_size];
ushort digits;
const int pic = BLI_path_sequence_decode(
anim->filepath_first, head, sizeof(head), tail, sizeof(tail), &digits) +
position;
BLI_path_sequence_encode(anim->filepath, sizeof(anim->filepath), head, tail, digits, pic);
ibuf = IMB_loadiffname(anim->filepath, IB_rect, anim->colorspace);
if (ibuf) {
anim->cur_position = position;
}
break;
}
case ImbAnimType::Movie:
ibuf = movie_fetchibuf(anim, position);
if (ibuf) {
anim->cur_position = position;
IMB_convert_rgba_to_abgr(ibuf);
}
break;
#ifdef WITH_FFMPEG
case ImbAnimType::Ffmpeg:
ibuf = ffmpeg_fetchibuf(anim, position, tc);
if (ibuf) {
anim->cur_position = position;
}
filter_y = 0; /* done internally */
break;
#endif
default:
break;
if (anim->state == ImBufAnim::State::Valid) {
ibuf = ffmpeg_fetchibuf(anim, position, tc);
if (ibuf) {
anim->cur_position = position;
}
}
#endif
if (ibuf) {
if (filter_y) {
IMB_filtery(ibuf);
}
SNPRINTF(ibuf->filepath, "%s.%04d", anim->filepath, anim->cur_position + 1);
}
return ibuf;

View File

@ -458,9 +458,7 @@ static void get_tc_filepath(ImBufAnim *anim, IMB_Timecode_Type tc, char *filepat
* - common rebuilder structures
* ---------------------------------------------------------------------- */
struct IndexBuildContext {
ImbAnimType anim_type;
};
struct IndexBuildContext {};
/* ----------------------------------------------------------------------
* - ffmpeg rebuilder
@ -786,8 +784,7 @@ static void free_proxy_output_ffmpeg(proxy_output_ctx *ctx, int rollback)
MEM_freeN(ctx);
}
struct FFmpegIndexBuilderContext {
int anim_type;
struct FFmpegIndexBuilderContext : public IndexBuildContext {
AVFormatContext *iFormatCtx;
AVCodecContext *iCodecCtx;
@ -1248,7 +1245,6 @@ IndexBuildContext *IMB_anim_index_rebuild_context(ImBufAnim *anim,
GSet *file_list,
bool build_only_on_bad_performance)
{
IndexBuildContext *context = nullptr;
int proxy_sizes_to_build = proxy_sizes_in_use;
int i;
@ -1297,24 +1293,16 @@ IndexBuildContext *IMB_anim_index_rebuild_context(ImBufAnim *anim,
return nullptr;
}
switch (anim->curtype) {
IndexBuildContext *context = nullptr;
#ifdef WITH_FFMPEG
case ImbAnimType::Ffmpeg:
context = index_ffmpeg_create_context(
anim, tcs_in_use, proxy_sizes_to_build, quality, build_only_on_bad_performance);
break;
if (anim->state == ImBufAnim::State::Valid) {
context = index_ffmpeg_create_context(
anim, tcs_in_use, proxy_sizes_to_build, quality, build_only_on_bad_performance);
}
#else
UNUSED_VARS(build_only_on_bad_performance);
UNUSED_VARS(build_only_on_bad_performance);
#endif
default:
break;
}
if (context) {
context->anim_type = anim->curtype;
}
return context;
UNUSED_VARS(tcs_in_use, proxy_sizes_in_use, quality);
@ -1328,33 +1316,23 @@ void IMB_anim_index_rebuild(IndexBuildContext *context,
/* NOLINTNEXTLINE: readability-non-const-parameter. */
float *progress)
{
switch (context->anim_type) {
#ifdef WITH_FFMPEG
case ImbAnimType::Ffmpeg:
if (indexer_need_to_build_proxy((FFmpegIndexBuilderContext *)context)) {
index_rebuild_ffmpeg((FFmpegIndexBuilderContext *)context, stop, do_update, progress);
}
break;
#endif
default:
break;
if (context != nullptr) {
if (indexer_need_to_build_proxy((FFmpegIndexBuilderContext *)context)) {
index_rebuild_ffmpeg((FFmpegIndexBuilderContext *)context, stop, do_update, progress);
}
}
#endif
UNUSED_VARS(stop, do_update, progress);
}
void IMB_anim_index_rebuild_finish(IndexBuildContext *context, const bool stop)
{
switch (context->anim_type) {
#ifdef WITH_FFMPEG
case ImbAnimType::Ffmpeg:
index_rebuild_ffmpeg_finish((FFmpegIndexBuilderContext *)context, stop);
break;
#endif
default:
break;
if (context != nullptr) {
index_rebuild_ffmpeg_finish((FFmpegIndexBuilderContext *)context, stop);
}
#endif
/* static defined at top of the file */
UNUSED_VARS(stop, proxy_sizes);
}

View File

@ -299,62 +299,19 @@ static int isffmpeg(const char *filepath)
}
#endif
ImbAnimType imb_get_anim_type(const char *filepath)
bool IMB_isanim(const char *filepath)
{
BLI_stat_t st;
BLI_assert(!BLI_path_is_rel(filepath));
if (UTIL_DEBUG) {
printf("%s: %s\n", __func__, filepath);
}
#ifndef _WIN32
# ifdef WITH_FFMPEG
/* stat test below fails on large files > 4GB */
#ifdef WITH_FFMPEG
if (isffmpeg(filepath)) {
return ImbAnimType::Ffmpeg;
}
# endif
if (BLI_stat(filepath, &st) == -1) {
return ImbAnimType::NotAnim;
}
if (((st.st_mode) & S_IFMT) != S_IFREG) {
return ImbAnimType::NotAnim;
return true;
}
#endif
if (ismovie(filepath)) {
return ImbAnimType::Movie;
}
#else /* !_WIN32 */
if (BLI_stat(filepath, &st) == -1) {
return ImbAnimType::NotAnim;
}
if (((st.st_mode) & S_IFMT) != S_IFREG) {
return ImbAnimType::NotAnim;
}
if (ismovie(filepath)) {
return ImbAnimType::Movie;
}
# ifdef WITH_FFMPEG
if (isffmpeg(filepath)) {
return ImbAnimType::Ffmpeg;
}
# endif
#endif /* !_WIN32 */
/* Assume a single image is part of an image sequence. */
if (IMB_ispic(filepath)) {
return ImbAnimType::Sequence;
}
return ImbAnimType::NotAnim;
}
bool IMB_isanim(const char *filepath)
{
ImbAnimType type = imb_get_anim_type(filepath);
return !ELEM(type, ImbAnimType::NotAnim, ImbAnimType::Sequence);
return false;
}