Merge pull request #152 from danielgindi/recording

Recording
This commit is contained in:
Thong Nguyen 2014-12-29 17:51:26 +00:00
commit a5b6360b1c
2 changed files with 246 additions and 3 deletions

View File

@ -63,6 +63,9 @@
#define STK_DEFAULT_PACKET_BUFFER_SIZE (2048)
#define STK_DEFAULT_GRACE_PERIOD_AFTER_SEEK_SECONDS (0.5)
#define OSSTATUS_PRINTF_PLACEHOLDER @"%c%c%c%c"
#define OSSTATUS_PRINTF_VALUE(status) ((status) >> 24) & 0xFF, ((status) >> 16) & 0xFF, ((status) >> 8) & 0xFF, (status) & 0xFF
#define LOGINFO(x) [self logInfo:[NSString stringWithFormat:@"%s %@", sel_getName(_cmd), x]];
static void PopulateOptionsWithDefault(STKAudioPlayerOptions* options)
@ -173,6 +176,7 @@ static AudioComponentDescription nbandUnitDescription;
static AudioComponentDescription outputUnitDescription;
static AudioComponentDescription convertUnitDescription;
static AudioStreamBasicDescription canonicalAudioStreamBasicDescription;
static AudioStreamBasicDescription recordAudioStreamBasicDescription;
@interface STKAudioPlayer()
{
@ -241,7 +245,16 @@ static AudioStreamBasicDescription canonicalAudioStreamBasicDescription;
AudioFileStreamID audioFileStream;
NSConditionLock* threadStartedLock;
NSConditionLock* threadFinishedCondLock;
AudioFileID recordAudioFileId;
UInt32 recordFilePacketPosition;
AudioConverterRef recordAudioConverterRef;
UInt32 recordOutputBufferSize;
UInt8 *recordOutputBuffer;
UInt32 recordPacketsPerBuffer;
UInt32 recordPacketSize;
AudioStreamPacketDescription *recordPacketDescriptions;
void(^stopBackBackgroundTaskBlock)();
int32_t seekVersion;
@ -311,7 +324,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
.mBitsPerChannel = 8 * bytesPerSample,
.mBytesPerPacket = (bytesPerSample * 2)
};
outputUnitDescription = (AudioComponentDescription)
{
.componentType = kAudioUnitType_Output,
@ -542,6 +555,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
OSSpinLockUnlock(&currentEntryReferencesLock);
}
[self closeRecordAudioFile];
[self stopAudioUnitWithReason:STKAudioPlayerStopReasonDisposed];
[self clearQueue];
@ -1099,6 +1114,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
[currentlyReadingEntry.dataSource registerForEvents:[NSRunLoop currentRunLoop]];
[currentlyReadingEntry.dataSource seekToOffset:0];
[self closeRecordAudioFile];
if (startPlaying)
{
if (clearQueue)
@ -1401,6 +1418,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
currentlyReadingEntry = nil;
OSSpinLockUnlock(&currentEntryReferencesLock);
pthread_mutex_unlock(&playerMutex);
[self closeRecordAudioFile];
self.internalState = STKAudioPlayerInternalStateDisposed;
@ -1463,6 +1482,11 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
AudioConverterReset(audioConverterRef);
}
if (recordAudioConverterRef)
{
AudioConverterReset(recordAudioConverterRef);
}
[currentEntry reset];
[currentEntry.dataSource seekToOffset:seekByteOffset];
@ -1581,7 +1605,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
}
NSObject* queueItemId = currentlyReadingEntry.queueItemId;
[self closeRecordAudioFile];
[self dispatchSyncOnMainThread:^
{
[self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId];
@ -1706,6 +1732,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
return;
}
[self closeRecordAudioFile];
[self stopAudioUnitWithReason:STKAudioPlayerStopReasonUserAction];
[self resetPcmBuffers];
@ -1800,6 +1828,35 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
self.muted = NO;
}
-(void) closeRecordAudioFile
{
if (recordAudioFileId)
{
AudioFileClose(recordAudioFileId);
recordAudioFileId = NULL;
}
if (recordAudioConverterRef)
{
AudioConverterDispose(recordAudioConverterRef);
recordAudioConverterRef = nil;
}
if (recordOutputBuffer)
{
free(recordOutputBuffer);
recordOutputBuffer = NULL;
}
if (recordPacketDescriptions)
{
free(recordPacketDescriptions);
recordPacketDescriptions = NULL;
}
recordFilePacketPosition = 0;
}
-(void) dispose
{
[self stop];
@ -1878,6 +1935,11 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
{
AudioConverterReset(audioConverterRef);
if (recordAudioConverterRef)
{
AudioConverterReset(recordAudioConverterRef);
}
return;
}
@ -1885,6 +1947,25 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
canonicalAudioStreamBasicDescription.mChannelsPerFrame = asbd->mChannelsPerFrame;
BOOL isRecording = currentlyReadingEntry.dataSource.recordToFileUrl != nil;
if (isRecording)
{
recordAudioStreamBasicDescription = (AudioStreamBasicDescription)
{
.mFormatID = kAudioFormatMPEG4AAC,
.mFormatFlags = kMPEG4Object_AAC_LC,
.mChannelsPerFrame = canonicalAudioStreamBasicDescription.mChannelsPerFrame,
.mSampleRate = canonicalAudioStreamBasicDescription.mSampleRate,
};
UInt32 dataSize = sizeof(recordAudioStreamBasicDescription);
AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
0,
NULL,
&dataSize,
&recordAudioStreamBasicDescription);
}
AudioClassDescription classDesc;
if (GetHardwareCodecClassDesc(asbd->mFormatID, &classDesc))
@ -1903,6 +1984,16 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
return;
}
}
if (isRecording && !recordAudioConverterRef)
{
status = AudioConverterNew(&canonicalAudioStreamBasicDescription, &recordAudioStreamBasicDescription, &recordAudioConverterRef);
if (status)
{
NSLog(@"STKAudioPlayer failed to create a recording audio converter");
}
}
audioConverterAudioStreamBasicDescription = *asbd;
@ -1933,6 +2024,82 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
return;
}
}
if (recordAudioConverterRef)
{
if (recordAudioFileId)
{
AudioFileClose(recordAudioFileId);
recordAudioFileId = NULL;
}
if (recordOutputBuffer)
{
free(recordOutputBuffer);
recordOutputBuffer = NULL;
}
if (recordPacketDescriptions)
{
free(recordPacketDescriptions);
recordPacketDescriptions = NULL;
}
recordOutputBufferSize = 32 * 1024;
recordPacketSize = canonicalAudioStreamBasicDescription.mBytesPerPacket;
if (recordPacketSize == 0)
{
UInt32 size = sizeof(recordPacketSize);
if (0 == AudioConverterGetProperty(recordAudioConverterRef, kAudioConverterPropertyMaximumOutputPacketSize, &size, &recordPacketSize))
{
if (recordPacketSize > recordOutputBufferSize)
{
recordOutputBufferSize = recordPacketSize;
}
recordPacketsPerBuffer = recordOutputBufferSize / recordPacketSize;
}
else
{
AudioConverterDispose(recordAudioConverterRef);
recordAudioConverterRef = NULL;
NSLog(@"STKAudioPlayer: Can't support this output format for recording");
}
}
else
{
recordPacketsPerBuffer = recordOutputBufferSize / recordPacketSize;
}
UInt32 propertySize = sizeof(UInt32);
UInt32 externallyFramed = 0;
OSStatus error = AudioFormatGetProperty(kAudioFormatProperty_FormatIsExternallyFramed, sizeof(recordAudioStreamBasicDescription), &recordAudioStreamBasicDescription, &propertySize, &externallyFramed);
if (externallyFramed)
{
recordPacketDescriptions = (AudioStreamPacketDescription *)malloc(sizeof(AudioStreamPacketDescription) * recordPacketsPerBuffer);
}
recordOutputBuffer = (UInt8 *)malloc(sizeof(UInt8) * recordOutputBufferSize);
error = AudioFileCreateWithURL(
(__bridge CFURLRef)(currentlyReadingEntry.dataSource.recordToFileUrl),
kAudioFileCAFType,
&recordAudioStreamBasicDescription,
kAudioFileFlags_EraseFile,
&recordAudioFileId);
recordFilePacketPosition = 0;
if (error)
{
NSLog(@"STKAudioPlayer failed to create a recording audio file at %@", currentlyReadingEntry.dataSource.recordToFileUrl);
[self closeRecordAudioFile];
}
}
}
-(void) createOutputUnit
@ -2424,6 +2591,11 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu
status = AudioConverterFillComplexBuffer(audioConverterRef, AudioConverterCallback, (void*)&convertInfo, &framesToDecode, &localPcmBufferList, NULL);
framesAdded = framesToDecode;
if ((status == 100 || status == 0) && recordAudioFileId && recordAudioConverterRef)
{
[self handleRecordingOfAudioPackets:framesToDecode audioBuffer:&localPcmBufferList.mBuffers[0]];
}
if (status == 100)
{
@ -2467,6 +2639,11 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu
framesAdded += framesToDecode;
if ((status == 100 || status == 0) && recordAudioFileId && recordAudioConverterRef)
{
[self handleRecordingOfAudioPackets:framesToDecode audioBuffer:&localPcmBufferList.mBuffers[0]];
}
if (status == 100)
{
OSSpinLockLock(&pcmBufferSpinLock);
@ -2511,6 +2688,11 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu
framesAdded = framesToDecode;
if ((status == 100 || status == 0) && recordAudioFileId && recordAudioConverterRef)
{
[self handleRecordingOfAudioPackets:framesToDecode audioBuffer:&localPcmBufferList.mBuffers[0]];
}
if (status == 100)
{
OSSpinLockLock(&pcmBufferSpinLock);
@ -2545,6 +2727,66 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu
}
}
- (void)handleRecordingOfAudioPackets:(UInt32)numberOfPackets audioBuffer:(AudioBuffer *)audioBuffer
{
if (recordAudioFileId && recordAudioConverterRef)
{
AudioConvertInfo recordConvertInfo;
recordConvertInfo.done = NO;
recordConvertInfo.numberOfPackets = numberOfPackets;
recordConvertInfo.packetDescriptions = NULL;
recordConvertInfo.audioBuffer = *audioBuffer;
AudioBufferList convertedData;
convertedData.mNumberBuffers = 1;
convertedData.mBuffers[0].mNumberChannels = recordAudioStreamBasicDescription.mChannelsPerFrame;
convertedData.mBuffers[0].mDataByteSize = recordOutputBufferSize;
convertedData.mBuffers[0].mData = recordOutputBuffer;
UInt32 ioOutputDataPackets;
OSStatus status;
while (1)
{
ioOutputDataPackets = recordPacketsPerBuffer;
status = AudioConverterFillComplexBuffer(recordAudioConverterRef, AudioConverterCallback, (void*)&recordConvertInfo, &ioOutputDataPackets, &convertedData, recordPacketDescriptions);
if (status == 100 || status == 0)
{
if (ioOutputDataPackets > 0)
{
OSStatus writeError = AudioFileWritePackets(recordAudioFileId,
NO,
convertedData.mBuffers[0].mDataByteSize,
recordPacketDescriptions,
recordFilePacketPosition,
&ioOutputDataPackets,
convertedData.mBuffers[0].mData);
if (writeError)
{
NSLog(@"STKAudioPlayer:handleRecordingOfAudioPackets failed on AudioFileWritePackets with error \"" OSSTATUS_PRINTF_PLACEHOLDER "\"", OSSTATUS_PRINTF_VALUE(writeError));
}
else
{
recordFilePacketPosition += ioOutputDataPackets;
}
}
}
else
{
NSLog(@"STKAudioPlayer: Unexpected error during recording audio file conversion");
}
if (status == 100)
{
break;
}
}
}
}
static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData)
{
STKAudioPlayer* audioPlayer = (__bridge STKAudioPlayer*)inRefCon;

View File

@ -51,6 +51,7 @@
@property (readonly) BOOL hasBytesAvailable;
@property (nonatomic, readwrite, assign) double durationHint;
@property (readwrite, unsafe_unretained) id<STKDataSourceDelegate> delegate;
@property (nonatomic, strong) NSURL *recordToFileUrl;
-(BOOL) registerForEvents:(NSRunLoop*)runLoop;
-(void) unregisterForEvents;