Allow recording a specific DataSource to AAC-LC
This commit is contained in:
parent
4c7ce6c4ec
commit
09e5602464
|
|
@ -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(¤tEntryReferencesLock);
|
||||
}
|
||||
|
||||
[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(¤tEntryReferencesLock);
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue