123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 |
- /* vim: set ft=objc fenc=utf-8 sw=2 ts=2 et: */
- /*
- * DOUAudioStreamer - A Core Audio based streaming audio player for iOS/Mac:
- *
- * https://github.com/douban/DOUAudioStreamer
- *
- * Copyright 2013-2016 Douban Inc. All rights reserved.
- *
- * Use and distribution licensed under the BSD license. See
- * the LICENSE file for full text.
- *
- * Authors:
- * Chongyu Zhu <i@lembacon.com>
- *
- */
- #import "DOUAudioDecoder.h"
- #import "DOUAudioFileProvider.h"
- #import "DOUAudioPlaybackItem.h"
- #import "DOUAudioLPCM.h"
- #include <AudioToolbox/AudioToolbox.h>
- #include <pthread.h>
- typedef struct {
- AudioFileID afid;
- SInt64 pos;
- void *srcBuffer;
- UInt32 srcBufferSize;
- AudioStreamBasicDescription srcFormat;
- UInt32 srcSizePerPacket;
- UInt32 numPacketsPerRead;
- AudioStreamPacketDescription *pktDescs;
- } AudioFileIO;
- typedef struct {
- AudioStreamBasicDescription inputFormat;
- AudioStreamBasicDescription outputFormat;
- AudioFileIO afio;
- SInt64 decodeValidFrames;
- AudioStreamPacketDescription *outputPktDescs;
- UInt32 outputBufferSize;
- void *outputBuffer;
- UInt32 numOutputPackets;
- SInt64 outputPos;
- pthread_mutex_t mutex;
- } DecodingContext;
- @interface DOUAudioDecoder () {
- @private
- DOUAudioPlaybackItem *_playbackItem;
- DOUAudioLPCM *_lpcm;
- AudioStreamBasicDescription _outputFormat;
- AudioConverterRef _audioConverter;
- NSUInteger _bufferSize;
- DecodingContext _decodingContext;
- BOOL _decodingContextInitialized;
- }
- @end
- @implementation DOUAudioDecoder
- @synthesize playbackItem = _playbackItem;
- @synthesize lpcm = _lpcm;
- + (AudioStreamBasicDescription)defaultOutputFormat
- {
- static AudioStreamBasicDescription defaultOutputFormat;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- defaultOutputFormat.mFormatID = kAudioFormatLinearPCM;
- defaultOutputFormat.mSampleRate = 44100;
- defaultOutputFormat.mBitsPerChannel = 16;
- defaultOutputFormat.mChannelsPerFrame = 2;
- defaultOutputFormat.mBytesPerFrame = defaultOutputFormat.mChannelsPerFrame * (defaultOutputFormat.mBitsPerChannel / 8);
- defaultOutputFormat.mFramesPerPacket = 1;
- defaultOutputFormat.mBytesPerPacket = defaultOutputFormat.mFramesPerPacket * defaultOutputFormat.mBytesPerFrame;
- defaultOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
- });
- return defaultOutputFormat;
- }
- + (instancetype)decoderWithPlaybackItem:(DOUAudioPlaybackItem *)playbackItem
- bufferSize:(NSUInteger)bufferSize
- {
- return [[[self class] alloc] initWithPlaybackItem:playbackItem
- bufferSize:bufferSize];
- }
- - (instancetype)initWithPlaybackItem:(DOUAudioPlaybackItem *)playbackItem
- bufferSize:(NSUInteger)bufferSize
- {
- self = [super init];
- if (self) {
- _playbackItem = playbackItem;
- _bufferSize = bufferSize;
- _lpcm = [[DOUAudioLPCM alloc] init];
- _outputFormat = [[self class] defaultOutputFormat];
- [self _createAudioConverter];
- if (_audioConverter == NULL) {
- return nil;
- }
- }
- return self;
- }
- - (void)dealloc
- {
- if (_decodingContextInitialized) {
- [self tearDown];
- }
- if (_audioConverter != NULL) {
- AudioConverterDispose(_audioConverter);
- }
- }
- - (void)_createAudioConverter
- {
- AudioStreamBasicDescription inputFormat = [_playbackItem fileFormat];
- OSStatus status = AudioConverterNew(&inputFormat, &_outputFormat, &_audioConverter);
- if (status != noErr) {
- _audioConverter = NULL;
- }
- }
- - (void)_fillMagicCookieForAudioFileID:(AudioFileID)inputFile
- {
- UInt32 cookieSize = 0;
- OSStatus status = AudioFileGetPropertyInfo(inputFile, kAudioFilePropertyMagicCookieData, &cookieSize, NULL);
- if (status == noErr && cookieSize > 0) {
- void *cookie = malloc(cookieSize);
- status = AudioFileGetProperty(inputFile, kAudioFilePropertyMagicCookieData, &cookieSize, cookie);
- if (status != noErr) {
- free(cookie);
- return;
- }
- status = AudioConverterSetProperty(_audioConverter, kAudioConverterDecompressionMagicCookie, cookieSize, cookie);
- free(cookie);
- if (status != noErr) {
- return;
- }
- }
- }
- - (BOOL)setUp
- {
- if (_decodingContextInitialized) {
- return YES;
- }
- AudioFileID inputFile = [_playbackItem fileID];
- if (inputFile == NULL) {
- return NO;
- }
- _decodingContext.inputFormat = [_playbackItem fileFormat];
- _decodingContext.outputFormat = _outputFormat;
- [self _fillMagicCookieForAudioFileID:inputFile];
- UInt32 size;
- OSStatus status;
- size = sizeof(_decodingContext.inputFormat);
- status = AudioConverterGetProperty(_audioConverter, kAudioConverterCurrentInputStreamDescription, &size, &_decodingContext.inputFormat);
- if (status != noErr) {
- return NO;
- }
- size = sizeof(_decodingContext.outputFormat);
- status = AudioConverterGetProperty(_audioConverter, kAudioConverterCurrentOutputStreamDescription, &size, &_decodingContext.outputFormat);
- if (status != noErr) {
- return NO;
- }
- AudioStreamBasicDescription baseFormat;
- UInt32 propertySize = sizeof(baseFormat);
- AudioFileGetProperty(inputFile, kAudioFilePropertyDataFormat, &propertySize, &baseFormat);
- double actualToBaseSampleRateRatio = 1.0;
- if (_decodingContext.inputFormat.mSampleRate != baseFormat.mSampleRate &&
- _decodingContext.inputFormat.mSampleRate != 0.0 &&
- baseFormat.mSampleRate != 0.0) {
- actualToBaseSampleRateRatio = _decodingContext.inputFormat.mSampleRate / baseFormat.mSampleRate;
- }
- double srcRatio = 1.0;
- if (_decodingContext.outputFormat.mSampleRate != 0.0 &&
- _decodingContext.inputFormat.mSampleRate != 0.0) {
- srcRatio = _decodingContext.outputFormat.mSampleRate / _decodingContext.inputFormat.mSampleRate;
- }
- _decodingContext.decodeValidFrames = 0;
- AudioFilePacketTableInfo srcPti;
- if (_decodingContext.inputFormat.mBitsPerChannel == 0) {
- size = sizeof(srcPti);
- status = AudioFileGetProperty(inputFile, kAudioFilePropertyPacketTableInfo, &size, &srcPti);
- if (status == noErr) {
- _decodingContext.decodeValidFrames = (SInt64)(actualToBaseSampleRateRatio * srcRatio * srcPti.mNumberValidFrames + 0.5);
- AudioConverterPrimeInfo primeInfo;
- primeInfo.leadingFrames = (UInt32)(srcPti.mPrimingFrames * actualToBaseSampleRateRatio + 0.5);
- primeInfo.trailingFrames = 0;
- status = AudioConverterSetProperty(_audioConverter, kAudioConverterPrimeInfo, sizeof(primeInfo), &primeInfo);
- if (status != noErr) {
- return NO;
- }
- }
- }
- _decodingContext.afio.afid = inputFile;
- _decodingContext.afio.srcBufferSize = (UInt32)_bufferSize;
- _decodingContext.afio.srcBuffer = malloc(_decodingContext.afio.srcBufferSize);
- _decodingContext.afio.pos = 0;
- _decodingContext.afio.srcFormat = _decodingContext.inputFormat;
- if (_decodingContext.inputFormat.mBytesPerPacket == 0) {
- size = sizeof(_decodingContext.afio.srcSizePerPacket);
- status = AudioFileGetProperty(inputFile, kAudioFilePropertyPacketSizeUpperBound, &size, &_decodingContext.afio.srcSizePerPacket);
- if (status != noErr) {
- free(_decodingContext.afio.srcBuffer);
- return NO;
- }
- _decodingContext.afio.numPacketsPerRead = _decodingContext.afio.srcBufferSize / _decodingContext.afio.srcSizePerPacket;
- _decodingContext.afio.pktDescs = (AudioStreamPacketDescription *)malloc(sizeof(AudioStreamPacketDescription) * _decodingContext.afio.numPacketsPerRead);
- }
- else {
- _decodingContext.afio.srcSizePerPacket = _decodingContext.inputFormat.mBytesPerPacket;
- _decodingContext.afio.numPacketsPerRead = _decodingContext.afio.srcBufferSize / _decodingContext.afio.srcSizePerPacket;
- _decodingContext.afio.pktDescs = NULL;
- }
- _decodingContext.outputPktDescs = NULL;
- UInt32 outputSizePerPacket = _decodingContext.outputFormat.mBytesPerPacket;
- _decodingContext.outputBufferSize = (UInt32)_bufferSize;
- _decodingContext.outputBuffer = malloc(_decodingContext.outputBufferSize);
- if (outputSizePerPacket == 0) {
- size = sizeof(outputSizePerPacket);
- status = AudioConverterGetProperty(_audioConverter, kAudioConverterPropertyMaximumOutputPacketSize, &size, &outputSizePerPacket);
- if (status != noErr) {
- free(_decodingContext.afio.srcBuffer);
- free(_decodingContext.outputBuffer);
- if (_decodingContext.afio.pktDescs != NULL) {
- free(_decodingContext.afio.pktDescs);
- }
- return NO;
- }
- _decodingContext.outputPktDescs = (AudioStreamPacketDescription *)malloc(sizeof(AudioStreamPacketDescription) * _decodingContext.outputBufferSize / outputSizePerPacket);
- }
- _decodingContext.numOutputPackets = _decodingContext.outputBufferSize / outputSizePerPacket;
- _decodingContext.outputPos = 0;
- pthread_mutex_init(&_decodingContext.mutex, NULL);
- _decodingContextInitialized = YES;
- return YES;
- }
- - (void)tearDown
- {
- if (!_decodingContextInitialized) {
- return;
- }
- free(_decodingContext.afio.srcBuffer);
- free(_decodingContext.outputBuffer);
- if (_decodingContext.afio.pktDescs != NULL) {
- free(_decodingContext.afio.pktDescs);
- }
- if (_decodingContext.outputPktDescs != NULL) {
- free(_decodingContext.outputPktDescs);
- }
- pthread_mutex_destroy(&_decodingContext.mutex);
- _decodingContextInitialized = NO;
- }
- static OSStatus decoder_data_proc(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData)
- {
- AudioFileIO *afio = (AudioFileIO *)inUserData;
- if (*ioNumberDataPackets > afio->numPacketsPerRead) {
- *ioNumberDataPackets = afio->numPacketsPerRead;
- }
- UInt32 outNumBytes;
- OSStatus status = AudioFileReadPackets(afio->afid, FALSE, &outNumBytes, afio->pktDescs, afio->pos, ioNumberDataPackets, afio->srcBuffer);
- if (status != noErr) {
- return status;
- }
- afio->pos += *ioNumberDataPackets;
- ioData->mBuffers[0].mData = afio->srcBuffer;
- ioData->mBuffers[0].mDataByteSize = outNumBytes;
- ioData->mBuffers[0].mNumberChannels = afio->srcFormat.mChannelsPerFrame;
- if (outDataPacketDescription != NULL) {
- *outDataPacketDescription = afio->pktDescs;
- }
- return noErr;
- }
- - (DOUAudioDecoderStatus)decodeOnce
- {
- if (!_decodingContextInitialized) {
- return DOUAudioDecoderFailed;
- }
- pthread_mutex_lock(&_decodingContext.mutex);
- DOUAudioFileProvider *provider = [_playbackItem fileProvider];
- if ([provider isFailed]) {
- [_lpcm setEnd:YES];
- pthread_mutex_unlock(&_decodingContext.mutex);
- return DOUAudioDecoderFailed;
- }
- if (![provider isFinished]) {
- NSUInteger dataOffset = [_playbackItem dataOffset];
- NSUInteger expectedDataLength = [provider expectedLength];
- NSInteger receivedDataLength = (NSInteger)([provider receivedLength] - dataOffset);
- SInt64 packetNumber = _decodingContext.afio.pos + _decodingContext.afio.numPacketsPerRead;
- SInt64 packetDataOffset = packetNumber * _decodingContext.afio.srcSizePerPacket;
- SInt64 bytesPerPacket = _decodingContext.afio.srcSizePerPacket;
- SInt64 bytesPerRead = bytesPerPacket * _decodingContext.afio.numPacketsPerRead;
- SInt64 framesPerPacket = _decodingContext.inputFormat.mFramesPerPacket;
- double intervalPerPacket = 1000.0 / _decodingContext.inputFormat.mSampleRate * framesPerPacket;
- double intervalPerRead = intervalPerPacket / bytesPerPacket * bytesPerRead;
- double downloadTime = 1000.0 * (bytesPerRead - (receivedDataLength - packetDataOffset)) / [provider downloadSpeed];
- SInt64 bytesRemaining = (SInt64)(expectedDataLength - (NSUInteger)receivedDataLength);
- if (receivedDataLength < packetDataOffset ||
- (bytesRemaining > 0 &&
- downloadTime > intervalPerRead)) {
- pthread_mutex_unlock(&_decodingContext.mutex);
- return DOUAudioDecoderWaiting;
- }
- }
- AudioBufferList fillBufList;
- fillBufList.mNumberBuffers = 1;
- fillBufList.mBuffers[0].mNumberChannels = _decodingContext.inputFormat.mChannelsPerFrame;
- fillBufList.mBuffers[0].mDataByteSize = _decodingContext.outputBufferSize;
- fillBufList.mBuffers[0].mData = _decodingContext.outputBuffer;
- OSStatus status;
- UInt32 ioOutputDataPackets = _decodingContext.numOutputPackets;
- status = AudioConverterFillComplexBuffer(_audioConverter, decoder_data_proc, &_decodingContext.afio, &ioOutputDataPackets, &fillBufList, _decodingContext.outputPktDescs);
- if (status != noErr) {
- pthread_mutex_unlock(&_decodingContext.mutex);
- return DOUAudioDecoderFailed;
- }
- if (ioOutputDataPackets == 0) {
- [_lpcm setEnd:YES];
- pthread_mutex_unlock(&_decodingContext.mutex);
- return DOUAudioDecoderEndEncountered;
- }
- SInt64 frame1 = _decodingContext.outputPos + ioOutputDataPackets;
- if (_decodingContext.decodeValidFrames != 0 &&
- frame1 > _decodingContext.decodeValidFrames) {
- SInt64 framesToTrim64 = frame1 - _decodingContext.decodeValidFrames;
- UInt32 framesToTrim = (framesToTrim64 > ioOutputDataPackets) ? ioOutputDataPackets : (UInt32)framesToTrim64;
- int bytesToTrim = (int)(framesToTrim * _decodingContext.outputFormat.mBytesPerFrame);
- fillBufList.mBuffers[0].mDataByteSize -= (unsigned long)bytesToTrim;
- ioOutputDataPackets -= framesToTrim;
- if (ioOutputDataPackets == 0) {
- [_lpcm setEnd:YES];
- pthread_mutex_unlock(&_decodingContext.mutex);
- return DOUAudioDecoderEndEncountered;
- }
- }
- UInt32 inNumBytes = fillBufList.mBuffers[0].mDataByteSize;
- [_lpcm writeBytes:_decodingContext.outputBuffer length:inNumBytes];
- _decodingContext.outputPos += ioOutputDataPackets;
- pthread_mutex_unlock(&_decodingContext.mutex);
- return DOUAudioDecoderSucceeded;
- }
- - (void)seekToTime:(NSUInteger)milliseconds
- {
- if (!_decodingContextInitialized) {
- return;
- }
- pthread_mutex_lock(&_decodingContext.mutex);
- double frames = (double)milliseconds * _decodingContext.inputFormat.mSampleRate / 1000.0;
- double packets = frames / _decodingContext.inputFormat.mFramesPerPacket;
- SInt64 packetNumebr = (SInt64)lrint(floor(packets));
- _decodingContext.afio.pos = packetNumebr;
- _decodingContext.outputPos = packetNumebr * _decodingContext.inputFormat.mFramesPerPacket / _decodingContext.outputFormat.mFramesPerPacket;
-
- pthread_mutex_unlock(&_decodingContext.mutex);
- }
- @end
|