file_stream.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. /*
  2. * This file is part of the FreeStreamer project,
  3. * (C)Copyright 2011-2018 Matias Muhonen <mmu@iki.fi> 穆马帝
  4. * See the file ''LICENSE'' for using the code.
  5. *
  6. * https://github.com/muhku/FreeStreamer
  7. */
  8. #include "file_stream.h"
  9. #include "stream_configuration.h"
  10. namespace astreamer {
  11. File_Stream::File_Stream() :
  12. m_url(0),
  13. m_readStream(0),
  14. m_scheduledInRunLoop(false),
  15. m_readPending(false),
  16. m_fileReadBuffer(0),
  17. m_id3Parser(new ID3_Parser()),
  18. m_contentType(0)
  19. {
  20. m_id3Parser->m_delegate = this;
  21. }
  22. File_Stream::~File_Stream()
  23. {
  24. close();
  25. if (m_fileReadBuffer) {
  26. delete [] m_fileReadBuffer;
  27. m_fileReadBuffer = 0;
  28. }
  29. if (m_url) {
  30. CFRelease(m_url);
  31. m_url = 0;
  32. }
  33. delete m_id3Parser;
  34. m_id3Parser = 0;
  35. if (m_contentType) {
  36. CFRelease(m_contentType);
  37. }
  38. }
  39. Input_Stream_Position File_Stream::position()
  40. {
  41. return m_position;
  42. }
  43. CFStringRef File_Stream::contentType()
  44. {
  45. if (m_contentType) {
  46. // Use the provided content type
  47. return m_contentType;
  48. }
  49. // Try to resolve the content type from the file
  50. CFStringRef contentType = CFSTR("");
  51. CFStringRef pathComponent = 0;
  52. CFIndex len = 0;
  53. CFRange range;
  54. CFStringRef suffix = 0;
  55. if (!m_url) {
  56. goto done;
  57. }
  58. pathComponent = CFURLCopyLastPathComponent(m_url);
  59. if (!pathComponent) {
  60. goto done;
  61. }
  62. len = CFStringGetLength(pathComponent);
  63. if (len > 5) {
  64. range.length = 4;
  65. range.location = len - 4;
  66. suffix = CFStringCreateWithSubstring(kCFAllocatorDefault,
  67. pathComponent,
  68. range);
  69. if (!suffix) {
  70. goto done;
  71. }
  72. // TODO: we should do the content-type resolvation in a better way.
  73. if (CFStringCompare(suffix, CFSTR(".mp3"), 0) == kCFCompareEqualTo) {
  74. contentType = CFSTR("audio/mpeg");
  75. } else if (CFStringCompare(suffix, CFSTR(".m4a"), 0) == kCFCompareEqualTo) {
  76. contentType = CFSTR("audio/x-m4a");
  77. } else if (CFStringCompare(suffix, CFSTR(".mp4"), 0) == kCFCompareEqualTo) {
  78. contentType = CFSTR("audio/mp4");
  79. } else if (CFStringCompare(suffix, CFSTR(".aac"), 0) == kCFCompareEqualTo) {
  80. contentType = CFSTR("audio/aac");
  81. }
  82. }
  83. done:
  84. if (pathComponent) {
  85. CFRelease(pathComponent);
  86. }
  87. if (suffix) {
  88. CFRelease(suffix);
  89. }
  90. return contentType;
  91. }
  92. void File_Stream::setContentType(CFStringRef contentType)
  93. {
  94. if (m_contentType) {
  95. CFRelease(m_contentType);
  96. m_contentType = 0;
  97. }
  98. if (contentType) {
  99. m_contentType = CFStringCreateCopy(kCFAllocatorDefault, contentType);
  100. }
  101. }
  102. size_t File_Stream::contentLength()
  103. {
  104. CFNumberRef length = NULL;
  105. CFErrorRef err = NULL;
  106. if (CFURLCopyResourcePropertyForKey(m_url, kCFURLFileSizeKey, &length, &err)) {
  107. CFIndex fileLength;
  108. if (CFNumberGetValue(length, kCFNumberCFIndexType, &fileLength)) {
  109. CFRelease(length);
  110. return fileLength;
  111. }
  112. }
  113. return 0;
  114. }
  115. bool File_Stream::open()
  116. {
  117. Input_Stream_Position position;
  118. position.start = 0;
  119. position.end = 0;
  120. m_id3Parser->reset();
  121. return open(position);
  122. }
  123. bool File_Stream::open(const Input_Stream_Position& position)
  124. {
  125. bool success = false;
  126. CFStreamClientContext CTX = { 0, this, NULL, NULL, NULL };
  127. /* Already opened a read stream, return */
  128. if (m_readStream) {
  129. goto out;
  130. }
  131. if (!m_url) {
  132. goto out;
  133. }
  134. /* Reset state */
  135. m_position = position;
  136. m_readPending = false;
  137. /* Failed to create a stream */
  138. if (!(m_readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, m_url))) {
  139. goto out;
  140. }
  141. if (m_position.start > 0) {
  142. CFNumberRef position = CFNumberCreate(0, kCFNumberLongLongType, &m_position.start);
  143. CFReadStreamSetProperty(m_readStream, kCFStreamPropertyFileCurrentOffset, position);
  144. CFRelease(position);
  145. }
  146. if (!CFReadStreamSetClient(m_readStream, kCFStreamEventHasBytesAvailable |
  147. kCFStreamEventEndEncountered |
  148. kCFStreamEventErrorOccurred, readCallBack, &CTX)) {
  149. CFRelease(m_readStream);
  150. m_readStream = 0;
  151. goto out;
  152. }
  153. setScheduledInRunLoop(true);
  154. if (!CFReadStreamOpen(m_readStream)) {
  155. /* Open failed: clean */
  156. CFReadStreamSetClient(m_readStream, 0, NULL, NULL);
  157. setScheduledInRunLoop(false);
  158. if (m_readStream) {
  159. CFRelease(m_readStream);
  160. m_readStream = 0;
  161. }
  162. goto out;
  163. }
  164. success = true;
  165. out:
  166. if (success) {
  167. if (m_delegate) {
  168. m_delegate->streamIsReadyRead();
  169. }
  170. }
  171. return success;
  172. }
  173. void File_Stream::close()
  174. {
  175. /* The stream has been already closed */
  176. if (!m_readStream) {
  177. return;
  178. }
  179. CFReadStreamSetClient(m_readStream, 0, NULL, NULL);
  180. setScheduledInRunLoop(false);
  181. CFReadStreamClose(m_readStream);
  182. CFRelease(m_readStream);
  183. m_readStream = 0;
  184. }
  185. void File_Stream::setScheduledInRunLoop(bool scheduledInRunLoop)
  186. {
  187. /* The stream has not been opened, or it has been already closed */
  188. if (!m_readStream) {
  189. return;
  190. }
  191. /* The state doesn't change */
  192. if (m_scheduledInRunLoop == scheduledInRunLoop) {
  193. return;
  194. }
  195. if (m_scheduledInRunLoop) {
  196. CFReadStreamUnscheduleFromRunLoop(m_readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
  197. } else {
  198. if (m_readPending) {
  199. m_readPending = false;
  200. readCallBack(m_readStream, kCFStreamEventHasBytesAvailable, this);
  201. }
  202. CFReadStreamScheduleWithRunLoop(m_readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
  203. }
  204. m_scheduledInRunLoop = scheduledInRunLoop;
  205. }
  206. void File_Stream::setUrl(CFURLRef url)
  207. {
  208. if (m_url) {
  209. CFRelease(m_url);
  210. }
  211. if (url) {
  212. m_url = (CFURLRef)CFRetain(url);
  213. } else {
  214. m_url = NULL;
  215. }
  216. }
  217. bool File_Stream::canHandleUrl(CFURLRef url)
  218. {
  219. if (!url) {
  220. return false;
  221. }
  222. CFStringRef scheme = CFURLCopyScheme(url);
  223. if (scheme) {
  224. if (CFStringCompare(scheme, CFSTR("file"), 0) == kCFCompareEqualTo) {
  225. CFRelease(scheme);
  226. // The only scheme we claim to handle are the local files
  227. return true;
  228. }
  229. CFRelease(scheme);
  230. }
  231. // We don't handle anything else but local files
  232. return false;
  233. }
  234. /* ID3_Parser_Delegate */
  235. void File_Stream::id3metaDataAvailable(std::map<CFStringRef,CFStringRef> metaData)
  236. {
  237. if (m_delegate) {
  238. m_delegate->streamMetaDataAvailable(metaData);
  239. }
  240. }
  241. void File_Stream::id3tagSizeAvailable(UInt32 tagSize)
  242. {
  243. if (m_delegate) {
  244. m_delegate->streamMetaDataByteSizeAvailable(tagSize);
  245. }
  246. }
  247. void File_Stream::readCallBack(CFReadStreamRef stream, CFStreamEventType eventType, void *clientCallBackInfo)
  248. {
  249. File_Stream *THIS = static_cast<File_Stream*>(clientCallBackInfo);
  250. Stream_Configuration *config = Stream_Configuration::configuration();
  251. switch (eventType) {
  252. case kCFStreamEventHasBytesAvailable: {
  253. if (!THIS->m_fileReadBuffer) {
  254. THIS->m_fileReadBuffer = new UInt8[config->httpConnectionBufferSize];
  255. }
  256. while (CFReadStreamHasBytesAvailable(stream)) {
  257. if (!THIS->m_scheduledInRunLoop) {
  258. /*
  259. * This is critical - though the stream has data available,
  260. * do not try to feed the audio queue with data, if it has
  261. * indicated that it doesn't want more data due to buffers
  262. * full.
  263. */
  264. THIS->m_readPending = true;
  265. break;
  266. }
  267. CFIndex bytesRead = CFReadStreamRead(stream, THIS->m_fileReadBuffer, config->httpConnectionBufferSize);
  268. if (CFReadStreamGetStatus(stream) == kCFStreamStatusError ||
  269. bytesRead < 0) {
  270. if (THIS->m_delegate) {
  271. CFStringRef reportedNetworkError = NULL;
  272. CFErrorRef streamError = CFReadStreamCopyError(stream);
  273. if (streamError) {
  274. CFStringRef errorDesc = CFErrorCopyDescription(streamError);
  275. if (errorDesc) {
  276. reportedNetworkError = CFStringCreateCopy(kCFAllocatorDefault, errorDesc);
  277. CFRelease(errorDesc);
  278. }
  279. CFRelease(streamError);
  280. }
  281. THIS->m_delegate->streamErrorOccurred(reportedNetworkError);
  282. if (reportedNetworkError) {
  283. CFRelease(reportedNetworkError);
  284. }
  285. }
  286. break;
  287. }
  288. if (bytesRead > 0) {
  289. if (THIS->m_delegate) {
  290. THIS->m_delegate->streamHasBytesAvailable(THIS->m_fileReadBuffer, (UInt32)bytesRead);
  291. }
  292. if (THIS->m_id3Parser->wantData()) {
  293. THIS->m_id3Parser->feedData(THIS->m_fileReadBuffer, (UInt32)bytesRead);
  294. }
  295. }
  296. }
  297. break;
  298. }
  299. case kCFStreamEventEndEncountered: {
  300. if (THIS->m_delegate) {
  301. THIS->m_delegate->streamEndEncountered();
  302. }
  303. break;
  304. }
  305. case kCFStreamEventErrorOccurred: {
  306. if (THIS->m_delegate) {
  307. CFStringRef reportedNetworkError = NULL;
  308. CFErrorRef streamError = CFReadStreamCopyError(stream);
  309. if (streamError) {
  310. CFStringRef errorDesc = CFErrorCopyDescription(streamError);
  311. if (errorDesc) {
  312. reportedNetworkError = CFStringCreateCopy(kCFAllocatorDefault, errorDesc);
  313. CFRelease(errorDesc);
  314. }
  315. CFRelease(streamError);
  316. }
  317. THIS->m_delegate->streamErrorOccurred(reportedNetworkError);
  318. if (reportedNetworkError) {
  319. CFRelease(reportedNetworkError);
  320. }
  321. }
  322. break;
  323. }
  324. }
  325. }
  326. } // namespace astreamer