123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- #include "http/api.h"
- #include "HTTPPlayback.h"
- #include "http/svc_http_demuxer.h"
- #include "service/ifc_servicefactory.h"
- #include <time.h>
- #ifdef _WIN32
- #include "nu/AutoChar.h"
- #endif
- #include "nu/strsafe.h"
- #include "nx/nxsleep.h"
- #ifdef __ANDROID__
- #include <android/log.h> // TODO: replace with generic logging API
- #else
- #define ANDROID_LOG_INFO 0
- #define ANDROID_LOG_ERROR 1
- void __android_log_print(int, const char *, const char *, ...)
- {
- }
- #endif
- #include <time.h>
- HTTPPlayback::HTTPPlayback()
- {
- http=0;
- demuxer=0;
- }
- int HTTPPlayback::Initialize(nx_uri_t url, ifc_player *player)
- {
- int ret = PlaybackBase::Initialize(url, player);
- if (ret != NErr_Success)
- return ret;
- http=0;
- demuxer=0;
- ifc_playback::Retain(); /* the thread needs to hold a reference to this object so that it doesn't disappear out from under us */
- NXThreadCreate(&playback_thread, HTTPPlayerThreadFunction, this);
- return NErr_Success;
- }
- HTTPPlayback::~HTTPPlayback()
- {
- if (demuxer)
- demuxer->Release();
- if (http)
- jnl_http_release(http);
- }
- nx_thread_return_t HTTPPlayback::HTTPPlayerThreadFunction(nx_thread_parameter_t param)
- {
- HTTPPlayback *playback = (HTTPPlayback *)param;
- NXThreadCurrentSetPriority(NX_THREAD_PRIORITY_PLAYBACK);
- nx_thread_return_t ret = playback->DecodeLoop();
- playback->ifc_playback::Release();
- return ret;
- }
- int HTTPPlayback::Init()
- {
- http = jnl_http_create(2*1024*1024, 0);
- if (!http)
- return NErr_OutOfMemory;
-
- return NErr_Success;
- }
- static void SetupHTTP(jnl_http_t http)
- {
- char accept[1024], user_agent[256];
- accept[0]=0;
- user_agent[0]=0;
- size_t accept_length=sizeof(accept)/sizeof(*accept);
- size_t user_agent_length=sizeof(user_agent)/sizeof(*user_agent);
- char *p_accept = accept, *p_user_agent=user_agent;
- const char *application_user_agent = WASABI2_API_APP->GetUserAgent();
- StringCchCopyExA(p_user_agent, user_agent_length, application_user_agent, &p_user_agent, &user_agent_length, 0);
- GUID http_demuxer_guid = svc_http_demuxer::GetServiceType();
- ifc_serviceFactory *sf;
- size_t n = 0;
- while (sf = WASABI2_API_SVC->EnumService(http_demuxer_guid, n++))
- {
- svc_http_demuxer *l = (svc_http_demuxer*)sf->GetInterface();
- if (l)
- {
- const char *this_accept;
- size_t i=0;
- while (this_accept=l->EnumerateAcceptedTypes(i++))
- {
- if (accept == p_accept) // first one added
- StringCchCopyExA(p_accept, accept_length, this_accept, &p_accept, &accept_length, 0);
- else
- StringCchPrintfExA(p_accept, accept_length, &p_accept, &accept_length, 0, ", %s", this_accept);
- }
- const char *this_user_agent = l->GetUserAgent();
- if (this_user_agent)
- {
- StringCchPrintfExA(p_user_agent, user_agent_length, &p_user_agent, &user_agent_length, 0, " %s", this_user_agent);
- }
- l->CustomizeHTTP(http);
- l->Release();
- }
- }
- if (accept != p_accept)
- jnl_http_addheadervalue(http, "Accept", accept);
- jnl_http_addheadervalue(http, "User-Agent", user_agent);
- jnl_http_addheadervalue(http, "Connection", "close");
- }
- static NError FindDemuxer(nx_uri_t uri, jnl_http_t http, ifc_http_demuxer **demuxer)
- {
- GUID http_demuxer_guid = svc_http_demuxer::GetServiceType();
- ifc_serviceFactory *sf;
- bool again;
- int pass=0;
- do
- {
- size_t n = 0;
- again=false;
- while (sf = WASABI2_API_SVC->EnumService(http_demuxer_guid, n++))
- {
- svc_http_demuxer *l = (svc_http_demuxer*)sf->GetInterface();
- if (l)
- {
- NError err = l->CreateDemuxer(uri, http, demuxer, pass);
- if (err == NErr_Success)
- return NErr_Success;
- if (err == NErr_TryAgain)
- again=true;
- }
- }
- pass++;
- } while (again);
- return NErr_NoMatchingImplementation;
- }
- int HTTPPlayback::Internal_Connect(uint64_t byte_position)
- {
- int http_ver = byte_position?1:0;
- if (byte_position != 0)
- {
- char str[512];
- StringCchPrintfA(str, 512, "Range: bytes=%llu-", byte_position);
- jnl_http_addheader(http, str);
- }
- //jnl_http_allow_accept_all_reply_codes(http);
- #ifdef _WIN32
- jnl_http_connect(http, AutoChar(filename->string), http_ver, "GET");
- #else
- jnl_http_connect(http, filename->string, http_ver, "GET");
- #endif
- /* wait for connection */
- time_t start_time = time(0);
- int http_status;
- do
- {
- int ret = PlaybackBase::Sleep(10, PlaybackBase::WAKE_STOP);
- if (ret == PlaybackBase::WAKE_STOP)
- return NErr_Interrupted;
- ret = jnl_http_run(http);
- if (ret == HTTPGET_RUN_ERROR)
- return NErr_ConnectionFailed;
- if (start_time + 15 < time(0))
- return NErr_TimedOut;
- http_status = jnl_http_get_status(http);
- } while (http_status == HTTPGET_STATUS_CONNECTING || http_status == HTTPGET_STATUS_READING_HEADERS);
- if (http_status == HTTPGET_STATUS_ERROR)
- {
- switch(jnl_http_getreplycode(http))
- {
- case 400:
- return NErr_BadRequest;
- case 401:
- // TODO: deal with this specially
- return NErr_Unauthorized;
- case 403:
- // TODO: deal with this specially?
- return NErr_Forbidden;
- case 404:
- return NErr_NotFound;
- case 405:
- return NErr_BadMethod;
- case 406:
- return NErr_NotAcceptable;
- case 407:
- // TODO: deal with this specially
- return NErr_ProxyAuthenticationRequired;
- case 408:
- return NErr_RequestTimeout;
- case 409:
- return NErr_Conflict;
- case 410:
- return NErr_Gone;
- case 500:
- return NErr_InternalServerError;
- case 503:
- return NErr_ServiceUnavailable;
- default:
- return NErr_ConnectionFailed;
- }
- }
- return NErr_Success;
- }
- nx_thread_return_t HTTPPlayback::DecodeLoop()
- {
- player->OnLoaded(filename);
- int ret = Init();
- if (ret != NErr_Success)
- {
- player->OnError(ret);
- return 0;
- }
- SetupHTTP(http);
- /* connect, then find an ifc_http_demuxer */
- ret = Internal_Connect(0);
- if (ret == NErr_Success && FindDemuxer(filename, http, &demuxer) == NErr_Success && demuxer)
- {
- /* turn control over to the demuxer */
- ret = demuxer->Run(this, player, secondary_parameters);
- if (ret == NErr_EndOfFile)
- {
- /* TODO: re-implement the individual demuxers so they keep calling set position for a while */
- player->OnClosed();
- return 0;
- }
- }
- else if (ret == NErr_Interrupted)
- {
- player->OnStopped();
- return 0;
- }
- else if (ret == NErr_TimedOut)
- {
- player->OnError(ret);
- return 0;
- }
- else if (ret == NErr_Success)
- {
- player->OnError(NErr_NoMatchingImplementation);
- return 0;
- }
- else
- {
- __android_log_print(ANDROID_LOG_INFO, "libreplicant", "[http] error: %d, reply code: %d", ret, jnl_http_getreplycode(http));
- player->OnError(ret);
- return 0;
- }
- return 0;
- }
- int HTTPPlayback::HTTP_Wake(int mask)
- {
- return PlaybackBase::Wake(mask);
- }
- int HTTPPlayback::HTTP_Check(int mask)
- {
- return PlaybackBase::Check(mask);
- }
- int HTTPPlayback::HTTP_Wait(unsigned int milliseconds, int mask)
- {
- return PlaybackBase::Wait(milliseconds, mask);
- }
- int HTTPPlayback::HTTP_Sleep(int milliseconds, int mask)
- {
- return PlaybackBase::Sleep(milliseconds, mask);
- }
- Agave_Seek *HTTPPlayback::HTTP_GetSeek()
- {
- return PlaybackBase::GetSeek();
- }
- void HTTPPlayback::HTTP_FreeSeek(Agave_Seek *seek)
- {
- PlaybackBase::FreeSeek(seek);
- }
- int HTTPPlayback::HTTP_Seek(uint64_t byte_position)
- {
- jnl_http_reset_headers(http);
- SetupHTTP(http);
- return Internal_Connect(byte_position);
- }
- #if defined(_WIN32) && !defined(strcasecmp)
- #define strcasecmp _stricmp
- #endif
- int HTTPPlayback::HTTP_Seekable()
- {
- const char *accept_ranges = jnl_http_getheader(http, "accept-ranges");
- if (accept_ranges && !strcasecmp(accept_ranges, "none"))
- return NErr_False; /* server says it doesn't accept ranges */
-
- /* note that not having an accept-ranges header doesn't necessary mean it's not seekable. see RFC2616 14.5 */
- return NErr_True;
- }
- int HTTPPlayback::HTTP_AudioOpen(const ifc_audioout::Parameters *format, ifc_audioout **out_output)
- {
- __android_log_print(ANDROID_LOG_INFO, "libreplicant", "[http] output_service=%x", output_service);
- return output_service->AudioOpen(format, player, secondary_parameters, out_output);
- }
|