123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908 |
- /*
- * This file is part of the FreeStreamer project,
- * (C)Copyright 2011-2018 Matias Muhonen <mmu@iki.fi> 穆马帝
- * See the file ''LICENSE'' for using the code.
- *
- * https://github.com/muhku/FreeStreamer
- */
- #include "http_stream.h"
- #include "audio_queue.h"
- #include "id3_parser.h"
- #include "stream_configuration.h"
- //#define HS_DEBUG 1
- #if !defined (HS_DEBUG)
- #define HS_TRACE(...) do {} while (0)
- #define HS_TRACE_CFSTRING(X) do {} while (0)
- #else
- #define HS_TRACE(...) printf(__VA_ARGS__)
- #define HS_TRACE_CFSTRING(X) HS_TRACE("%s\n", CFStringGetCStringPtr(X, kCFStringEncodingMacRoman))
- #endif
- /*
- * Comment the following line to disable ID3 tag support:
- */
- #define INCLUDE_ID3TAG_SUPPORT 1
- namespace astreamer {
- CFStringRef HTTP_Stream::httpRequestMethod = CFSTR("GET");
- CFStringRef HTTP_Stream::httpUserAgentHeader = CFSTR("User-Agent");
- CFStringRef HTTP_Stream::httpRangeHeader = CFSTR("Range");
- CFStringRef HTTP_Stream::icyMetaDataHeader = CFSTR("Icy-MetaData");
- CFStringRef HTTP_Stream::icyMetaDataValue = CFSTR("1"); /* always request ICY metadata, if available */
-
- /* HTTP_Stream: public */
- HTTP_Stream::HTTP_Stream() :
- m_readStream(0),
- m_scheduledInRunLoop(false),
- m_readPending(false),
- m_url(0),
- m_httpHeadersParsed(false),
- m_contentType(0),
- m_contentLength(0),
- m_bytesRead(0),
-
- m_icyStream(false),
- m_icyHeaderCR(false),
- m_icyHeadersRead(false),
- m_icyHeadersParsed(false),
-
- m_icyName(0),
-
- m_icyMetaDataInterval(0),
- m_dataByteReadCount(0),
- m_metaDataBytesRemaining(0),
-
- m_httpReadBuffer(0),
- m_icyReadBuffer(0),
-
- m_id3Parser(new ID3_Parser())
- {
- m_id3Parser->m_delegate = this;
- }
- HTTP_Stream::~HTTP_Stream()
- {
- close();
-
- for (std::vector<CFStringRef>::iterator h = m_icyHeaderLines.begin(); h != m_icyHeaderLines.end(); ++h) {
- CFRelease(*h);
- }
-
- m_icyHeaderLines.clear();
-
- if (m_contentType) {
- CFRelease(m_contentType);
- m_contentType = 0;
- }
-
- if (m_icyName) {
- CFRelease(m_icyName);
- m_icyName = 0;
- }
-
- if (m_httpReadBuffer) {
- delete [] m_httpReadBuffer;
- m_httpReadBuffer = 0;
- }
- if (m_icyReadBuffer) {
- delete [] m_icyReadBuffer;
- m_icyReadBuffer = 0;
- }
- if (m_url) {
- CFRelease(m_url);
- m_url = 0;
- }
-
- delete m_id3Parser;
- m_id3Parser = 0;
- }
-
- Input_Stream_Position HTTP_Stream::position()
- {
- return m_position;
- }
-
- CFStringRef HTTP_Stream::contentType()
- {
- return m_contentType;
- }
-
- size_t HTTP_Stream::contentLength()
- {
- return m_contentLength;
- }
-
- bool HTTP_Stream::open()
- {
- Input_Stream_Position position;
- position.start = 0;
- position.end = 0;
-
- m_contentLength = 0;
- #ifdef INCLUDE_ID3TAG_SUPPORT
- m_id3Parser->reset();
- #endif
-
- return open(position);
- }
- bool HTTP_Stream::open(const Input_Stream_Position& position)
- {
- bool success = false;
- CFStreamClientContext CTX = { 0, this, NULL, NULL, NULL };
-
- /* Already opened a read stream, return */
- if (m_readStream) {
- goto out;
- }
-
- /* Reset state */
- m_position = position;
-
- m_readPending = false;
- m_httpHeadersParsed = false;
-
- if (m_contentType) {
- CFRelease(m_contentType);
- m_contentType = NULL;
- }
-
- m_icyStream = false;
- m_icyHeaderCR = false;
- m_icyHeadersRead = false;
- m_icyHeadersParsed = false;
-
- if (m_icyName) {
- CFRelease(m_icyName);
- m_icyName = 0;
- }
-
- for (std::vector<CFStringRef>::iterator h = m_icyHeaderLines.begin(); h != m_icyHeaderLines.end(); ++h) {
- CFRelease(*h);
- }
-
- m_icyHeaderLines.clear();
- m_icyMetaDataInterval = 0;
- m_dataByteReadCount = 0;
- m_metaDataBytesRemaining = 0;
- m_bytesRead = 0;
-
- if (!m_url) {
- goto out;
- }
-
- /* Failed to create a stream */
- if (!(m_readStream = createReadStream(m_url))) {
- goto out;
- }
-
- if (!CFReadStreamSetClient(m_readStream, kCFStreamEventHasBytesAvailable |
- kCFStreamEventEndEncountered |
- kCFStreamEventErrorOccurred, readCallBack, &CTX)) {
- CFRelease(m_readStream);
- m_readStream = 0;
- goto out;
- }
-
- setScheduledInRunLoop(true);
-
- if (!CFReadStreamOpen(m_readStream)) {
- /* Open failed: clean */
- CFReadStreamSetClient(m_readStream, 0, NULL, NULL);
- setScheduledInRunLoop(false);
- if (m_readStream) {
- CFRelease(m_readStream);
- m_readStream = 0;
- }
- goto out;
- }
-
- success = true;
- out:
- return success;
- }
- void HTTP_Stream::close()
- {
- /* The stream has been already closed */
- if (!m_readStream) {
- return;
- }
-
- CFReadStreamSetClient(m_readStream, 0, NULL, NULL);
- setScheduledInRunLoop(false);
- CFReadStreamClose(m_readStream);
- CFRelease(m_readStream);
- m_readStream = 0;
- }
-
- void HTTP_Stream::setScheduledInRunLoop(bool scheduledInRunLoop)
- {
- /* The stream has not been opened, or it has been already closed */
- if (!m_readStream) {
- return;
- }
-
- /* The state doesn't change */
- if (m_scheduledInRunLoop == scheduledInRunLoop) {
- return;
- }
-
- if (m_scheduledInRunLoop) {
- CFReadStreamUnscheduleFromRunLoop(m_readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
- } else {
- if (m_readPending) {
- m_readPending = false;
-
- readCallBack(m_readStream, kCFStreamEventHasBytesAvailable, this);
- }
-
- CFReadStreamScheduleWithRunLoop(m_readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
- }
-
- m_scheduledInRunLoop = scheduledInRunLoop;
- }
-
- void HTTP_Stream::setUrl(CFURLRef url)
- {
- if (m_url) {
- CFRelease(m_url);
- }
- if (url) {
- m_url = (CFURLRef)CFRetain(url);
- } else {
- m_url = NULL;
- }
- }
-
- bool HTTP_Stream::canHandleUrl(CFURLRef url)
- {
- if (!url) {
- return false;
- }
-
- CFStringRef scheme = CFURLCopyScheme(url);
-
- if (scheme) {
- if (CFStringCompare(scheme, CFSTR("file"), 0) == kCFCompareEqualTo) {
- CFRelease(scheme);
-
- // The only scheme we claim not to handle are local files.
- return false;
- }
-
- CFRelease(scheme);
- }
-
- return true;
- }
-
- void HTTP_Stream::id3metaDataAvailable(std::map<CFStringRef,CFStringRef> metaData)
- {
- if (m_delegate) {
- m_delegate->streamMetaDataAvailable(metaData);
- }
- }
-
- void HTTP_Stream::id3tagSizeAvailable(UInt32 tagSize)
- {
- if (m_delegate) {
- m_delegate->streamMetaDataByteSizeAvailable(tagSize);
- }
- }
- /* private */
-
- CFReadStreamRef HTTP_Stream::createReadStream(CFURLRef url)
- {
- CFReadStreamRef readStream = 0;
- CFHTTPMessageRef request = 0;
- CFDictionaryRef proxySettings = 0;
-
- Stream_Configuration *config = Stream_Configuration::configuration();
-
- if (!(request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, httpRequestMethod, url, kCFHTTPVersion1_1))) {
- goto out;
- }
-
- if (config->userAgent) {
- CFHTTPMessageSetHeaderFieldValue(request, httpUserAgentHeader, config->userAgent);
- }
-
- CFHTTPMessageSetHeaderFieldValue(request, icyMetaDataHeader, icyMetaDataValue);
-
- if (m_position.start > 0 && m_position.end > m_position.start) {
- CFStringRef rangeHeaderValue = CFStringCreateWithFormat(NULL,
- NULL,
- CFSTR("bytes=%llu-%llu"),
- m_position.start,
- m_position.end);
-
- CFHTTPMessageSetHeaderFieldValue(request, httpRangeHeader, rangeHeaderValue);
- CFRelease(rangeHeaderValue);
- } else if (m_position.start > 0 && m_position.end < m_position.start) {
- CFStringRef rangeHeaderValue = CFStringCreateWithFormat(NULL,
- NULL,
- CFSTR("bytes=%llu-"),
- m_position.start);
- CFHTTPMessageSetHeaderFieldValue(request, httpRangeHeader, rangeHeaderValue);
- CFRelease(rangeHeaderValue);
- }
-
-
- if (config->predefinedHttpHeaderValues) {
- const CFIndex numKeys = CFDictionaryGetCount(config->predefinedHttpHeaderValues);
-
- if (numKeys > 0) {
- CFTypeRef *keys = (CFTypeRef *) malloc(numKeys * sizeof(CFTypeRef));
-
- if (keys) {
- CFDictionaryGetKeysAndValues(config->predefinedHttpHeaderValues, (const void **) keys, NULL);
-
- for (CFIndex i=0; i < numKeys; i++) {
- CFTypeRef key = keys[i];
-
- if (CFGetTypeID(key) == CFStringGetTypeID()) {
- const void *value = CFDictionaryGetValue(config->predefinedHttpHeaderValues, (const void *) key);
-
- if (value) {
- CFStringRef headerKey = (CFStringRef) key;
-
- CFTypeRef valueRef = (CFTypeRef) value;
-
- if (CFGetTypeID(valueRef) == CFStringGetTypeID()) {
- CFStringRef headerValue = (CFStringRef) valueRef;
-
- HS_TRACE("Setting predefined HTTP header ");
- HS_TRACE_CFSTRING(headerKey);
- HS_TRACE_CFSTRING(headerValue);
-
- CFHTTPMessageSetHeaderFieldValue(request, headerKey, headerValue);
- }
- }
- }
- }
-
- free(keys);
- }
- }
- }
-
- if (!(readStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request))) {
- goto out;
- }
-
- CFReadStreamSetProperty(readStream,
- kCFStreamNetworkServiceType,
- kCFStreamNetworkServiceTypeBackground);
-
- CFReadStreamSetProperty(readStream,
- kCFStreamPropertyHTTPShouldAutoredirect,
- kCFBooleanTrue);
-
- proxySettings = CFNetworkCopySystemProxySettings();
- if (proxySettings) {
- CFReadStreamSetProperty(readStream, kCFStreamPropertyHTTPProxy, proxySettings);
- CFRelease(proxySettings);
- }
-
- out:
- if (request) {
- CFRelease(request);
- }
-
- return readStream;
- }
-
- void HTTP_Stream::parseHttpHeadersIfNeeded(const UInt8 *buf, const CFIndex bufSize)
- {
- if (m_httpHeadersParsed) {
- return;
- }
- m_httpHeadersParsed = true;
-
- /* If the response has the "ICY 200 OK" string,
- * we are dealing with the ShoutCast protocol.
- * The HTTP headers won't be available.
- */
- if (bufSize >= 10 &&
- buf[0] == 0x49 && buf[1] == 0x43 && buf[2] == 0x59 &&
- buf[3] == 0x20 && buf[4] == 0x32 && buf[5] == 0x30 &&
- buf[6] == 0x30 && buf[7] == 0x20 && buf[8] == 0x4F &&
- buf[9] == 0x4B) {
- m_icyStream = true;
-
- HS_TRACE("Detected an IceCast stream\n");
-
- // This is an ICY stream, don't try to parse the HTTP headers
- return;
- }
-
- HS_TRACE("A regular HTTP stream\n");
-
- CFHTTPMessageRef response = (CFHTTPMessageRef)CFReadStreamCopyProperty(m_readStream, kCFStreamPropertyHTTPResponseHeader);
- CFIndex statusCode = 0;
-
- if (response) {
- /*
- * If the server responded with the icy-metaint header, the response
- * body will be encoded in the ShoutCast protocol.
- */
- CFStringRef icyMetaIntString = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("icy-metaint"));
- if (icyMetaIntString) {
- m_icyStream = true;
- m_icyHeadersParsed = true;
- m_icyHeadersRead = true;
- m_icyMetaDataInterval = CFStringGetIntValue(icyMetaIntString);
- CFRelease(icyMetaIntString);
- }
-
- HS_TRACE("icy-metaint: %zu\n", m_icyMetaDataInterval);
-
- statusCode = CFHTTPMessageGetResponseStatusCode(response);
-
- HS_TRACE("HTTP response code %zu", statusCode);
-
- CFStringRef icyNameString = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("icy-name"));
- if (icyNameString) {
- if (m_icyName) {
- CFRelease(m_icyName);
- }
- m_icyName = icyNameString;
-
- if (m_delegate) {
- std::map<CFStringRef,CFStringRef> metadataMap;
-
- metadataMap[CFSTR("IcecastStationName")] = CFStringCreateCopy(kCFAllocatorDefault, m_icyName);
-
- m_delegate->streamMetaDataAvailable(metadataMap);
- }
- }
-
- if (m_contentType) {
- CFRelease(m_contentType);
- }
-
- m_contentType = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Content-Type"));
-
- HS_TRACE("Content-type: ");
- HS_TRACE_CFSTRING(m_contentType);
-
- CFStringRef contentLengthString = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Content-Length"));
- if (contentLengthString) {
- m_contentLength = CFStringGetIntValue(contentLengthString);
-
- CFRelease(contentLengthString);
- }
-
- CFRelease(response);
- }
-
- if (m_delegate &&
- (statusCode == 200 || statusCode == 206)) {
- m_delegate->streamIsReadyRead();
- } else {
- if (m_delegate) {
- CFStringRef statusCodeString = CFStringCreateWithFormat(NULL,
- NULL,
- CFSTR("HTTP response code %d"),
- (unsigned int)statusCode);
- m_delegate->streamErrorOccurred(statusCodeString);
-
- if (statusCodeString) {
- CFRelease(statusCodeString);
- }
- }
- }
- }
-
- void HTTP_Stream::parseICYStream(const UInt8 *buf, const CFIndex bufSize)
- {
- HS_TRACE("Parsing an IceCast stream, received %li bytes\n", bufSize);
-
- CFIndex offset = 0;
- CFIndex bytesFound = 0;
- if (!m_icyHeadersRead) {
- HS_TRACE("ICY headers not read, reading\n");
-
- for (; offset < bufSize; offset++) {
- if (m_icyHeaderCR && buf[offset] == '\n') {
- if (bytesFound > 0) {
- m_icyHeaderLines.push_back(createMetaDataStringWithMostReasonableEncoding(&buf[offset-bytesFound-1], bytesFound));
-
- bytesFound = 0;
-
- HS_TRACE_CFSTRING(m_icyHeaderLines[m_icyHeaderLines.size()-1]);
-
- continue;
- }
-
- HS_TRACE("End of ICY headers\n");
-
- m_icyHeadersRead = true;
- break;
- }
-
- if (buf[offset] == '\r') {
- m_icyHeaderCR = true;
- continue;
- } else {
- m_icyHeaderCR = false;
- }
-
- bytesFound++;
- }
- } else if (!m_icyHeadersParsed) {
- HS_TRACE("ICY headers not parsed, parsing\n");
-
- const CFStringRef icyContentTypeHeader = CFSTR("content-type:");
- const CFStringRef icyMetaDataHeader = CFSTR("icy-metaint:");
- const CFStringRef icyNameHeader = CFSTR("icy-name:");
- const CFIndex icyContenTypeHeaderLength = CFStringGetLength(icyContentTypeHeader);
- const CFIndex icyMetaDataHeaderLength = CFStringGetLength(icyMetaDataHeader);
- const CFIndex icyNameHeaderLength = CFStringGetLength(icyNameHeader);
-
- for (std::vector<CFStringRef>::iterator h = m_icyHeaderLines.begin(); h != m_icyHeaderLines.end(); ++h) {
- CFStringRef line = *h;
- const CFIndex lineLength = CFStringGetLength(line);
-
- if (lineLength == 0) {
- continue;
- }
-
- HS_TRACE_CFSTRING(line);
-
- if (CFStringCompareWithOptions(line,
- icyContentTypeHeader,
- CFRangeMake(0, icyContenTypeHeaderLength),
- 0) == kCFCompareEqualTo) {
- if (m_contentType) {
- CFRelease(m_contentType);
- m_contentType = 0;
- }
- m_contentType = CFStringCreateWithSubstring(kCFAllocatorDefault,
- line,
- CFRangeMake(icyContenTypeHeaderLength, lineLength - icyContenTypeHeaderLength));
-
- }
-
- if (CFStringCompareWithOptions(line,
- icyMetaDataHeader,
- CFRangeMake(0, icyMetaDataHeaderLength),
- 0) == kCFCompareEqualTo) {
- CFStringRef metadataInterval = CFStringCreateWithSubstring(kCFAllocatorDefault,
- line,
- CFRangeMake(icyMetaDataHeaderLength, lineLength - icyMetaDataHeaderLength));
-
- if (metadataInterval) {
- m_icyMetaDataInterval = CFStringGetIntValue(metadataInterval);
-
- CFRelease(metadataInterval);
- } else {
- m_icyMetaDataInterval = 0;
- }
- }
-
- if (CFStringCompareWithOptions(line,
- icyNameHeader,
- CFRangeMake(0, icyNameHeaderLength),
- 0) == kCFCompareEqualTo) {
- if (m_icyName) {
- CFRelease(m_icyName);
- }
-
- m_icyName = CFStringCreateWithSubstring(kCFAllocatorDefault,
- line,
- CFRangeMake(icyNameHeaderLength, lineLength - icyNameHeaderLength));
- }
- }
-
- m_icyHeadersParsed = true;
- offset++;
-
- if (m_delegate) {
- m_delegate->streamIsReadyRead();
- }
- }
-
- Stream_Configuration *config = Stream_Configuration::configuration();
-
- if (!m_icyReadBuffer) {
- m_icyReadBuffer = new UInt8[config->httpConnectionBufferSize];
- }
-
- HS_TRACE("Reading ICY stream for playback\n");
-
- UInt32 i=0;
-
- for (; offset < bufSize; offset++) {
- // is this a metadata byte?
- if (m_metaDataBytesRemaining > 0) {
- m_metaDataBytesRemaining--;
-
- if (m_metaDataBytesRemaining == 0) {
- m_dataByteReadCount = 0;
-
- if (m_delegate && !m_icyMetaData.empty()) {
- std::map<CFStringRef,CFStringRef> metadataMap;
-
- CFStringRef metaData = createMetaDataStringWithMostReasonableEncoding(&m_icyMetaData[0],
- m_icyMetaData.size());
-
- if (!metaData) {
- // Metadata encoding failed, cannot parse.
- m_icyMetaData.clear();
- continue;
- }
-
- CFArrayRef tokens = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault,
- metaData,
- CFSTR(";"));
-
- for (CFIndex i=0, max=CFArrayGetCount(tokens); i < max; i++) {
- CFStringRef token = (CFStringRef) CFArrayGetValueAtIndex(tokens, i);
-
- CFRange foundRange;
-
- if (CFStringFindWithOptions(token,
- CFSTR("='"),
- CFRangeMake(0, CFStringGetLength(token)),
- NULL,
- &foundRange) == true) {
-
- CFRange keyRange = CFRangeMake(0, foundRange.location);
-
- CFStringRef metadaKey = CFStringCreateWithSubstring(kCFAllocatorDefault,
- token,
- keyRange);
-
- CFRange valueRange = CFRangeMake(foundRange.location + 2, CFStringGetLength(token) - keyRange.length - 3);
-
- CFStringRef metadaValue = CFStringCreateWithSubstring(kCFAllocatorDefault,
- token,
- valueRange);
-
- metadataMap[metadaKey] = metadaValue;
- }
- }
-
- CFRelease(tokens);
- CFRelease(metaData);
-
- if (m_icyName) {
- metadataMap[CFSTR("IcecastStationName")] = CFStringCreateCopy(kCFAllocatorDefault, m_icyName);
- }
-
- m_delegate->streamMetaDataAvailable(metadataMap);
- }
- m_icyMetaData.clear();
- continue;
- }
-
- m_icyMetaData.push_back(buf[offset]);
- continue;
- }
-
- // is this the interval byte?
- if (m_icyMetaDataInterval > 0 && m_dataByteReadCount == m_icyMetaDataInterval) {
- m_metaDataBytesRemaining = buf[offset] * 16;
-
- if (m_metaDataBytesRemaining == 0) {
- m_dataByteReadCount = 0;
- }
- continue;
- }
-
- // a data byte
- m_dataByteReadCount++;
- m_icyReadBuffer[i++] = buf[offset];
- }
-
- if (m_delegate && i > 0) {
- m_delegate->streamHasBytesAvailable(m_icyReadBuffer, i);
- }
- }
-
- #define TRY_ENCODING(STR,ENC) STR = CFStringCreateWithBytes(kCFAllocatorDefault, bytes, numBytes, ENC, false); \
- if (STR != NULL) { return STR; }
-
- CFStringRef HTTP_Stream::createMetaDataStringWithMostReasonableEncoding(const UInt8 *bytes, const CFIndex numBytes)
- {
- CFStringRef metaData;
-
- TRY_ENCODING(metaData, kCFStringEncodingUTF8);
- TRY_ENCODING(metaData, kCFStringEncodingISOLatin1);
- TRY_ENCODING(metaData, kCFStringEncodingWindowsLatin1);
- TRY_ENCODING(metaData, kCFStringEncodingNextStepLatin);
- TRY_ENCODING(metaData, kCFStringEncodingISOLatin2);
- TRY_ENCODING(metaData, kCFStringEncodingISOLatin3);
- TRY_ENCODING(metaData, kCFStringEncodingISOLatin4);
- TRY_ENCODING(metaData, kCFStringEncodingISOLatinCyrillic);
- TRY_ENCODING(metaData, kCFStringEncodingISOLatinArabic);
- TRY_ENCODING(metaData, kCFStringEncodingISOLatinGreek);
- TRY_ENCODING(metaData, kCFStringEncodingISOLatinHebrew);
- TRY_ENCODING(metaData, kCFStringEncodingISOLatin5);
- TRY_ENCODING(metaData, kCFStringEncodingISOLatin6);
- TRY_ENCODING(metaData, kCFStringEncodingISOLatinThai);
- TRY_ENCODING(metaData, kCFStringEncodingISOLatin7);
- TRY_ENCODING(metaData, kCFStringEncodingISOLatin8);
- TRY_ENCODING(metaData, kCFStringEncodingISOLatin9);
- TRY_ENCODING(metaData, kCFStringEncodingWindowsLatin2);
- TRY_ENCODING(metaData, kCFStringEncodingWindowsCyrillic);
- TRY_ENCODING(metaData, kCFStringEncodingWindowsGreek);
- TRY_ENCODING(metaData, kCFStringEncodingWindowsLatin5);
- TRY_ENCODING(metaData, kCFStringEncodingWindowsHebrew);
- TRY_ENCODING(metaData, kCFStringEncodingWindowsArabic);
- TRY_ENCODING(metaData, kCFStringEncodingKOI8_R);
- TRY_ENCODING(metaData, kCFStringEncodingBig5);
- TRY_ENCODING(metaData, kCFStringEncodingASCII);
-
- return metaData;
- }
-
- #undef TRY_ENCODING
-
- void HTTP_Stream::readCallBack(CFReadStreamRef stream, CFStreamEventType eventType, void *clientCallBackInfo)
- {
- HTTP_Stream *THIS = static_cast<HTTP_Stream*>(clientCallBackInfo);
-
- Stream_Configuration *config = Stream_Configuration::configuration();
-
- CFStringRef reportedNetworkError = NULL;
-
- switch (eventType) {
- case kCFStreamEventHasBytesAvailable: {
- if (!THIS->m_httpReadBuffer) {
- THIS->m_httpReadBuffer = new UInt8[config->httpConnectionBufferSize];
- }
-
- while (CFReadStreamHasBytesAvailable(stream)) {
- if (!THIS->m_scheduledInRunLoop) {
- /*
- * This is critical - though the stream has data available,
- * do not try to feed the audio queue with data, if it has
- * indicated that it doesn't want more data due to buffers
- * full.
- */
- THIS->m_readPending = true;
- break;
- }
-
- CFIndex bytesRead = CFReadStreamRead(stream, THIS->m_httpReadBuffer, config->httpConnectionBufferSize);
-
- if (CFReadStreamGetStatus(stream) == kCFStreamStatusError ||
- bytesRead < 0) {
- if (THIS->contentLength() > 0) {
- /*
- * Try to recover gracefully if we have a non-continuous stream
- */
- Input_Stream_Position currentPosition = THIS->position();
-
- Input_Stream_Position recoveryPosition;
- recoveryPosition.start = currentPosition.start + THIS->m_bytesRead;
- recoveryPosition.end = THIS->contentLength();
-
- HS_TRACE("Recovering HTTP stream, start %llu\n", recoveryPosition.start);
-
- THIS->open(recoveryPosition);
-
- break;
- }
-
- CFErrorRef streamError = CFReadStreamCopyError(stream);
-
- if (streamError) {
- CFStringRef errorDesc = CFErrorCopyDescription(streamError);
-
- if (errorDesc) {
- reportedNetworkError = CFStringCreateCopy(kCFAllocatorDefault, errorDesc);
-
- CFRelease(errorDesc);
- }
-
- CFRelease(streamError);
- }
-
- if (THIS->m_delegate) {
- THIS->m_delegate->streamErrorOccurred(reportedNetworkError);
-
- if (reportedNetworkError) {
- CFRelease(reportedNetworkError);
- reportedNetworkError = NULL;
- }
- }
- break;
- }
-
- if (bytesRead > 0) {
- THIS->m_bytesRead += bytesRead;
-
- HS_TRACE("Read %li bytes, total %llu\n", bytesRead, THIS->m_bytesRead);
-
- THIS->parseHttpHeadersIfNeeded(THIS->m_httpReadBuffer, bytesRead);
-
- #ifdef INCLUDE_ID3TAG_SUPPORT
- if (!THIS->m_icyStream && THIS->m_id3Parser->wantData()) {
- THIS->m_id3Parser->feedData(THIS->m_httpReadBuffer, (UInt32)bytesRead);
- }
- #endif
-
- if (THIS->m_icyStream) {
- HS_TRACE("Parsing ICY stream\n");
-
- THIS->parseICYStream(THIS->m_httpReadBuffer, bytesRead);
- } else {
- if (THIS->m_delegate) {
- HS_TRACE("Not an ICY stream; calling the delegate back\n");
-
- THIS->m_delegate->streamHasBytesAvailable(THIS->m_httpReadBuffer, (UInt32)bytesRead);
- }
- }
- }
- }
-
- if (reportedNetworkError) {
- CFRelease(reportedNetworkError);
- reportedNetworkError = NULL;
- }
-
- break;
- }
- case kCFStreamEventEndEncountered: {
-
- // This should concerns only non-continous streams
- if (THIS->m_bytesRead < THIS->contentLength()) {
- HS_TRACE("End of stream, but we have read only %llu bytes on a total of %li. Missing: %llu\n", THIS->m_bytesRead, THIS->contentLength(), (THIS->contentLength() - THIS->m_bytesRead));
-
- Input_Stream_Position currentPosition = THIS->position();
-
- Input_Stream_Position recoveryPosition;
- recoveryPosition.start = currentPosition.start + THIS->m_bytesRead;
- recoveryPosition.end = THIS->contentLength();
-
- HS_TRACE("Reopen for the end of the file from byte position: %llu\n", recoveryPosition.start);
- THIS->close();
- THIS->open(recoveryPosition);
- break;
- }
-
- if (THIS->m_delegate) {
- THIS->m_delegate->streamEndEncountered();
- }
- break;
- }
- case kCFStreamEventErrorOccurred: {
- if (THIS->m_delegate) {
- CFStringRef reportedNetworkError = NULL;
- CFErrorRef streamError = CFReadStreamCopyError(stream);
-
- if (streamError) {
- CFStringRef errorDesc = CFErrorCopyDescription(streamError);
-
- if (errorDesc) {
- reportedNetworkError = CFStringCreateCopy(kCFAllocatorDefault, errorDesc);
-
- CFRelease(errorDesc);
- }
-
- CFRelease(streamError);
- }
-
- THIS->m_delegate->streamErrorOccurred(reportedNetworkError);
- if (reportedNetworkError) {
- CFRelease(reportedNetworkError);
- }
- }
- break;
- }
- }
- }
- } // namespace astreamer
|