void Audio_Stream::seekToTime(unsigned newSeekTime) { unsigned duration = durationInSeconds(); if (!(duration > 0)) { return; } close(); setState(SEEKING); HTTP_Stream_Position position; double offset = (double)newSeekTime / (double)duration; position.start = m_dataOffset + offset * (contentLength() - m_dataOffset); position.end = contentLength(); m_seekTime = newSeekTime; if (m_httpStream->open(position)) { AS_TRACE("%s: HTTP stream opened, buffering...\n", __PRETTY_FUNCTION__); m_httpStreamRunning = true; } else { AS_TRACE("%s: failed to open the HTTP stream\n", __PRETTY_FUNCTION__); setState(FAILED); } }
/* This is called by audio file stream parser when it finds property values */ void Audio_Stream::propertyValueCallback(void *inClientData, AudioFileStreamID inAudioFileStream, AudioFileStreamPropertyID inPropertyID, UInt32 *ioFlags) { AS_TRACE("%s\n", __PRETTY_FUNCTION__); Audio_Stream *THIS = static_cast<Audio_Stream*>(inClientData); if (!THIS->m_audioStreamParserRunning) { AS_TRACE("%s: stray callback detected!\n", __PRETTY_FUNCTION__); return; } switch (inPropertyID) { case kAudioFileStreamProperty_DataOffset: { SInt64 offset; UInt32 offsetSize = sizeof(offset); OSStatus result = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataOffset, &offsetSize, &offset); if (result == 0) { THIS->m_dataOffset = offset; } else { AS_TRACE("%s: reading kAudioFileStreamProperty_DataOffset property failed\n", __PRETTY_FUNCTION__); } break; } default: { THIS->m_audioQueue->handlePropertyChange(inAudioFileStream, inPropertyID, ioFlags); break; } } }
void Audio_Stream::open() { if (m_httpStreamRunning) { AS_TRACE("%s: already running: return\n", __PRETTY_FUNCTION__); return; } if (m_needNewQueue && m_audioQueue) { m_needNewQueue = false; closeAudioQueue(); } m_contentLength = 0; m_seekTime = 0; m_processedPacketsSizeTotal = 0; m_processedPacketsCount = 0; m_bitrateBufferIndex = 0; if (m_httpStream->open()) { AS_TRACE("%s: HTTP stream opened, buffering...\n", __PRETTY_FUNCTION__); m_httpStreamRunning = true; setState(BUFFERING); } else { AS_TRACE("%s: failed to open the HTTP stream\n", __PRETTY_FUNCTION__); closeAndSignalError(AS_ERR_OPEN); } }
void Audio_Stream::audioQueueBuffersEmpty() { AS_TRACE("%s: enter\n", __PRETTY_FUNCTION__); if (m_httpStreamRunning) { /* Still feeding the audio queue with data, don't stop yet */ return; } AS_TRACE("%s: closing the audio queue\n", __PRETTY_FUNCTION__); if (m_audioStreamParserRunning) { if (AudioFileStreamClose(m_audioFileStream) != 0) { AS_TRACE("%s: AudioFileStreamClose failed\n", __PRETTY_FUNCTION__); } m_audioStreamParserRunning = false; } // Keep the audio queue running until it has finished playing audioQueue()->stop(false); m_needNewQueue = true; AS_TRACE("%s: leave\n", __PRETTY_FUNCTION__); }
void Audio_Stream::streamIsReadyRead() { if (m_audioStreamParserRunning) { AS_TRACE("%s: parser already running!\n", __PRETTY_FUNCTION__); return; } /* Check if the stream's MIME type begins with audio/ */ std::string contentType = m_httpStream->contentType(); const char *audioContentType = "audio/"; size_t audioContentTypeLength = strlen(audioContentType); if (contentType.compare(0, audioContentTypeLength, audioContentType) != 0) { closeAndSignalError(AS_ERR_OPEN); return; } /* OK, it should be an audio stream, let's try to open it */ OSStatus result = AudioFileStreamOpen(this, propertyValueCallback, streamDataCallback, audioStreamTypeFromContentType(contentType), &m_audioFileStream); if (result == 0) { AS_TRACE("%s: audio file stream opened.\n", __PRETTY_FUNCTION__); m_audioStreamParserRunning = true; } else { closeAndSignalError(AS_ERR_OPEN); } }
void Audio_Stream::streamHasBytesAvailable(UInt8 *data, UInt32 numBytes) { AS_TRACE("%s: %u bytes\n", __FUNCTION__, (unsigned int)numBytes); if (!m_httpStreamRunning) { AS_TRACE("%s: stray callback detected!\n", __PRETTY_FUNCTION__); return; } if (m_fileOutput) { m_fileOutput->write(data, numBytes); } if (m_audioStreamParserRunning) { OSStatus result = AudioFileStreamParseBytes(m_audioFileStream, numBytes, data, 0); if (result != 0) { AS_TRACE("%s: AudioFileStreamParseBytes error %d\n", __PRETTY_FUNCTION__, (int)result); closeAndSignalError(AS_ERR_STREAM_PARSE); } else if (m_initializationError == kAudioConverterErr_FormatNotSupported) { AS_TRACE("Audio stream initialization failed due to unsupported format\n"); closeAndSignalError(AS_ERR_UNSUPPORTED_FORMAT); } else if (m_initializationError != noErr) { AS_TRACE("Audio stream initialization failed due to unknown error\n"); closeAndSignalError(AS_ERR_OPEN); } } }
void Audio_Stream::streamErrorOccurred() { AS_TRACE("%s\n", __PRETTY_FUNCTION__); if (!m_httpStreamRunning) { AS_TRACE("%s: stray callback detected!\n", __PRETTY_FUNCTION__); return; } closeAndSignalError(AS_ERR_NETWORK); }
/* This is called by audio file stream parser when it finds property values */ void Audio_Stream::propertyValueCallback(void *inClientData, AudioFileStreamID inAudioFileStream, AudioFileStreamPropertyID inPropertyID, UInt32 *ioFlags) { AS_TRACE("%s\n", __PRETTY_FUNCTION__); Audio_Stream *THIS = static_cast<Audio_Stream*>(inClientData); if (!THIS->m_audioStreamParserRunning) { AS_TRACE("%s: stray callback detected!\n", __PRETTY_FUNCTION__); return; } THIS->m_audioQueue->handlePropertyChange(inAudioFileStream, inPropertyID, ioFlags); }
/* This is called by audio file stream parser when it finds packets of audio */ void Audio_Stream::streamDataCallback(void *inClientData, UInt32 inNumberBytes, UInt32 inNumberPackets, const void *inInputData, AudioStreamPacketDescription *inPacketDescriptions) { AS_TRACE("%s: inNumberBytes %lu, inNumberPackets %lu\n", __FUNCTION__, inNumberBytes, inNumberPackets); Audio_Stream *THIS = static_cast<Audio_Stream*>(inClientData); if (!THIS->m_audioStreamParserRunning) { AS_TRACE("%s: stray callback detected!\n", __PRETTY_FUNCTION__); return; } THIS->m_audioQueue->handleAudioPackets(inNumberBytes, inNumberPackets, inInputData, inPacketDescriptions); }
void Audio_Stream::close() { AS_TRACE("%s: enter\n", __PRETTY_FUNCTION__); if (m_watchdogTimer) { CFRunLoopTimerInvalidate(m_watchdogTimer); CFRelease(m_watchdogTimer), m_watchdogTimer = 0; } /* Close the HTTP stream first so that the audio stream parser isn't fed with more data to parse */ if (m_httpStreamRunning) { m_httpStream->close(); m_httpStreamRunning = false; } if (m_audioStreamParserRunning) { if (m_audioFileStream) { if (AudioFileStreamClose(m_audioFileStream) != 0) { AS_TRACE("%s: AudioFileStreamClose failed\n", __PRETTY_FUNCTION__); } m_audioFileStream = 0; } m_audioStreamParserRunning = false; } closeAudioQueue(); if (FAILED != state()) { /* * Set the stream state to stopped if the stream was stopped successfully. * We don't want to cause a spurious stopped state as the fail state should * be the final state in case the stream failed. */ setState(STOPPED); } /* * Free any remaining queud packets for encoding. */ queued_packet_t *cur = m_queuedHead; while (cur) { queued_packet_t *tmp = cur->next; free(cur); cur = tmp; } m_queuedHead = m_queuedTail = 0; AS_TRACE("%s: leave\n", __PRETTY_FUNCTION__); }
void Audio_Stream::streamEndEncountered() { AS_TRACE("%s\n", __PRETTY_FUNCTION__); if (!m_httpStreamRunning) { AS_TRACE("%s: stray callback detected!\n", __PRETTY_FUNCTION__); return; } setState(END_OF_FILE); m_httpStream->close(); m_httpStreamRunning = false; }
void Audio_Stream::seekToTime(unsigned newSeekTime) { unsigned duration = durationInSeconds(); if (!(duration > 0)) { return; } if (state() == SEEKING) { return; } else { setState(SEEKING); } m_seekTime = newSeekTime; double offset = (double)newSeekTime / (double)duration; UInt64 seekByteOffset = m_dataOffset + offset * (contentLength() - m_dataOffset); HTTP_Stream_Position position; position.start = seekByteOffset; position.end = contentLength(); double packetDuration = m_srcFormat.mFramesPerPacket / m_srcFormat.mSampleRate; if (packetDuration > 0 && bitrate() > 0) { UInt32 ioFlags = 0; SInt64 packetAlignedByteOffset; SInt64 seekPacket = floor((double)newSeekTime / packetDuration); OSStatus err = AudioFileStreamSeek(m_audioFileStream, seekPacket, &packetAlignedByteOffset, &ioFlags); if (!err) { position.start = packetAlignedByteOffset + m_dataOffset; } } close(); AS_TRACE("Seeking position %llu\n", position.start); if (m_httpStream->open(position)) { AS_TRACE("%s: HTTP stream opened, buffering...\n", __PRETTY_FUNCTION__); m_httpStreamRunning = true; } else { AS_TRACE("%s: failed to open the fHTTP stream\n", __PRETTY_FUNCTION__); setState(FAILED); } }
void Audio_Stream::open() { if (m_httpStreamRunning) { AS_TRACE("%s: already running: return\n", __PRETTY_FUNCTION__); return; } if (m_httpStream->open()) { AS_TRACE("%s: HTTP stream opened, buffering...\n", __PRETTY_FUNCTION__); m_httpStreamRunning = true; setState(BUFFERING); } else { AS_TRACE("%s: failed to open the HTTP stream\n", __PRETTY_FUNCTION__); setState(FAILED); } }
void Audio_Stream::streamIsReadyRead() { if (m_audioStreamParserRunning) { AS_TRACE("%s: parser already running!\n", __PRETTY_FUNCTION__); return; } CFStringRef audioContentType = CFSTR("audio/"); const CFIndex audioContentTypeLength = CFStringGetLength(audioContentType); bool matchesAudioContentType = false; CFStringRef contentType = m_httpStream->contentType(); if (m_contentType) { CFRelease(m_contentType), m_contentType = 0; } if (contentType) { m_contentType = CFStringCreateCopy(kCFAllocatorDefault, contentType); /* Check if the stream's MIME type begins with audio/ */ matchesAudioContentType = (kCFCompareEqualTo == CFStringCompareWithOptions(contentType, CFSTR("audio/"), CFRangeMake(0, audioContentTypeLength), 0)); } if (m_strictContentTypeChecking && !matchesAudioContentType) { closeAndSignalError(AS_ERR_OPEN); return; } m_audioDataByteCount = 0; /* OK, it should be an audio stream, let's try to open it */ OSStatus result = AudioFileStreamOpen(this, propertyValueCallback, streamDataCallback, audioStreamTypeFromContentType(contentType), &m_audioFileStream); if (result == 0) { AS_TRACE("%s: audio file stream opened.\n", __PRETTY_FUNCTION__); m_audioStreamParserRunning = true; } else { closeAndSignalError(AS_ERR_OPEN); } }
void Audio_Stream::streamHasBytesAvailable(UInt8 *data, CFIndex numBytes) { AS_TRACE("%s: %lu bytes\n", __FUNCTION__, numBytes); if (!m_httpStreamRunning) { AS_TRACE("%s: stray callback detected!\n", __PRETTY_FUNCTION__); return; } if (m_audioStreamParserRunning) { OSStatus result = AudioFileStreamParseBytes(m_audioFileStream, numBytes, data, 0); if (result != 0) { AS_TRACE("%s: AudioFileStreamParseBytes error %d\n", __PRETTY_FUNCTION__, (int)result); closeAndSignalError(AS_ERR_STREAM_PARSE); } } }
void Audio_Stream::watchdogTimerCallback(CFRunLoopTimerRef timer, void *info) { Audio_Stream *THIS = (Audio_Stream *)info; if (PLAYING != THIS->state()) { AS_TRACE("The stream startup watchdog activated: stream didn't start to play soon enough\n"); THIS->closeAndSignalError(AS_ERR_OPEN); } }
OSStatus Audio_Stream::encoderDataCallback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData) { Audio_Stream *THIS = (Audio_Stream *)inUserData; AS_TRACE("encoderDataCallback called\n"); // Dequeue one packet per time for the decoder queued_packet_t *front = THIS->m_queuedHead; if (!front) { /* * End of stream - Inside your input procedure, you must set the total amount of packets read and the sizes of the data in the AudioBufferList to zero. The input procedure should also return noErr. This will signal the AudioConverter that you are out of data. More specifically, set ioNumberDataPackets and ioBufferList->mDataByteSize to zero in your input proc and return noErr. Where ioNumberDataPackets is the amount of data converted and ioBufferList->mDataByteSize is the size of the amount of data converted in each AudioBuffer within your input procedure callback. Your input procedure may be called a few more times; you should just keep returning zero and noErr. */ AS_TRACE("run out of data to provide for encoding\n"); *ioNumberDataPackets = 0; ioData->mBuffers[0].mDataByteSize = 0; return noErr; } *ioNumberDataPackets = 1; ioData->mBuffers[0].mData = front->data; ioData->mBuffers[0].mDataByteSize = front->desc.mDataByteSize; ioData->mBuffers[0].mNumberChannels = THIS->m_srcFormat.mChannelsPerFrame; if (outDataPacketDescription) { *outDataPacketDescription = &front->desc; } THIS->m_queuedHead = front->next; front->next = NULL; THIS->m_processedPackets.push_front(front); THIS->m_processedPacketsSizeTotal += front->desc.mDataByteSize; THIS->m_processedPacketsCount++; return noErr; }
void Audio_Stream::closeAndSignalError(int errorCode) { AS_TRACE("%s: error %i\n", __PRETTY_FUNCTION__, errorCode); setState(FAILED); close(); if (m_delegate) { m_delegate->audioStreamErrorOccurred(errorCode); } }
void Audio_Stream::closeAudioQueue() { if (!m_audioQueue) { return; } AS_TRACE("Releasing audio queue\n"); m_audioQueue->m_delegate = 0; delete m_audioQueue, m_audioQueue = 0; }
AudioFileTypeID Audio_Stream::audioStreamTypeFromContentType(CFStringRef contentType) { AudioFileTypeID fileTypeHint = kAudioFileMP3Type; if (!contentType) { AS_TRACE("***** Unable to detect the audio stream type: missing content-type! *****\n"); goto out; } if (CFStringCompare(contentType, CFSTR("audio/mpeg"), 0) == kCFCompareEqualTo) { fileTypeHint = kAudioFileMP3Type; AS_TRACE("kAudioFileMP3Type detected\n"); } else if (CFStringCompare(contentType, CFSTR("audio/x-wav"), 0) == kCFCompareEqualTo) { fileTypeHint = kAudioFileWAVEType; AS_TRACE("kAudioFileWAVEType detected\n"); } else if (CFStringCompare(contentType, CFSTR("audio/x-aifc"), 0) == kCFCompareEqualTo) { fileTypeHint = kAudioFileAIFCType; AS_TRACE("kAudioFileAIFCType detected\n"); } else if (CFStringCompare(contentType, CFSTR("audio/x-aiff"), 0) == kCFCompareEqualTo) { fileTypeHint = kAudioFileAIFFType; AS_TRACE("kAudioFileAIFFType detected\n"); } else if (CFStringCompare(contentType, CFSTR("audio/x-m4a"), 0) == kCFCompareEqualTo) { fileTypeHint = kAudioFileM4AType; AS_TRACE("kAudioFileM4AType detected\n"); } else if (CFStringCompare(contentType, CFSTR("audio/mp4"), 0) == kCFCompareEqualTo) { fileTypeHint = kAudioFileMPEG4Type; AS_TRACE("kAudioFileMPEG4Type detected\n"); } else if (CFStringCompare(contentType, CFSTR("audio/x-caf"), 0) == kCFCompareEqualTo) { fileTypeHint = kAudioFileCAFType; AS_TRACE("kAudioFileCAFType detected\n"); } else if (CFStringCompare(contentType, CFSTR("audio/aac"), 0) == kCFCompareEqualTo || CFStringCompare(contentType, CFSTR("audio/aacp"), 0) == kCFCompareEqualTo) { fileTypeHint = kAudioFileAAC_ADTSType; AS_TRACE("kAudioFileAAC_ADTSType detected\n"); } else { AS_TRACE("***** Unable to detect the audio stream type *****\n"); } out: return fileTypeHint; }
AudioFileTypeID Audio_Stream::audioStreamTypeFromContentType(std::string contentType) { AudioFileTypeID fileTypeHint = kAudioFileAAC_ADTSType; if (contentType.compare("") == 0) { AS_TRACE("***** Unable to detect the audio stream type: missing content-type! *****\n"); goto out; } if (contentType.compare("audio/mpeg") == 0) { fileTypeHint = kAudioFileMP3Type; AS_TRACE("kAudioFileMP3Type detected\n"); } else if (contentType.compare("audio/x-wav") == 0) { fileTypeHint = kAudioFileWAVEType; AS_TRACE("kAudioFileWAVEType detected\n"); } else if (contentType.compare("audio/x-aifc") == 0) { fileTypeHint = kAudioFileAIFCType; AS_TRACE("kAudioFileAIFCType detected\n"); } else if (contentType.compare("audio/x-aiff") == 0) { fileTypeHint = kAudioFileAIFFType; AS_TRACE("kAudioFileAIFFType detected\n"); } else if (contentType.compare("audio/x-m4a") == 0) { fileTypeHint = kAudioFileM4AType; AS_TRACE("kAudioFileM4AType detected\n"); } else if (contentType.compare("audio/mp4") == 0) { fileTypeHint = kAudioFileMPEG4Type; AS_TRACE("kAudioFileMPEG4Type detected\n"); } else if (contentType.compare("audio/x-caf") == 0) { fileTypeHint = kAudioFileCAFType; AS_TRACE("kAudioFileCAFType detected\n"); } else if (contentType.compare("audio/aac") == 0 || contentType.compare("audio/aacp") == 0) { fileTypeHint = kAudioFileAAC_ADTSType; AS_TRACE("kAudioFileAAC_ADTSType detected\n"); } else { AS_TRACE("***** Unable to detect the audio stream type from content-type %s *****\n", contentType.c_str()); } out: return fileTypeHint; }
Audio_Queue* Audio_Stream::audioQueue() { if (!m_audioQueue) { AS_TRACE("No audio queue, creating\n"); m_audioQueue = new Audio_Queue(); m_audioQueue->m_delegate = this; m_audioQueue->m_streamDesc = m_dstFormat; } return m_audioQueue; }
void Audio_Stream::close() { AS_TRACE("%s: enter\n", __PRETTY_FUNCTION__); /* Close the HTTP stream first so that the audio stream parser isn't fed with more data to parse */ if (m_httpStreamRunning) { m_httpStream->close(); m_httpStreamRunning = false; } if (m_audioStreamParserRunning) { if (AudioFileStreamClose(m_audioFileStream) != 0) { AS_TRACE("%s: AudioFileStreamClose failed\n", __PRETTY_FUNCTION__); } m_audioStreamParserRunning = false; } m_audioQueue->stop(); AS_TRACE("%s: leave\n", __PRETTY_FUNCTION__); }
void Audio_Stream::close() { AS_TRACE("%s: enter\n", __PRETTY_FUNCTION__); /* Close the HTTP stream first so that the audio stream parser isn't fed with more data to parse */ if (m_httpStreamRunning) { m_httpStream->close(); m_httpStreamRunning = false; } if (m_audioStreamParserRunning) { if (m_audioFileStream) { if (AudioFileStreamClose(m_audioFileStream) != 0) { AS_TRACE("%s: AudioFileStreamClose failed\n", __PRETTY_FUNCTION__); } m_audioFileStream = 0; } m_audioStreamParserRunning = false; } closeAudioQueue(); setState(STOPPED); /* * Free any remaining queud packets for encoding. */ queued_packet_t *cur = m_queuedHead; while (cur) { queued_packet_t *tmp = cur->next; free(cur); cur = tmp; } m_queuedHead = m_queuedTail = 0; AS_TRACE("%s: leave\n", __PRETTY_FUNCTION__); }
void Audio_Stream::audioQueueBuffersEmpty() { AS_TRACE("%s: enter\n", __PRETTY_FUNCTION__); if (m_httpStreamRunning) { /* Still feeding the audio queue with data, don't stop yet */ return; } AS_TRACE("%s: closing the audio queue\n", __PRETTY_FUNCTION__); if (m_audioStreamParserRunning) { if (AudioFileStreamClose(m_audioFileStream) != 0) { AS_TRACE("%s: AudioFileStreamClose failed\n", __PRETTY_FUNCTION__); } m_audioStreamParserRunning = false; } m_audioQueue->stop(); AS_TRACE("%s: leave\n", __PRETTY_FUNCTION__); }
void Audio_Stream::streamEndEncountered() { AS_TRACE("%s\n", __PRETTY_FUNCTION__); if (!m_httpStreamRunning) { AS_TRACE("%s: stray callback detected!\n", __PRETTY_FUNCTION__); return; } setState(END_OF_FILE); m_httpStream->close(); m_httpStreamRunning = false; /* * When the audio playback is fine, the queue will signal * back that the playback has ended. However, if there was * a problem with the playback (a corrupted audio file for instance), * the queue will not signal back. */ if (!m_audioQueue->initialized()) { closeAndSignalError(AS_ERR_STREAM_PARSE); } }
/* This is called by audio file stream parser when it finds property values */ void Audio_Stream::propertyValueCallback(void *inClientData, AudioFileStreamID inAudioFileStream, AudioFileStreamPropertyID inPropertyID, UInt32 *ioFlags) { AS_TRACE("%s\n", __PRETTY_FUNCTION__); Audio_Stream *THIS = static_cast<Audio_Stream*>(inClientData); if (!THIS->m_audioStreamParserRunning) { AS_TRACE("%s: stray callback detected!\n", __PRETTY_FUNCTION__); return; } switch (inPropertyID) { case kAudioFileStreamProperty_DataOffset: { SInt64 offset; UInt32 offsetSize = sizeof(offset); OSStatus result = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataOffset, &offsetSize, &offset); if (result == 0) { THIS->m_dataOffset = offset; } else { AS_TRACE("%s: reading kAudioFileStreamProperty_DataOffset property failed\n", __PRETTY_FUNCTION__); } break; } case kAudioFileStreamProperty_AudioDataByteCount: { UInt32 byteCountSize = sizeof(UInt64); OSStatus err = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_AudioDataByteCount, &byteCountSize, &THIS->m_audioDataByteCount); if (err) { THIS->m_audioDataByteCount = 0; } break; } case kAudioFileStreamProperty_ReadyToProducePackets: { memset(&(THIS->m_srcFormat), 0, sizeof m_srcFormat); UInt32 asbdSize = sizeof(m_srcFormat); OSStatus err = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataFormat, &asbdSize, &(THIS->m_srcFormat)); if (err) { AS_TRACE("Unable to set the src format\n"); break; } THIS->m_packetDuration = THIS->m_srcFormat.mFramesPerPacket / THIS->m_srcFormat.mSampleRate; AS_TRACE("srcFormat, bytes per packet %i\n", (unsigned int)THIS->m_srcFormat.mBytesPerPacket); if (THIS->m_audioConverter) { AudioConverterDispose(THIS->m_audioConverter); } err = AudioConverterNew(&(THIS->m_srcFormat), &(THIS->m_dstFormat), &(THIS->m_audioConverter)); if (err) { AS_TRACE("Error in creating an audio converter, error %i\n", err); THIS->m_initializationError = err; } THIS->setCookiesForStream(inAudioFileStream); THIS->audioQueue()->handlePropertyChange(inAudioFileStream, inPropertyID, ioFlags); break; } default: { THIS->audioQueue()->handlePropertyChange(inAudioFileStream, inPropertyID, ioFlags); break; } } }
void Audio_Stream::audioQueueBuffersEmpty() { AS_TRACE("%s: enter\n", __PRETTY_FUNCTION__); Stream_Configuration *config = Stream_Configuration::configuration(); if (m_httpStreamRunning && FAILED != state()) { /* Still feeding the audio queue with data, don't stop yet */ setState(BUFFERING); if (m_firstBufferingTime == 0) { // Never buffered, just increase the counter m_firstBufferingTime = CFAbsoluteTimeGetCurrent(); m_bounceCount++; AS_TRACE("stream buffered, increasing bounce count %zu, interval %i\n", m_bounceCount, config->bounceInterval); } else { // Buffered before, calculate the difference CFAbsoluteTime cur = CFAbsoluteTimeGetCurrent(); int diff = cur - m_firstBufferingTime; if (diff >= config->bounceInterval) { // More than bounceInterval seconds passed from the last // buffering. So not a continuous bouncing. Reset the // counters. m_bounceCount = 0; m_firstBufferingTime = 0; AS_TRACE("%i seconds passed from last buffering, resetting counters, interval %i\n", diff, config->bounceInterval); } else { m_bounceCount++; AS_TRACE("%i seconds passed from last buffering, increasing bounce count to %zu, interval %i\n", diff, m_bounceCount, config->bounceInterval); } } // Check if we have reached the bounce state if (m_bounceCount >= config->maxBounceCount) { closeAndSignalError(AS_ERR_BOUNCING); } return; } AS_TRACE("%s: closing the audio queue\n", __PRETTY_FUNCTION__); if (m_audioStreamParserRunning) { if (AudioFileStreamClose(m_audioFileStream) != 0) { AS_TRACE("%s: AudioFileStreamClose failed\n", __PRETTY_FUNCTION__); } m_audioStreamParserRunning = false; } // Keep the audio queue running until it has finished playing audioQueue()->stop(false); m_needNewQueue = true; AS_TRACE("%s: leave\n", __PRETTY_FUNCTION__); }
/* This is called by audio file stream parser when it finds packets of audio */ void Audio_Stream::streamDataCallback(void *inClientData, UInt32 inNumberBytes, UInt32 inNumberPackets, const void *inInputData, AudioStreamPacketDescription *inPacketDescriptions) { AS_TRACE("%s: inNumberBytes %u, inNumberPackets %u\n", __FUNCTION__, inNumberBytes, inNumberPackets); Audio_Stream *THIS = static_cast<Audio_Stream*>(inClientData); if (!THIS->m_audioStreamParserRunning) { AS_TRACE("%s: stray callback detected!\n", __PRETTY_FUNCTION__); return; } for (int i = 0; i < inNumberPackets; i++) { /* Allocate the packet */ UInt32 size = inPacketDescriptions[i].mDataByteSize; queued_packet_t *packet = (queued_packet_t *)malloc(sizeof(queued_packet_t) + size); if (THIS->m_bitrateBufferIndex < kAudioStreamBitrateBufferSize) { // Only keep sampling for one buffer cycle; this is to keep the counters (for instance) duration // stable. THIS->m_bitrateBuffer[THIS->m_bitrateBufferIndex++] = 8 * inPacketDescriptions[i].mDataByteSize / THIS->m_packetDuration; } /* Prepare the packet */ packet->next = NULL; packet->desc = inPacketDescriptions[i]; packet->desc.mStartOffset = 0; memcpy(packet->data, (const char *)inInputData + inPacketDescriptions[i].mStartOffset, size); if (THIS->m_queuedHead == NULL) { THIS->m_queuedHead = THIS->m_queuedTail = packet; } else { THIS->m_queuedTail->next = packet; THIS->m_queuedTail = packet; } } int count = 0; queued_packet_t *cur = THIS->m_queuedHead; while (cur) { cur = cur->next; count++; } Stream_Configuration *config = Stream_Configuration::configuration(); if (count > config->decodeQueueSize) { AudioBufferList outputBufferList; outputBufferList.mNumberBuffers = 1; outputBufferList.mBuffers[0].mNumberChannels = THIS->m_dstFormat.mChannelsPerFrame; outputBufferList.mBuffers[0].mDataByteSize = THIS->m_outputBufferSize; outputBufferList.mBuffers[0].mData = THIS->m_outputBuffer; AudioStreamPacketDescription description; description.mStartOffset = 0; description.mDataByteSize = THIS->m_outputBufferSize; description.mVariableFramesInPacket = 0; UInt32 ioOutputDataPackets = THIS->m_outputBufferSize / THIS->m_dstFormat.mBytesPerPacket; AS_TRACE("calling AudioConverterFillComplexBuffer\n"); OSStatus err = AudioConverterFillComplexBuffer(THIS->m_audioConverter, &encoderDataCallback, THIS, &ioOutputDataPackets, &outputBufferList, NULL); if (err == noErr) { AS_TRACE("%i output bytes available for the audio queue\n", (unsigned int)ioOutputDataPackets); if (THIS->m_watchdogTimer) { AS_TRACE("The stream started to play, canceling the watchdog\n"); CFRunLoopTimerInvalidate(THIS->m_watchdogTimer); CFRelease(THIS->m_watchdogTimer), THIS->m_watchdogTimer = 0; } THIS->setState(PLAYING); THIS->audioQueue()->handleAudioPackets(outputBufferList.mBuffers[0].mDataByteSize, outputBufferList.mNumberBuffers, outputBufferList.mBuffers[0].mData, &description); if (THIS->m_delegate) { THIS->m_delegate->samplesAvailable(outputBufferList, description); } for(std::list<queued_packet_t*>::iterator iter = THIS->m_processedPackets.begin(); iter != THIS->m_processedPackets.end(); iter++) { queued_packet_t *cur = *iter; free(cur); } THIS->m_processedPackets.clear(); } else { AS_TRACE("AudioConverterFillComplexBuffer failed, error %i\n", err); } } else { AS_TRACE("Less than %i packets queued, returning...\n", config->decodeQueueSize); } }
void Audio_Stream::open() { if (m_httpStreamRunning) { AS_TRACE("%s: already running: return\n", __PRETTY_FUNCTION__); return; } if (m_needNewQueue && m_audioQueue) { m_needNewQueue = false; closeAudioQueue(); } m_contentLength = 0; m_seekTime = 0; m_bounceCount = 0; m_firstBufferingTime = 0; m_processedPacketsCount = 0; m_bitrateBufferIndex = 0; m_initializationError = noErr; if (m_watchdogTimer) { CFRunLoopTimerInvalidate(m_watchdogTimer); CFRelease(m_watchdogTimer), m_watchdogTimer = 0; } Stream_Configuration *config = Stream_Configuration::configuration(); if (m_contentType) { CFRelease(m_contentType), m_contentType = NULL; } if (m_httpStream->open()) { AS_TRACE("%s: HTTP stream opened, buffering...\n", __PRETTY_FUNCTION__); m_httpStreamRunning = true; setState(BUFFERING); if (config->startupWatchdogPeriod > 0) { /* * Start the WD if we have one requested. In this way we can track * that the stream doesn't stuck forever on the buffering state * (for instance some network error condition) */ CFRunLoopTimerContext ctx = {0, this, NULL, NULL, NULL}; m_watchdogTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + config->startupWatchdogPeriod, 0, 0, 0, watchdogTimerCallback, &ctx); AS_TRACE("Starting the startup watchdog, period %i seconds\n", config->startupWatchdogPeriod); CFRunLoopAddTimer(CFRunLoopGetCurrent(), m_watchdogTimer, kCFRunLoopCommonModes); } } else { AS_TRACE("%s: failed to open the HTTP stream\n", __PRETTY_FUNCTION__); closeAndSignalError(AS_ERR_OPEN); } }