HTTPPlayback.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. #include "http/api.h"
  2. #include "HTTPPlayback.h"
  3. #include "http/svc_http_demuxer.h"
  4. #include "service/ifc_servicefactory.h"
  5. #include <time.h>
  6. #ifdef _WIN32
  7. #include "nu/AutoChar.h"
  8. #endif
  9. #include "nu/strsafe.h"
  10. #include "nx/nxsleep.h"
  11. #ifdef __ANDROID__
  12. #include <android/log.h> // TODO: replace with generic logging API
  13. #else
  14. #define ANDROID_LOG_INFO 0
  15. #define ANDROID_LOG_ERROR 1
  16. void __android_log_print(int, const char *, const char *, ...)
  17. {
  18. }
  19. #endif
  20. #include <time.h>
  21. HTTPPlayback::HTTPPlayback()
  22. {
  23. http=0;
  24. demuxer=0;
  25. }
  26. int HTTPPlayback::Initialize(nx_uri_t url, ifc_player *player)
  27. {
  28. int ret = PlaybackBase::Initialize(url, player);
  29. if (ret != NErr_Success)
  30. return ret;
  31. http=0;
  32. demuxer=0;
  33. ifc_playback::Retain(); /* the thread needs to hold a reference to this object so that it doesn't disappear out from under us */
  34. NXThreadCreate(&playback_thread, HTTPPlayerThreadFunction, this);
  35. return NErr_Success;
  36. }
  37. HTTPPlayback::~HTTPPlayback()
  38. {
  39. if (demuxer)
  40. demuxer->Release();
  41. if (http)
  42. jnl_http_release(http);
  43. }
  44. nx_thread_return_t HTTPPlayback::HTTPPlayerThreadFunction(nx_thread_parameter_t param)
  45. {
  46. HTTPPlayback *playback = (HTTPPlayback *)param;
  47. NXThreadCurrentSetPriority(NX_THREAD_PRIORITY_PLAYBACK);
  48. nx_thread_return_t ret = playback->DecodeLoop();
  49. playback->ifc_playback::Release();
  50. return ret;
  51. }
  52. int HTTPPlayback::Init()
  53. {
  54. http = jnl_http_create(2*1024*1024, 0);
  55. if (!http)
  56. return NErr_OutOfMemory;
  57. return NErr_Success;
  58. }
  59. static void SetupHTTP(jnl_http_t http)
  60. {
  61. char accept[1024], user_agent[256];
  62. accept[0]=0;
  63. user_agent[0]=0;
  64. size_t accept_length=sizeof(accept)/sizeof(*accept);
  65. size_t user_agent_length=sizeof(user_agent)/sizeof(*user_agent);
  66. char *p_accept = accept, *p_user_agent=user_agent;
  67. const char *application_user_agent = WASABI2_API_APP->GetUserAgent();
  68. StringCchCopyExA(p_user_agent, user_agent_length, application_user_agent, &p_user_agent, &user_agent_length, 0);
  69. GUID http_demuxer_guid = svc_http_demuxer::GetServiceType();
  70. ifc_serviceFactory *sf;
  71. size_t n = 0;
  72. while (sf = WASABI2_API_SVC->EnumService(http_demuxer_guid, n++))
  73. {
  74. svc_http_demuxer *l = (svc_http_demuxer*)sf->GetInterface();
  75. if (l)
  76. {
  77. const char *this_accept;
  78. size_t i=0;
  79. while (this_accept=l->EnumerateAcceptedTypes(i++))
  80. {
  81. if (accept == p_accept) // first one added
  82. StringCchCopyExA(p_accept, accept_length, this_accept, &p_accept, &accept_length, 0);
  83. else
  84. StringCchPrintfExA(p_accept, accept_length, &p_accept, &accept_length, 0, ", %s", this_accept);
  85. }
  86. const char *this_user_agent = l->GetUserAgent();
  87. if (this_user_agent)
  88. {
  89. StringCchPrintfExA(p_user_agent, user_agent_length, &p_user_agent, &user_agent_length, 0, " %s", this_user_agent);
  90. }
  91. l->CustomizeHTTP(http);
  92. l->Release();
  93. }
  94. }
  95. if (accept != p_accept)
  96. jnl_http_addheadervalue(http, "Accept", accept);
  97. jnl_http_addheadervalue(http, "User-Agent", user_agent);
  98. jnl_http_addheadervalue(http, "Connection", "close");
  99. }
  100. static NError FindDemuxer(nx_uri_t uri, jnl_http_t http, ifc_http_demuxer **demuxer)
  101. {
  102. GUID http_demuxer_guid = svc_http_demuxer::GetServiceType();
  103. ifc_serviceFactory *sf;
  104. bool again;
  105. int pass=0;
  106. do
  107. {
  108. size_t n = 0;
  109. again=false;
  110. while (sf = WASABI2_API_SVC->EnumService(http_demuxer_guid, n++))
  111. {
  112. svc_http_demuxer *l = (svc_http_demuxer*)sf->GetInterface();
  113. if (l)
  114. {
  115. NError err = l->CreateDemuxer(uri, http, demuxer, pass);
  116. if (err == NErr_Success)
  117. return NErr_Success;
  118. if (err == NErr_TryAgain)
  119. again=true;
  120. }
  121. }
  122. pass++;
  123. } while (again);
  124. return NErr_NoMatchingImplementation;
  125. }
  126. int HTTPPlayback::Internal_Connect(uint64_t byte_position)
  127. {
  128. int http_ver = byte_position?1:0;
  129. if (byte_position != 0)
  130. {
  131. char str[512];
  132. StringCchPrintfA(str, 512, "Range: bytes=%llu-", byte_position);
  133. jnl_http_addheader(http, str);
  134. }
  135. //jnl_http_allow_accept_all_reply_codes(http);
  136. #ifdef _WIN32
  137. jnl_http_connect(http, AutoChar(filename->string), http_ver, "GET");
  138. #else
  139. jnl_http_connect(http, filename->string, http_ver, "GET");
  140. #endif
  141. /* wait for connection */
  142. time_t start_time = time(0);
  143. int http_status;
  144. do
  145. {
  146. int ret = PlaybackBase::Sleep(10, PlaybackBase::WAKE_STOP);
  147. if (ret == PlaybackBase::WAKE_STOP)
  148. return NErr_Interrupted;
  149. ret = jnl_http_run(http);
  150. if (ret == HTTPGET_RUN_ERROR)
  151. return NErr_ConnectionFailed;
  152. if (start_time + 15 < time(0))
  153. return NErr_TimedOut;
  154. http_status = jnl_http_get_status(http);
  155. } while (http_status == HTTPGET_STATUS_CONNECTING || http_status == HTTPGET_STATUS_READING_HEADERS);
  156. if (http_status == HTTPGET_STATUS_ERROR)
  157. {
  158. switch(jnl_http_getreplycode(http))
  159. {
  160. case 400:
  161. return NErr_BadRequest;
  162. case 401:
  163. // TODO: deal with this specially
  164. return NErr_Unauthorized;
  165. case 403:
  166. // TODO: deal with this specially?
  167. return NErr_Forbidden;
  168. case 404:
  169. return NErr_NotFound;
  170. case 405:
  171. return NErr_BadMethod;
  172. case 406:
  173. return NErr_NotAcceptable;
  174. case 407:
  175. // TODO: deal with this specially
  176. return NErr_ProxyAuthenticationRequired;
  177. case 408:
  178. return NErr_RequestTimeout;
  179. case 409:
  180. return NErr_Conflict;
  181. case 410:
  182. return NErr_Gone;
  183. case 500:
  184. return NErr_InternalServerError;
  185. case 503:
  186. return NErr_ServiceUnavailable;
  187. default:
  188. return NErr_ConnectionFailed;
  189. }
  190. }
  191. return NErr_Success;
  192. }
  193. nx_thread_return_t HTTPPlayback::DecodeLoop()
  194. {
  195. player->OnLoaded(filename);
  196. int ret = Init();
  197. if (ret != NErr_Success)
  198. {
  199. player->OnError(ret);
  200. return 0;
  201. }
  202. SetupHTTP(http);
  203. /* connect, then find an ifc_http_demuxer */
  204. ret = Internal_Connect(0);
  205. if (ret == NErr_Success && FindDemuxer(filename, http, &demuxer) == NErr_Success && demuxer)
  206. {
  207. /* turn control over to the demuxer */
  208. ret = demuxer->Run(this, player, secondary_parameters);
  209. if (ret == NErr_EndOfFile)
  210. {
  211. /* TODO: re-implement the individual demuxers so they keep calling set position for a while */
  212. player->OnClosed();
  213. return 0;
  214. }
  215. }
  216. else if (ret == NErr_Interrupted)
  217. {
  218. player->OnStopped();
  219. return 0;
  220. }
  221. else if (ret == NErr_TimedOut)
  222. {
  223. player->OnError(ret);
  224. return 0;
  225. }
  226. else if (ret == NErr_Success)
  227. {
  228. player->OnError(NErr_NoMatchingImplementation);
  229. return 0;
  230. }
  231. else
  232. {
  233. __android_log_print(ANDROID_LOG_INFO, "libreplicant", "[http] error: %d, reply code: %d", ret, jnl_http_getreplycode(http));
  234. player->OnError(ret);
  235. return 0;
  236. }
  237. return 0;
  238. }
  239. int HTTPPlayback::HTTP_Wake(int mask)
  240. {
  241. return PlaybackBase::Wake(mask);
  242. }
  243. int HTTPPlayback::HTTP_Check(int mask)
  244. {
  245. return PlaybackBase::Check(mask);
  246. }
  247. int HTTPPlayback::HTTP_Wait(unsigned int milliseconds, int mask)
  248. {
  249. return PlaybackBase::Wait(milliseconds, mask);
  250. }
  251. int HTTPPlayback::HTTP_Sleep(int milliseconds, int mask)
  252. {
  253. return PlaybackBase::Sleep(milliseconds, mask);
  254. }
  255. Agave_Seek *HTTPPlayback::HTTP_GetSeek()
  256. {
  257. return PlaybackBase::GetSeek();
  258. }
  259. void HTTPPlayback::HTTP_FreeSeek(Agave_Seek *seek)
  260. {
  261. PlaybackBase::FreeSeek(seek);
  262. }
  263. int HTTPPlayback::HTTP_Seek(uint64_t byte_position)
  264. {
  265. jnl_http_reset_headers(http);
  266. SetupHTTP(http);
  267. return Internal_Connect(byte_position);
  268. }
  269. #if defined(_WIN32) && !defined(strcasecmp)
  270. #define strcasecmp _stricmp
  271. #endif
  272. int HTTPPlayback::HTTP_Seekable()
  273. {
  274. const char *accept_ranges = jnl_http_getheader(http, "accept-ranges");
  275. if (accept_ranges && !strcasecmp(accept_ranges, "none"))
  276. return NErr_False; /* server says it doesn't accept ranges */
  277. /* note that not having an accept-ranges header doesn't necessary mean it's not seekable. see RFC2616 14.5 */
  278. return NErr_True;
  279. }
  280. int HTTPPlayback::HTTP_AudioOpen(const ifc_audioout::Parameters *format, ifc_audioout **out_output)
  281. {
  282. __android_log_print(ANDROID_LOG_INFO, "libreplicant", "[http] output_service=%x", output_service);
  283. return output_service->AudioOpen(format, player, secondary_parameters, out_output);
  284. }