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::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::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::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::streamErrorOccurred() { AS_TRACE("%s\n", __PRETTY_FUNCTION__); if (!m_httpStreamRunning) { AS_TRACE("%s: stray callback detected!\n", __PRETTY_FUNCTION__); return; } closeAndSignalError(AS_ERR_NETWORK); }
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::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); } }
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__); }
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); } }