From d7d583c3ba749d8b7edb179cca5007030a61cab3 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Mon, 24 Feb 2014 17:05:54 +0000 Subject: [PATCH] Some chunking/paging buffering data source work --- .../StreamingKit.xcodeproj/project.pbxproj | 6 + StreamingKit/StreamingKit/STKBufferChunk.h | 22 +++ StreamingKit/StreamingKit/STKBufferChunk.m | 30 ++++ .../StreamingKit/STKBufferingDataSource.m | 158 ++++++++++-------- 4 files changed, 143 insertions(+), 73 deletions(-) create mode 100644 StreamingKit/StreamingKit/STKBufferChunk.h create mode 100644 StreamingKit/StreamingKit/STKBufferChunk.m diff --git a/StreamingKit/StreamingKit.xcodeproj/project.pbxproj b/StreamingKit/StreamingKit.xcodeproj/project.pbxproj index 354fa9d..d6633db 100644 --- a/StreamingKit/StreamingKit.xcodeproj/project.pbxproj +++ b/StreamingKit/StreamingKit.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ A1682FA318B3903900F29FEC /* STKBufferingDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1682FA218B3903900F29FEC /* STKBufferingDataSource.m */; }; + A168C6F118BB67DC003D170D /* STKBufferChunk.m in Sources */ = {isa = PBXBuildFile; fileRef = A168C6F018BB67DC003D170D /* STKBufferChunk.m */; }; A1A4996B189E744400E2A2E2 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A4996A189E744400E2A2E2 /* Cocoa.framework */; }; A1A49975189E744500E2A2E2 /* StreamingKitMac.m in Sources */ = {isa = PBXBuildFile; fileRef = A1A49974189E744500E2A2E2 /* StreamingKitMac.m */; }; A1A4997B189E744500E2A2E2 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4D9188D57F60010896F /* XCTest.framework */; }; @@ -86,6 +87,8 @@ /* Begin PBXFileReference section */ A1682FA118B3903900F29FEC /* STKBufferingDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKBufferingDataSource.h; sourceTree = ""; }; A1682FA218B3903900F29FEC /* STKBufferingDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKBufferingDataSource.m; sourceTree = ""; }; + A168C6EF18BB67DC003D170D /* STKBufferChunk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKBufferChunk.h; sourceTree = ""; }; + A168C6F018BB67DC003D170D /* STKBufferChunk.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKBufferChunk.m; sourceTree = ""; }; A1A49969189E744400E2A2E2 /* libStreamingKitMac.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libStreamingKitMac.a; sourceTree = BUILT_PRODUCTS_DIR; }; A1A4996A189E744400E2A2E2 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; }; A1A4996D189E744500E2A2E2 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -269,6 +272,8 @@ A1E7C4F2188D5E550010896F /* STKAudioPlayer.m */, A1E7C4F3188D5E550010896F /* STKAutoRecoveringHTTPDataSource.h */, A1E7C4F4188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m */, + A168C6EF18BB67DC003D170D /* STKBufferChunk.h */, + A168C6F018BB67DC003D170D /* STKBufferChunk.m */, A1682FA118B3903900F29FEC /* STKBufferingDataSource.h */, A1682FA218B3903900F29FEC /* STKBufferingDataSource.m */, A1E7C4F5188D5E550010896F /* STKCoreFoundationDataSource.h */, @@ -509,6 +514,7 @@ A1E7C503188D5E550010896F /* STKDataSourceWrapper.m in Sources */, A1682FA318B3903900F29FEC /* STKBufferingDataSource.m in Sources */, A1E7C502188D5E550010896F /* STKDataSource.m in Sources */, + A168C6F118BB67DC003D170D /* STKBufferChunk.m in Sources */, A1BF65D5189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m in Sources */, A1E7C500188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m in Sources */, ); diff --git a/StreamingKit/StreamingKit/STKBufferChunk.h b/StreamingKit/StreamingKit/STKBufferChunk.h new file mode 100644 index 0000000..554a0e7 --- /dev/null +++ b/StreamingKit/StreamingKit/STKBufferChunk.h @@ -0,0 +1,22 @@ +// +// STKBufferChunk.h +// StreamingKit +// +// Created by Thong Nguyen on 24/02/2014. +// Copyright (c) 2014 Thong Nguyen. All rights reserved. +// + +#import + +@interface STKBufferChunk : NSObject +{ +@public + UInt32 key; + UInt32 size; + UInt32 position; + UInt8* buffer; +} + +-(id) initWithBufferSize:(UInt32)sizeIn; + +@end diff --git a/StreamingKit/StreamingKit/STKBufferChunk.m b/StreamingKit/StreamingKit/STKBufferChunk.m new file mode 100644 index 0000000..575e351 --- /dev/null +++ b/StreamingKit/StreamingKit/STKBufferChunk.m @@ -0,0 +1,30 @@ +// +// STKBufferChunk.m +// StreamingKit +// +// Created by Thong Nguyen on 24/02/2014. +// Copyright (c) 2014 Thong Nguyen. All rights reserved. +// + +#import "STKBufferChunk.h" + +@implementation STKBufferChunk + +-(id) initWithBufferSize:(UInt32)sizeIn +{ + if (self = [super init]) + { + self->size = sizeIn; + + self->buffer = calloc(sizeof(UInt8), sizeIn); + } + + return self; +} + +-(void) dealloc +{ + free(self->buffer); +} + +@end diff --git a/StreamingKit/StreamingKit/STKBufferingDataSource.m b/StreamingKit/StreamingKit/STKBufferingDataSource.m index 5a4a06d..c91225a 100644 --- a/StreamingKit/StreamingKit/STKBufferingDataSource.m +++ b/StreamingKit/StreamingKit/STKBufferingDataSource.m @@ -33,17 +33,22 @@ **********************************************************************************/ #import "STKBufferingDataSource.h" +#import "STKBufferChunk.h" +#import + +#define STK_BUFFER_CHUNK_SIZE (128 * 1024) @interface STKBufferingDataSource() { @private NSRunLoop* runLoop; - int bufferStartIndex; - int bufferStartFileOffset; - int bufferBytesUsed; - int bufferBytesTotal; + SInt32 maxSize; + UInt32 chunkSize; + UInt32 chunkCount; SInt64 position; - uint8_t* buffer; + pthread_mutex_t mutex; + pthread_cond_t condition; + STKBufferChunk* __strong * bufferChunks; STKDataSource* dataSource; } @end @@ -63,7 +68,7 @@ if (self = [super init]) { threadStartedLock = [[NSConditionLock alloc] initWithCondition:0]; - } + } return self; } @@ -110,12 +115,21 @@ static STKBufferingDataSourceThread* thread; { if (self = [super init]) { + self->maxSize = maxSizeIn; self->dataSource = dataSourceIn; - self->bufferBytesTotal = maxSizeIn; + self->chunkSize = STK_BUFFER_CHUNK_SIZE; self->dataSource.delegate = self.delegate; [self->dataSource registerForEvents:[thread runLoop]]; + + pthread_mutexattr_t attr; + + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + + pthread_mutex_init(&self->mutex, &attr); + pthread_cond_init(&self->condition, NULL); } return self; @@ -125,20 +139,40 @@ static STKBufferingDataSourceThread* thread; { self->dataSource.delegate = nil; - free(self->buffer); + for (int i = 0; i < self->chunkCount; i++) + { + self->bufferChunks[i] = nil; + } + + free(self->bufferChunks); + + pthread_mutex_destroy(&self->mutex); + pthread_cond_destroy(&self->condition); } -(void) createBuffer { - if (self->buffer == nil) + if (self->bufferChunks == nil) { - self->bufferBytesTotal = MIN((int)self.length, self->bufferBytesTotal); - self->bufferBytesTotal = MAX(self->bufferBytesTotal, 1024); + int length = (int)MIN(self.length == 0? 1024 * 1024 : self.length, self->maxSize); - self->buffer = malloc(self->bufferBytesTotal); + self->chunkCount = (int)((length / self->chunkSize) + 1); + self->bufferChunks = (__strong STKBufferChunk**)calloc(sizeof(STKBufferChunk*), self->chunkCount); } } +-(STKBufferChunk*) chunkForPosition:(SInt64)positionIn createIfNotExist:(BOOL)createIfNotExist +{ + int chunkIndex = (int)(positionIn / chunkCount); + + if (self->bufferChunks[chunkIndex] == nil && createIfNotExist) + { + self->bufferChunks[chunkIndex] = [[STKBufferChunk alloc] initWithBufferSize:STK_BUFFER_CHUNK_SIZE]; + } + + return self->bufferChunks[chunkIndex]; +} + -(SInt64) length { return self->dataSource.length; @@ -146,33 +180,21 @@ static STKBufferingDataSourceThread* thread; -(void) seekToOffset:(SInt64)offset { + pthread_mutex_lock(&mutex); + + [self seekToNextGap]; + + pthread_mutex_unlock(&mutex); } -(BOOL) hasBytesAvailable { - return bufferBytesUsed > 0; + return NO; } -(int) readIntoBuffer:(UInt8*)bufferIn withSize:(int)size { - SInt64 bytesAlreadyReadInBuffer = (position - bufferStartFileOffset); - SInt64 bytesAvailable = bufferBytesUsed - bytesAlreadyReadInBuffer; - - if (bytesAvailable < 0) - { - return 0; - } - - int start = (bufferStartIndex + bytesAlreadyReadInBuffer) % bufferBytesTotal; - int end = (start + bufferBytesUsed) % bufferBytesTotal; - int bytesToRead = MIN(end - start, size); - - memcpy(self->buffer, bufferIn, bytesToRead); - - self->bufferBytesUsed -= bytesToRead; - self->bufferStartFileOffset += bytesToRead; - - return bytesToRead; + return 0; } -(BOOL) registerForEvents:(NSRunLoop*)runLoopIn @@ -197,60 +219,50 @@ static STKBufferingDataSourceThread* thread; [dataSource close]; } +-(void) seekToNextGap +{ +} + -(void) dataSourceDataAvailable:(STKDataSource*)dataSourceIn { - if (self->buffer == nil) + if (![dataSourceIn hasBytesAvailable]) + { + return; + } + + pthread_mutex_lock(&mutex); + + if (self->bufferChunks == nil) { [self createBuffer]; } - - UInt32 start = (bufferStartIndex + bufferBytesUsed) % bufferBytesTotal; - UInt32 end = (position - bufferStartFileOffset + bufferStartIndex) % bufferBytesTotal; - if (start >= end) + SInt64 sourcePosition = dataSourceIn.position; + + STKBufferChunk* chunk = [self chunkForPosition:sourcePosition createIfNotExist:YES]; + + if (chunk->position >= chunk->size) { - int bytesRead; - int bufferStartFileOffsetDelta = 0; - int bytesToRead = bufferBytesTotal - start; + [self seekToNextGap]; - if (bytesToRead > 0) - { - bytesRead = [dataSource readIntoBuffer:self->buffer + bufferStartIndex withSize:bytesToRead]; - } - else - { - bytesToRead = end; - - bytesRead = [dataSource readIntoBuffer:self->buffer withSize:bytesToRead]; - - bufferStartFileOffsetDelta = bytesRead - bufferStartIndex; - } - - if (bytesRead < 0) - { - return; - } - - bufferBytesUsed += bytesRead; - bufferStartFileOffset += bufferStartFileOffsetDelta; + return; } - else + + int offset = dataSourceIn.position % self->chunkSize; + + if (offset > chunk->position) { - int bytesToRead = end - start; + [self seekToNextGap]; - int bytesRead = [dataSource readIntoBuffer:self->buffer + start withSize:bytesToRead]; - - if (bytesToRead < 0) - { - return; - } - - int bufferStartFileOffsetDelta = (bytesRead + start) - bufferStartIndex; - - bufferStartIndex += bytesRead; - bufferBytesUsed += bytesRead; - bufferStartFileOffset += bufferStartFileOffsetDelta; + return; } + + int bytesToRead = self->chunkSize - offset; + int bytesRead = [dataSourceIn readIntoBuffer:(chunk->buffer + offset) withSize:bytesToRead]; + + chunk->position = offset + bytesRead; + + pthread_mutex_unlock(&mutex); } -(void) dataSourceErrorOccured:(STKDataSource*)dataSourceIn