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 6855449550
commit fb5193ab62
3 changed files with 62 additions and 14 deletions

View file

@ -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;

View file

@ -13,12 +13,27 @@
#include <libavcodec/avcodec.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_write(void *opaque, const uint8_t *buf, int buf_size);
int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence);
@interface FFMPEGDecoder : NSObject <CogDecoder> {
id<CogSource> source;
FFMPEGReader *reader;
BOOL seekable;
int channels;
uint32_t channelConfig;

View file

@ -19,9 +19,42 @@
#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) {
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<CogSource> 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;