Some chunking/paging buffering data source work
This commit is contained in:
parent
7540045361
commit
d7d583c3ba
|
|
@ -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 = "<group>"; };
|
||||
A1682FA218B3903900F29FEC /* STKBufferingDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKBufferingDataSource.m; sourceTree = "<group>"; };
|
||||
A168C6EF18BB67DC003D170D /* STKBufferChunk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKBufferChunk.h; sourceTree = "<group>"; };
|
||||
A168C6F018BB67DC003D170D /* STKBufferChunk.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKBufferChunk.m; sourceTree = "<group>"; };
|
||||
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 */,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// STKBufferChunk.h
|
||||
// StreamingKit
|
||||
//
|
||||
// Created by Thong Nguyen on 24/02/2014.
|
||||
// Copyright (c) 2014 Thong Nguyen. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface STKBufferChunk : NSObject
|
||||
{
|
||||
@public
|
||||
UInt32 key;
|
||||
UInt32 size;
|
||||
UInt32 position;
|
||||
UInt8* buffer;
|
||||
}
|
||||
|
||||
-(id) initWithBufferSize:(UInt32)sizeIn;
|
||||
|
||||
@end
|
||||
|
|
@ -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
|
||||
|
|
@ -33,17 +33,22 @@
|
|||
**********************************************************************************/
|
||||
|
||||
#import "STKBufferingDataSource.h"
|
||||
#import "STKBufferChunk.h"
|
||||
#import <pthread.h>
|
||||
|
||||
#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
|
||||
|
|
|
|||
Loading…
Reference in New Issue