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 <kode54@gmail.com>
This commit is contained in:
Christopher Snowhill 2025-02-26 21:20:27 -08:00
parent d6cd240de6
commit 814f65f830
3 changed files with 62 additions and 14 deletions

View file

@ -51,6 +51,8 @@
uint8_t *buffer = NULL; uint8_t *buffer = NULL;
FFMPEGReader *reader = nil;
// register all available codecs // register all available codecs
if(([[url scheme] isEqualToString:@"http"] || if(([[url scheme] isEqualToString:@"http"] ||
@ -80,7 +82,9 @@
goto exit; 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) { if(!ioCtx) {
ALog(@"Unable to create AVIO context"); ALog(@"Unable to create AVIO context");
goto exit; goto exit;

View file

@ -13,12 +13,27 @@
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
@interface FFMPEGReader : NSObject {
id<CogSource> file;
BOOL cachedSize;
int64_t size;
}
- (id)initWithFile:(id<CogSource>)f;
- (id<CogSource>)file;
- (int64_t)size;
@end
extern int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size); 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); extern int ffmpeg_write(void *opaque, const uint8_t *buf, int buf_size);
int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence); int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence);
@interface FFMPEGDecoder : NSObject <CogDecoder> { @interface FFMPEGDecoder : NSObject <CogDecoder> {
id<CogSource> source; id<CogSource> source;
FFMPEGReader *reader;
BOOL seekable; BOOL seekable;
int channels; int channels;
uint32_t channelConfig; uint32_t channelConfig;

View file

@ -19,9 +19,42 @@
#define ST_BUFF 2048 #define ST_BUFF 2048
@implementation FFMPEGReader
- (id)initWithFile:(id<CogSource>)f {
self = [super init];
if(self) {
file = f;
cachedSize = NO;
size = 0;
}
return self;
}
- (id<CogSource>)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) { int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size) {
id source = (__bridge id)opaque; FFMPEGReader *source = (__bridge FFMPEGReader*)opaque;
long sizeRead = [source read:buf amount:buf_size]; long sizeRead = [[source file] read:buf amount:buf_size];
if(sizeRead == 0) return AVERROR_EOF; if(sizeRead == 0) return AVERROR_EOF;
return (int)sizeRead; 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) { 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(whence & AVSEEK_SIZE) {
if([source seekable]) { return [source size];
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;
} }
whence &= ~(AVSEEK_SIZE | AVSEEK_FORCE); whence &= ~(AVSEEK_SIZE | AVSEEK_FORCE);
return [source seekable] ? ([source seek:offset whence:whence] ? [source tell] : -1) : -1; id<CogSource> file = [source file];
return [file seekable] ? ([file seek:offset whence:whence] ? [file tell] : -1) : -1;
} }
@implementation FFMPEGDecoder @implementation FFMPEGDecoder
@ -131,7 +158,9 @@ static uint8_t reverse_bits[0x100];
return NO; 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) { if(!ioCtx) {
ALog(@"Unable to create AVIO context"); ALog(@"Unable to create AVIO context");
return NO; return NO;