http_stream.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. /*
  2. * This file is part of the FreeStreamer project,
  3. * (C)Copyright 2011-2013 Matias Muhonen.
  4. * See the file ''LICENSE'' for using the code.
  5. */
  6. #include "http_stream.h"
  7. #include "audio_queue.h"
  8. #include "id3_parser.h"
  9. /*
  10. * Comment the following line to disable ID3 tag support:
  11. */
  12. #define INCLUDE_ID3TAG_SUPPORT 1
  13. namespace astreamer {
  14. const size_t HTTP_Stream::STREAM_BUFSIZ = Audio_Queue::AQ_BUFSIZ;
  15. CFStringRef HTTP_Stream::httpRequestMethod = CFSTR("GET");
  16. CFStringRef HTTP_Stream::httpUserAgentHeader = CFSTR("User-Agent");
  17. CFStringRef HTTP_Stream::httpUserAgentValue = CFSTR("aStreamer/1.0");
  18. CFStringRef HTTP_Stream::httpRangeHeader = CFSTR("Range");
  19. CFStringRef HTTP_Stream::icyMetaDataHeader = CFSTR("Icy-MetaData");
  20. CFStringRef HTTP_Stream::icyMetaDataValue = CFSTR("1"); /* always request ICY metadata, if available */
  21. /* HTTP_Stream: public */
  22. HTTP_Stream::HTTP_Stream() :
  23. m_readStream(0),
  24. m_scheduledInRunLoop(false),
  25. m_delegate(0),
  26. m_url(0),
  27. m_httpHeadersParsed(false),
  28. m_contentLength(0),
  29. m_icyStream(false),
  30. m_icyHeaderCR(false),
  31. m_icyHeadersRead(false),
  32. m_icyHeadersParsed(false),
  33. m_icyMetaDataInterval(0),
  34. m_dataByteReadCount(0),
  35. m_metaDataBytesRemaining(0),
  36. m_httpReadBuffer(0),
  37. m_icyReadBuffer(0),
  38. m_id3Parser(new ID3_Parser())
  39. {
  40. m_id3Parser->m_delegate = this;
  41. }
  42. HTTP_Stream::~HTTP_Stream()
  43. {
  44. close();
  45. if (m_httpReadBuffer) {
  46. delete m_httpReadBuffer, m_httpReadBuffer = 0;
  47. }
  48. if (m_icyReadBuffer) {
  49. delete m_icyReadBuffer, m_icyReadBuffer = 0;
  50. }
  51. if (m_url) {
  52. CFRelease(m_url), m_url = 0;
  53. }
  54. delete m_id3Parser, m_id3Parser = 0;
  55. }
  56. HTTP_Stream_Position HTTP_Stream::position()
  57. {
  58. return m_position;
  59. }
  60. std::string HTTP_Stream::contentType()
  61. {
  62. return m_contentType;
  63. }
  64. size_t HTTP_Stream::contentLength()
  65. {
  66. return m_contentLength;
  67. }
  68. bool HTTP_Stream::open()
  69. {
  70. HTTP_Stream_Position position;
  71. position.start = 0;
  72. position.end = 0;
  73. m_contentLength = 0;
  74. #ifdef INCLUDE_ID3TAG_SUPPORT
  75. m_id3Parser->reset();
  76. #endif
  77. return open(position);
  78. }
  79. bool HTTP_Stream::open(const HTTP_Stream_Position& position)
  80. {
  81. bool success = false;
  82. CFStreamClientContext CTX = { 0, this, NULL, NULL, NULL };
  83. /* Already opened a read stream, return */
  84. if (m_readStream) {
  85. goto out;
  86. }
  87. /* Reset state */
  88. m_position = position;
  89. m_httpHeadersParsed = false;
  90. m_contentType = "";
  91. m_icyStream = false;
  92. m_icyHeaderCR = false;
  93. m_icyHeadersRead = false;
  94. m_icyHeadersParsed = false;
  95. m_icyHeaderLines.clear();
  96. m_icyMetaDataInterval = 0;
  97. m_dataByteReadCount = 0;
  98. m_metaDataBytesRemaining = 0;
  99. /* Failed to create a stream */
  100. if (!(m_readStream = createReadStream(m_url))) {
  101. goto out;
  102. }
  103. if (!CFReadStreamSetClient(m_readStream, kCFStreamEventHasBytesAvailable |
  104. kCFStreamEventEndEncountered |
  105. kCFStreamEventErrorOccurred, readCallBack, &CTX)) {
  106. CFRelease(m_readStream), m_readStream = 0;
  107. goto out;
  108. }
  109. setScheduledInRunLoop(true);
  110. if (!CFReadStreamOpen(m_readStream)) {
  111. /* Open failed: clean */
  112. CFReadStreamSetClient(m_readStream, 0, NULL, NULL);
  113. setScheduledInRunLoop(false);
  114. if (m_readStream) {
  115. CFRelease(m_readStream), m_readStream = 0;
  116. }
  117. goto out;
  118. }
  119. success = true;
  120. out:
  121. return success;
  122. }
  123. void HTTP_Stream::close()
  124. {
  125. /* The stream has been already closed */
  126. if (!m_readStream) {
  127. return;
  128. }
  129. CFReadStreamSetClient(m_readStream, 0, NULL, NULL);
  130. setScheduledInRunLoop(false);
  131. CFReadStreamClose(m_readStream);
  132. CFRelease(m_readStream), m_readStream = 0;
  133. }
  134. void HTTP_Stream::setScheduledInRunLoop(bool scheduledInRunLoop)
  135. {
  136. /* The stream has not been opened, or it has been already closed */
  137. if (!m_readStream) {
  138. return;
  139. }
  140. /* The state doesn't change */
  141. if (m_scheduledInRunLoop == scheduledInRunLoop) {
  142. return;
  143. }
  144. if (m_scheduledInRunLoop) {
  145. CFReadStreamUnscheduleFromRunLoop(m_readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
  146. } else {
  147. CFReadStreamScheduleWithRunLoop(m_readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
  148. }
  149. m_scheduledInRunLoop = scheduledInRunLoop;
  150. }
  151. void HTTP_Stream::setUrl(CFURLRef url)
  152. {
  153. if (m_url) {
  154. CFRelease(m_url);
  155. }
  156. m_url = (CFURLRef)CFRetain(url);
  157. }
  158. void HTTP_Stream::id3metaDataAvailable(std::map<CFStringRef,CFStringRef> metaData)
  159. {
  160. if (m_delegate) {
  161. m_delegate->streamMetaDataAvailable(metaData);
  162. }
  163. }
  164. /* private */
  165. CFReadStreamRef HTTP_Stream::createReadStream(CFURLRef url)
  166. {
  167. CFReadStreamRef readStream = 0;
  168. CFHTTPMessageRef request;
  169. CFDictionaryRef proxySettings;
  170. if (!(request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, httpRequestMethod, url, kCFHTTPVersion1_1))) {
  171. goto out;
  172. }
  173. CFHTTPMessageSetHeaderFieldValue(request, httpUserAgentHeader, httpUserAgentValue);
  174. CFHTTPMessageSetHeaderFieldValue(request, icyMetaDataHeader, icyMetaDataValue);
  175. if (m_position.start > 0 && m_position.end > m_position.start) {
  176. CFStringRef rangeHeaderValue = CFStringCreateWithFormat(NULL,
  177. NULL,
  178. CFSTR("bytes=%lu-%lu"),
  179. m_position.start,
  180. m_position.end);
  181. CFHTTPMessageSetHeaderFieldValue(request, httpRangeHeader, rangeHeaderValue);
  182. CFRelease(rangeHeaderValue);
  183. }
  184. if (!(readStream = CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, request, 0))) {
  185. goto out;
  186. }
  187. CFReadStreamSetProperty(readStream,
  188. kCFStreamPropertyHTTPShouldAutoredirect,
  189. kCFBooleanTrue);
  190. proxySettings = CFNetworkCopySystemProxySettings();
  191. CFReadStreamSetProperty(readStream, kCFStreamPropertyHTTPProxy, proxySettings);
  192. CFRelease(proxySettings);
  193. out:
  194. if (request) {
  195. CFRelease(request);
  196. }
  197. return readStream;
  198. }
  199. void HTTP_Stream::parseHttpHeadersIfNeeded(UInt8 *buf, CFIndex bufSize)
  200. {
  201. if (m_httpHeadersParsed) {
  202. return;
  203. }
  204. m_httpHeadersParsed = true;
  205. /* If the response has the "ICY 200 OK" string,
  206. * we are dealing with the ShoutCast protocol.
  207. * The HTTP headers won't be available.
  208. */
  209. std::string header;
  210. for (CFIndex k=0; k < bufSize; k++) {
  211. UInt8 c = buf[k];
  212. // Ignore non-ASCII chars
  213. if (c < 32 || c > 126) {
  214. continue;
  215. }
  216. header.push_back(c);
  217. }
  218. char *p = strstr(header.c_str(), "ICY 200 OK");
  219. // This is an ICY stream, don't try to parse the HTTP headers
  220. if (p) {
  221. m_icyStream = true;
  222. return;
  223. }
  224. CFHTTPMessageRef response = (CFHTTPMessageRef)CFReadStreamCopyProperty(m_readStream, kCFStreamPropertyHTTPResponseHeader);
  225. if (response) {
  226. /*
  227. * If the server responded with the icy-metaint header, the response
  228. * body will be encoded in the ShoutCast protocol.
  229. */
  230. CFStringRef icyMetaIntString = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("icy-metaint"));
  231. if (icyMetaIntString) {
  232. m_icyStream = true;
  233. m_icyHeadersParsed = true;
  234. m_icyHeadersRead = true;
  235. m_icyMetaDataInterval = CFStringGetIntValue(icyMetaIntString);
  236. CFRelease(icyMetaIntString);
  237. }
  238. CFStringRef contentTypeString = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Content-Type"));
  239. if (contentTypeString) {
  240. CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(contentTypeString), kCFStringEncodingUTF8) + 1;
  241. char *buf = new char[len];
  242. if (CFStringGetCString(contentTypeString, buf, len, kCFStringEncodingUTF8)) {
  243. m_contentType.append(buf);
  244. }
  245. delete[] buf;
  246. CFRelease(contentTypeString);
  247. }
  248. CFStringRef contentLengthString = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Content-Length"));
  249. if (contentLengthString) {
  250. m_contentLength = CFStringGetIntValue(contentLengthString);
  251. CFRelease(contentLengthString);
  252. }
  253. CFRelease(response);
  254. }
  255. if (m_delegate) {
  256. m_delegate->streamIsReadyRead();
  257. }
  258. }
  259. void HTTP_Stream::parseICYStream(UInt8 *buf, CFIndex bufSize)
  260. {
  261. CFIndex offset = 0;
  262. if (!m_icyHeadersRead) {
  263. // TODO: fail after n tries?
  264. for (; offset < bufSize; offset++) {
  265. if (m_icyHeaderCR && buf[offset] == '\n') {
  266. m_icyHeaderLines.push_back(std::string(""));
  267. size_t n = m_icyHeaderLines.size();
  268. /* If the last two lines were empty, we have reached
  269. the end of the headers */
  270. if (n >= 2) {
  271. if (m_icyHeaderLines[n-2].empty() &&
  272. m_icyHeaderLines[n-1].empty()) {
  273. m_icyHeadersRead = true;
  274. break;
  275. }
  276. }
  277. continue;
  278. }
  279. if (buf[offset] == '\r') {
  280. m_icyHeaderCR = true;
  281. continue;
  282. } else {
  283. m_icyHeaderCR = false;
  284. }
  285. size_t numberOfLines = m_icyHeaderLines.size();
  286. if (numberOfLines == 0) {
  287. continue;
  288. }
  289. m_icyHeaderLines[numberOfLines - 1].push_back(buf[offset]);
  290. }
  291. } else if (!m_icyHeadersParsed) {
  292. const char *icyContentTypeHeader = "content-type:";
  293. const char *icyMetaDataHeader = "icy-metaint:";
  294. size_t icyContenTypeHeaderLength = strlen(icyContentTypeHeader);
  295. size_t icyMetaDataHeaderLength = strlen(icyMetaDataHeader);
  296. for (std::vector<std::string>::iterator h = m_icyHeaderLines.begin(); h != m_icyHeaderLines.end(); ++h) {
  297. if (h->compare(0, icyContenTypeHeaderLength, icyContentTypeHeader) == 0) {
  298. m_contentType = h->substr(icyContenTypeHeaderLength, h->length() - icyContenTypeHeaderLength);
  299. }
  300. if (h->compare(0, icyMetaDataHeaderLength, icyMetaDataHeader) == 0) {
  301. m_icyMetaDataInterval = atoi(h->substr(icyMetaDataHeaderLength, h->length() - icyMetaDataHeaderLength).c_str());
  302. }
  303. }
  304. m_icyHeadersParsed = true;
  305. offset++;
  306. if (m_delegate) {
  307. m_delegate->streamIsReadyRead();
  308. }
  309. }
  310. if (!m_icyReadBuffer) {
  311. m_icyReadBuffer = new UInt8[STREAM_BUFSIZ];
  312. }
  313. UInt32 i=0;
  314. for (; offset < bufSize; offset++) {
  315. // is this a metadata byte?
  316. if (m_metaDataBytesRemaining > 0) {
  317. m_metaDataBytesRemaining--;
  318. if (m_metaDataBytesRemaining == 0) {
  319. m_dataByteReadCount = 0;
  320. if (m_delegate && !m_icyMetaData.empty()) {
  321. std::map<CFStringRef,CFStringRef> metadataMap;
  322. CFStringRef metaData = createMetaDataStringWithMostReasonableEncoding(&m_icyMetaData[0],
  323. m_icyMetaData.size());
  324. if (!metaData) {
  325. // Metadata encoding failed, cannot parse.
  326. m_icyMetaData.clear();
  327. continue;
  328. }
  329. CFArrayRef tokens = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault,
  330. metaData,
  331. CFSTR(";"));
  332. for (CFIndex i=0, max=CFArrayGetCount(tokens); i < max; i++) {
  333. CFStringRef token = (CFStringRef) CFArrayGetValueAtIndex(tokens, i);
  334. CFRange foundRange;
  335. if (CFStringFindWithOptions(token,
  336. CFSTR("='"),
  337. CFRangeMake(0, CFStringGetLength(token)),
  338. NULL,
  339. &foundRange) == true) {
  340. CFRange keyRange = CFRangeMake(0, foundRange.location);
  341. CFStringRef metadaKey = CFStringCreateWithSubstring(kCFAllocatorDefault,
  342. token,
  343. keyRange);
  344. CFRange valueRange = CFRangeMake(foundRange.location + 2, CFStringGetLength(token) - keyRange.length - 3);
  345. CFStringRef metadaValue = CFStringCreateWithSubstring(kCFAllocatorDefault,
  346. token,
  347. valueRange);
  348. metadataMap[metadaKey] = metadaValue;
  349. }
  350. }
  351. CFRelease(tokens);
  352. CFRelease(metaData);
  353. m_delegate->streamMetaDataAvailable(metadataMap);
  354. }
  355. m_icyMetaData.clear();
  356. continue;
  357. }
  358. m_icyMetaData.push_back(buf[offset]);
  359. continue;
  360. }
  361. // is this the interval byte?
  362. if (m_icyMetaDataInterval > 0 && m_dataByteReadCount == m_icyMetaDataInterval) {
  363. m_metaDataBytesRemaining = buf[offset] * 16;
  364. if (m_metaDataBytesRemaining == 0) {
  365. m_dataByteReadCount = 0;
  366. }
  367. continue;
  368. }
  369. // a data byte
  370. m_dataByteReadCount++;
  371. m_icyReadBuffer[i++] = buf[offset];
  372. }
  373. if (m_delegate && i > 0) {
  374. m_delegate->streamHasBytesAvailable(m_icyReadBuffer, i);
  375. }
  376. }
  377. CFStringRef HTTP_Stream::createMetaDataStringWithMostReasonableEncoding(const UInt8 *bytes, CFIndex numBytes)
  378. {
  379. // Firstly try UTF-8
  380. CFStringRef metaData;
  381. metaData = CFStringCreateWithBytes(kCFAllocatorDefault,
  382. bytes,
  383. numBytes,
  384. kCFStringEncodingUTF8,
  385. false);
  386. if (metaData) {
  387. return metaData;
  388. }
  389. // Failed, try latin1
  390. metaData = CFStringCreateWithBytes(kCFAllocatorDefault,
  391. bytes,
  392. numBytes,
  393. kCFStringEncodingISOLatin1,
  394. false);
  395. if (metaData) {
  396. return metaData;
  397. }
  398. // Still failed, try ASCII
  399. metaData = CFStringCreateWithBytes(kCFAllocatorDefault,
  400. bytes,
  401. numBytes,
  402. kCFStringEncodingASCII,
  403. false);
  404. return metaData;
  405. }
  406. void HTTP_Stream::readCallBack(CFReadStreamRef stream, CFStreamEventType eventType, void *clientCallBackInfo)
  407. {
  408. HTTP_Stream *THIS = static_cast<HTTP_Stream*>(clientCallBackInfo);
  409. switch (eventType) {
  410. case kCFStreamEventHasBytesAvailable: {
  411. if (!THIS->m_httpReadBuffer) {
  412. THIS->m_httpReadBuffer = new UInt8[STREAM_BUFSIZ];
  413. }
  414. CFIndex bytesRead = CFReadStreamRead(stream, THIS->m_httpReadBuffer, STREAM_BUFSIZ);
  415. if (bytesRead < 0) {
  416. if (THIS->m_delegate) {
  417. THIS->m_delegate->streamErrorOccurred();
  418. }
  419. break;
  420. }
  421. if (bytesRead > 0) {
  422. THIS->parseHttpHeadersIfNeeded(THIS->m_httpReadBuffer, bytesRead);
  423. #ifdef INCLUDE_ID3TAG_SUPPORT
  424. if (THIS->m_id3Parser->wantData()) {
  425. THIS->m_id3Parser->feedData(THIS->m_httpReadBuffer, (UInt32)bytesRead);
  426. }
  427. #endif
  428. if (THIS->m_icyStream) {
  429. THIS->parseICYStream(THIS->m_httpReadBuffer, bytesRead);
  430. } else {
  431. if (THIS->m_delegate) {
  432. THIS->m_delegate->streamHasBytesAvailable(THIS->m_httpReadBuffer, (UInt32)bytesRead);
  433. }
  434. }
  435. }
  436. break;
  437. }
  438. case kCFStreamEventEndEncountered: {
  439. if (THIS->m_delegate) {
  440. THIS->m_delegate->streamEndEncountered();
  441. }
  442. break;
  443. }
  444. case kCFStreamEventErrorOccurred: {
  445. if (THIS->m_delegate) {
  446. THIS->m_delegate->streamErrorOccurred();
  447. }
  448. break;
  449. }
  450. }
  451. }
  452. } // namespace astreamer