From 814f65f83027f3213e83ca86707e558c7a0addec Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 26 Feb 2025 21:20:27 -0800 Subject: [PATCH] FFMPEG: Optimize file reader access Improve handling where FFmpeg may call the provided file reader with AVSEEK_SIZE repeatedly, when file size is not likely to change between repeated calls. This prevents repeated seek operations that would otherwise be required to probe the file size each time. Signed-off-by: Christopher Snowhill --- Plugins/FFMPEG/FFMPEGContainer.m | 6 +++- Plugins/FFMPEG/FFMPEGDecoder.h | 15 +++++++++ Plugins/FFMPEG/FFMPEGDecoder.m | 55 ++++++++++++++++++++++++-------- 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/Plugins/FFMPEG/FFMPEGContainer.m b/Plugins/FFMPEG/FFMPEGContainer.m index 002dd7b47..d10dbb04c 100644 --- a/Plugins/FFMPEG/FFMPEGContainer.m +++ b/Plugins/FFMPEG/FFMPEGContainer.m @@ -51,6 +51,8 @@ uint8_t *buffer = NULL; + FFMPEGReader *reader = nil; + // register all available codecs if(([[url scheme] isEqualToString:@"http"] || @@ -80,7 +82,9 @@ goto exit; } - ioCtx = avio_alloc_context(buffer, 32 * 1024, 0, (__bridge void *)source, ffmpeg_read, ffmpeg_write, ffmpeg_seek); + reader = [[FFMPEGReader alloc] initWithFile:source]; + + ioCtx = avio_alloc_context(buffer, 32 * 1024, 0, (__bridge void *)reader, ffmpeg_read, ffmpeg_write, ffmpeg_seek); if(!ioCtx) { ALog(@"Unable to create AVIO context"); goto exit; diff --git a/Plugins/FFMPEG/FFMPEGDecoder.h b/Plugins/FFMPEG/FFMPEGDecoder.h index 33570c237..82d43cf79 100644 --- a/Plugins/FFMPEG/FFMPEGDecoder.h +++ b/Plugins/FFMPEG/FFMPEGDecoder.h @@ -13,12 +13,27 @@ #include #include +@interface FFMPEGReader : NSObject { + id file; + + BOOL cachedSize; + int64_t size; +} + +- (id)initWithFile:(id)f; + +- (id)file; +- (int64_t)size; + +@end + extern int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size); extern int ffmpeg_write(void *opaque, const uint8_t *buf, int buf_size); int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence); @interface FFMPEGDecoder : NSObject { id source; + FFMPEGReader *reader; BOOL seekable; int channels; uint32_t channelConfig; diff --git a/Plugins/FFMPEG/FFMPEGDecoder.m b/Plugins/FFMPEG/FFMPEGDecoder.m index 8e604a997..c92ef0641 100644 --- a/Plugins/FFMPEG/FFMPEGDecoder.m +++ b/Plugins/FFMPEG/FFMPEGDecoder.m @@ -19,9 +19,42 @@ #define ST_BUFF 2048 +@implementation FFMPEGReader + +- (id)initWithFile:(id)f { + self = [super init]; + if(self) { + file = f; + cachedSize = NO; + size = 0; + } + return self; +} + +- (id)file { + return file; +} + +- (int64_t)size { + if(!cachedSize) { + if([file seekable]) { + int64_t curOffset = [file tell]; + [file seek:0 whence:SEEK_END]; + size = [file tell]; + [file seek:curOffset whence:SEEK_SET]; + } else { + size = -1; + } + cachedSize = YES; + } + return size; +} + +@end + int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size) { - id source = (__bridge id)opaque; - long sizeRead = [source read:buf amount:buf_size]; + FFMPEGReader *source = (__bridge FFMPEGReader*)opaque; + long sizeRead = [[source file] read:buf amount:buf_size]; if(sizeRead == 0) return AVERROR_EOF; return (int)sizeRead; } @@ -31,19 +64,13 @@ int ffmpeg_write(void *opaque, const uint8_t *buf, int buf_size) { } int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { - id source = (__bridge id)opaque; + FFMPEGReader *source = (__bridge FFMPEGReader*)opaque; if(whence & AVSEEK_SIZE) { - if([source seekable]) { - int64_t curOffset = [source tell]; - [source seek:0 whence:SEEK_END]; - int64_t size = [source tell]; - [source seek:curOffset whence:SEEK_SET]; - return size; - } - return -1; + return [source size]; } whence &= ~(AVSEEK_SIZE | AVSEEK_FORCE); - return [source seekable] ? ([source seek:offset whence:whence] ? [source tell] : -1) : -1; + id file = [source file]; + return [file seekable] ? ([file seek:offset whence:whence] ? [file tell] : -1) : -1; } @implementation FFMPEGDecoder @@ -131,7 +158,9 @@ static uint8_t reverse_bits[0x100]; return NO; } - ioCtx = avio_alloc_context(buffer, 32 * 1024, 0, (__bridge void *)source, ffmpeg_read, ffmpeg_write, ffmpeg_seek); + reader = [[FFMPEGReader alloc] initWithFile:source]; + + ioCtx = avio_alloc_context(buffer, 32 * 1024, 0, (__bridge void *)reader, ffmpeg_read, ffmpeg_write, ffmpeg_seek); if(!ioCtx) { ALog(@"Unable to create AVIO context"); return NO;