简单帧数据封包缓存处理
- 只在极端情况下才落盘(绝大多数帧在内存中,只有内存压力大/阻塞未推流的帧才写入 mmap 文件)。
- 文件只追加写入(写入不会覆盖),读指针始终向后顺序读取。
- 文件空间不够就自动扩容(比如翻倍增长)。
- 支持跨页、缺页自动处理。
- 只需简单的线程安全(比如用串行队列或简单加锁)。
#import <Foundation/Foundation.h>
typedef NS_ENUM(uint32_t, LFFrameType) {
LFFrameTypeAudio = 0,
LFFrameTypeVideo = 1,
};
typedef struct {
uint32_t magic; // 帧标识, 如 0xAA55AA55
uint32_t type; // 0:音频帧 1:视频帧
uint32_t length; // 帧数据长度
uint64_t timestamp; // 时间戳
} LFFrameHeader;
@interface LFFrameMmapSimpleCache : NSObject
- (instancetype)initWithPath:(NSString *)path initialSize:(size_t)size;
- (BOOL)writeFrame:(NSData *)frameData type:(LFFrameType)type timestamp:(uint64_t)timestamp;
- (BOOL)readNextFrame:(LFFrameHeader *)outHeader data:(NSData **)outData;
- (void)close;
@end
#import "LFFrameMmapSimpleCache.h"
#import <sys/mman.h>
#import <fcntl.h>
#import <unistd.h>
#define LF_FRAME_MAGIC 0xAA55AA55
@interface LFFrameMmapSimpleCache () {
int _fd;
void *_mmapPtr;
size_t _mmapSize;
size_t _writeOffset;
size_t _readOffset;
}
@property (nonatomic, strong) NSString *filePath;
@end
@implementation LFFrameMmapSimpleCache
- (instancetype)initWithPath:(NSString *)path initialSize:(size_t)size {
self = [super init];
if (self) {
_filePath = [path copy];
_mmapSize = size;
_writeOffset = 0;
_readOffset = 0;
_fd = open([path UTF8String], O_RDWR | O_CREAT, 0666);
if (_fd < 0) {
NSLog(@"open file failed");
return nil;
}
ftruncate(_fd, size);
_mmapPtr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0);
if (_mmapPtr == MAP_FAILED) {
NSLog(@"mmap failed");
close(_fd);
return nil;
}
}
return self;
}
- (void)dealloc { [self close]; }
- (void)close {
@synchronized (self) {
if (_mmapPtr && _mmapPtr != MAP_FAILED) {
msync(_mmapPtr, _mmapSize, MS_SYNC);
munmap(_mmapPtr, _mmapSize);
_mmapPtr = NULL;
}
if (_fd > 0) {
close(_fd);
_fd = -1;
}
}
}
// 扩容映射文件(文件大小翻倍)
- (BOOL)expandFileIfNeeded:(size_t)needMore {
if (_writeOffset + needMore <= _mmapSize) return YES;
size_t newSize = _mmapSize;
while (_writeOffset + needMore > newSize) newSize *= 2;
if (ftruncate(_fd, newSize) != 0) {
NSLog(@"ftruncate failed");
return NO;
}
void *newPtr = mremap(_mmapPtr, _mmapSize, newSize, MREMAP_MAYMOVE);
if (newPtr == MAP_FAILED) {
NSLog(@"mremap failed");
return NO;
}
_mmapPtr = newPtr;
_mmapSize = newSize;
return YES;
}
- (BOOL)writeFrame:(NSData *)frameData type:(LFFrameType)type timestamp:(uint64_t)timestamp {
@synchronized (self) {
size_t frameLen = frameData.length;
size_t totalLen = sizeof(LFFrameHeader) + frameLen;
if (![self expandFileIfNeeded:totalLen]) return NO;
LFFrameHeader header = {LF_FRAME_MAGIC, type, (uint32_t)frameLen, timestamp};
memcpy((Byte *)_mmapPtr + _writeOffset, &header, sizeof(header));
memcpy((Byte *)_mmapPtr + _writeOffset + sizeof(header), frameData.bytes, frameLen);
_writeOffset += totalLen;
return YES;
}
}
- (BOOL)readNextFrame:(LFFrameHeader *)outHeader data:(NSData **)outData {
@synchronized (self) {
if (_readOffset + sizeof(LFFrameHeader) > _writeOffset) return NO;
LFFrameHeader header = {0};
memcpy(&header, (Byte *)_mmapPtr + _readOffset, sizeof(header));
if (header.magic != LF_FRAME_MAGIC) {
NSLog(@"Frame header corrupted");
return NO;
}
if (_readOffset + sizeof(LFFrameHeader) + header.length > _writeOffset) return NO;
if (outHeader) *outHeader = header;
if (outData) {
NSData *data = [NSData dataWithBytes:(Byte *)_mmapPtr + _readOffset + sizeof(LFFrameHeader) length:header.length];
*outData = data;
}
_readOffset += sizeof(LFFrameHeader) + header.length;
return YES;
}
}
@end
#import <Foundation/Foundation.h>
#import "LFFrameMmapSimpleCache.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
LFFrameMmapSimpleCache *cache = [[LFFrameMmapSimpleCache alloc] initWithPath:@"/tmp/lf_simple_mmap" initialSize:4096];
// 写入测试
for (int i=0; i<100; ++i) {
NSString *str = [NSString stringWithFormat:@"FrameData-%d", i];
NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
[cache writeFrame:data type:LFFrameTypeVideo timestamp:1000+i*10];
}
// 读取测试
LFFrameHeader header;
NSData *frameData;
int idx = 0;
while ([cache readNextFrame:&header data:&frameData]) {
NSString *content = [[NSString alloc] initWithData:frameData encoding:NSUTF8StringEncoding];
NSLog(@"Read frame %d: ts=%llu, content=%@", idx++, header.timestamp, content);
}
[cache close];
}
return 0;
}
未经作者授权,禁止转载
本文来自博客园,作者:CoderWGB,转载请注明原文链接:https://www.cnblogs.com/wgb1234/articles/18952990
THE END

浙公网安备 33010602011771号