diff options
Diffstat (limited to 'Src/replicant/http')
-rw-r--r-- | Src/replicant/http/HTTPPlayback.cpp | 325 | ||||
-rw-r--r-- | Src/replicant/http/HTTPPlayback.h | 37 | ||||
-rw-r--r-- | Src/replicant/http/HTTPPlaybackService.cpp | 28 | ||||
-rw-r--r-- | Src/replicant/http/HTTPPlaybackService.h | 16 | ||||
-rw-r--r-- | Src/replicant/http/api.h | 8 | ||||
-rw-r--r-- | Src/replicant/http/http.vcxproj | 156 | ||||
-rw-r--r-- | Src/replicant/http/ifc_http.h | 68 | ||||
-rw-r--r-- | Src/replicant/http/ifc_http_demuxer.h | 25 | ||||
-rw-r--r-- | Src/replicant/http/main.cpp | 68 | ||||
-rw-r--r-- | Src/replicant/http/svc_http_demuxer.h | 38 |
10 files changed, 769 insertions, 0 deletions
diff --git a/Src/replicant/http/HTTPPlayback.cpp b/Src/replicant/http/HTTPPlayback.cpp new file mode 100644 index 00000000..01200e5a --- /dev/null +++ b/Src/replicant/http/HTTPPlayback.cpp @@ -0,0 +1,325 @@ +#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); +} diff --git a/Src/replicant/http/HTTPPlayback.h b/Src/replicant/http/HTTPPlayback.h new file mode 100644 index 00000000..12082b05 --- /dev/null +++ b/Src/replicant/http/HTTPPlayback.h @@ -0,0 +1,37 @@ +#pragma once +#include "nx/nxonce.h" +#include "jnetlib/jnetlib.h" +#include "http/ifc_http_demuxer.h" +#include "http/ifc_http.h" +#include "nswasabi/PlaybackBase.h" + +class HTTPPlayback : public PlaybackBase, public ifc_http +{ +public: + HTTPPlayback(); + ~HTTPPlayback(); + + int Initialize(nx_uri_t url, ifc_player *player); + /* ifc_http implementation */ + int WASABICALL HTTP_Wake(int mask); + int WASABICALL HTTP_Check(int mask); + int WASABICALL HTTP_Wait(unsigned int milliseconds, int mask); + int WASABICALL HTTP_Sleep(int milliseconds, int mask); + Agave_Seek *WASABICALL HTTP_GetSeek(); + void WASABICALL HTTP_FreeSeek(Agave_Seek *seek); + int WASABICALL HTTP_Seek(uint64_t byte_position); + int WASABICALL HTTP_Seekable(); + int WASABICALL HTTP_AudioOpen(const ifc_audioout::Parameters *format, ifc_audioout **out_output); +private: + int Internal_Connect(uint64_t byte_position); + ifc_http_demuxer *demuxer; + + volatile int paused; + volatile int stopped; + jnl_http_t http; + + int Init(); + + nx_thread_return_t NXTHREADCALL DecodeLoop(); + static nx_thread_return_t NXTHREADCALL HTTPPlayerThreadFunction(nx_thread_parameter_t param); +}; diff --git a/Src/replicant/http/HTTPPlaybackService.cpp b/Src/replicant/http/HTTPPlaybackService.cpp new file mode 100644 index 00000000..cd4d5c76 --- /dev/null +++ b/Src/replicant/http/HTTPPlaybackService.cpp @@ -0,0 +1,28 @@ +#include "HTTPPlaybackService.h" +#include "player/ifc_player.h" +#include "player/ifc_playback.h" +#include "HTTPPlayback.h" +#include "nx/nxpath.h" +#include "nswasabi/ReferenceCounted.h" + +int HTTPPlaybackService::PlaybackService_CreatePlayback(unsigned int pass, nx_uri_t filename, ifc_player *player, ifc_playback **out_playback_object) +{ + if (NXPathProtocol(filename, "http") == NErr_Success) + { + HTTPPlayback *http_playback = new ReferenceCounted<HTTPPlayback>; + if (!http_playback) + return NErr_OutOfMemory; + + int ret = http_playback->Initialize(filename, player); + if (ret != NErr_Success) + { + http_playback->ifc_playback::Release(); + return ret; + } + + *out_playback_object = http_playback; + return NErr_Success; + } + return NErr_False; +} + diff --git a/Src/replicant/http/HTTPPlaybackService.h b/Src/replicant/http/HTTPPlaybackService.h new file mode 100644 index 00000000..b048ce14 --- /dev/null +++ b/Src/replicant/http/HTTPPlaybackService.h @@ -0,0 +1,16 @@ +#pragma once +#include "player/svc_playback.h" +#include "nx/nxstring.h" +#include "nswasabi/ServiceName.h" + +// {672AF800-F239-40e5-8C87-3B4D305B72B2} +static const GUID http_playback_guid = +{ 0x672af800, 0xf239, 0x40e5, { 0x8c, 0x87, 0x3b, 0x4d, 0x30, 0x5b, 0x72, 0xb2 } }; + +class HTTPPlaybackService : public svc_playback +{ +public: + WASABI_SERVICE_NAME("HTTP Playback Service"); + static GUID GetServiceGUID() { return http_playback_guid; } + int WASABICALL PlaybackService_CreatePlayback(unsigned int pass, nx_uri_t filename, ifc_player *player, ifc_playback **out_playback_object); +};
\ No newline at end of file diff --git a/Src/replicant/http/api.h b/Src/replicant/http/api.h new file mode 100644 index 00000000..eb87b249 --- /dev/null +++ b/Src/replicant/http/api.h @@ -0,0 +1,8 @@ +#pragma once +#include "service/api_service.h" +extern api_service *serviceApi; +#define WASABI2_API_SVC serviceApi + +#include "application/api_application.h" +extern api_application *applicationApi; +#define WASABI2_API_APP applicationApi diff --git a/Src/replicant/http/http.vcxproj b/Src/replicant/http/http.vcxproj new file mode 100644 index 00000000..751e9d46 --- /dev/null +++ b/Src/replicant/http/http.vcxproj @@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{B6B8BAE5-BC2C-4A78-97C3-D0A5053F11F2}</ProjectGuid> + <RootNamespace>http</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>x86_Debug\</OutDir> + <IntDir>x86_Debug\</IntDir> + <TargetExt>.w5c</TargetExt> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>x64_Debug\</OutDir> + <IntDir>x64_Debug\</IntDir> + <TargetExt>.w5c</TargetExt> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>x86_Release\</OutDir> + <IntDir>x86_Release\</IntDir> + <TargetExt>.w5c</TargetExt> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>x64_Release\</OutDir> + <IntDir>x64_Release\</IntDir> + <TargetExt>.w5c</TargetExt> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;HTTP_EXPORTS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>..</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <CustomBuildStep> + <Command>copy "$(TargetPath)" "$(ProgramFiles)\Replicant\$(TargetName)$(TargetExt)"</Command> + <Outputs>$(ProgramFiles)\Replicant\$(TargetName)$(TargetExt)</Outputs> + <Inputs>$(TargetPath);%(Inputs)</Inputs> + </CustomBuildStep> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;HTTP_EXPORTS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>..</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <CustomBuildStep> + <Command>copy "$(TargetPath)" "$(ProgramFiles)\Replicant\$(TargetName)$(TargetExt)"</Command> + <Outputs>$(ProgramFiles)\Replicant\$(TargetName)$(TargetExt)</Outputs> + <Inputs>$(TargetPath);%(Inputs)</Inputs> + </CustomBuildStep> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="HTTPPlayback.cpp" /> + <ClCompile Include="HTTPPlaybackService.cpp" /> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="api.h" /> + <ClInclude Include="HTTPPlayback.h" /> + <ClInclude Include="HTTPPlaybackService.h" /> + <ClInclude Include="ifc_http.h" /> + <ClInclude Include="ifc_http_demuxer.h" /> + <ClInclude Include="svc_http_demuxer.h" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\jnetlib\jnetlib.vcxproj"> + <Project>{e105a0a2-7391-47c5-86ac-718003524c3d}</Project> + </ProjectReference> + <ProjectReference Include="..\nswasabi\nswasabi.vcxproj"> + <Project>{480502a0-71da-4bf0-bf99-2720d69a526b}</Project> + </ProjectReference> + <ProjectReference Include="..\nu\nu.vcxproj"> + <Project>{f1f5cd60-0d5b-4cea-9eeb-2f87ff9aa915}</Project> + </ProjectReference> + <ProjectReference Include="..\nx\nx.vcxproj"> + <Project>{2851cf33-337d-44d9-ba6d-30547b1cdef0}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/Src/replicant/http/ifc_http.h b/Src/replicant/http/ifc_http.h new file mode 100644 index 00000000..1e34906f --- /dev/null +++ b/Src/replicant/http/ifc_http.h @@ -0,0 +1,68 @@ +#pragma once +#include "foundation/dispatch.h" +#include "player/ifc_playback.h" +#include "foundation/types.h" +// TODO: benski> not sure that this is the best name for it, but it works for now + +class ifc_http : public Wasabi2::Dispatchable +{ +protected: + ifc_http() : Dispatchable(DISPATCHABLE_VERSION) {} + ~ifc_http() {} +public: + + enum + { + WAKE_KILL=(1<<0), + WAKE_PLAY=(1<<1), + WAKE_PAUSE=(1<<2), + WAKE_STOP=(1<<3), + WAKE_INTERRUPT=(1<<4), + WAKE_UNPAUSE=(1<<5), // this is actually unused in wake_flags, just used as a return value from Wake/WakeReason + WAKE_RESUME=(1<<6), // this is actually unused in wake_flags, just used as a return value from Wake/WakeReason + WAKE_START_MASK = WAKE_PLAY|WAKE_STOP, + WAKE_KILL_MASK = WAKE_KILL|WAKE_STOP, + WAKE_ALL_MASK = WAKE_KILL|WAKE_PLAY|WAKE_PAUSE|WAKE_STOP|WAKE_INTERRUPT, + }; + + // these aren't the best names, either + + // if playback flag isn't ready, sleeps until a flag changes and returns changed flag + int Wake(int mask) { return HTTP_Wake(mask); } + + // checks for pending flags and updates them + int Check(int mask) { return HTTP_Check(mask); } + + // like wake, but only wait a specified amount of time. will return 0 if flags didn't change + int Wait(unsigned int milliseconds, int mask) { return HTTP_Wait(milliseconds, mask); } + + int Sleep(unsigned int milliseconds, int mask) { return HTTP_Sleep(milliseconds, mask); } + + Agave_Seek *GetSeek() { return HTTP_GetSeek(); } + void FreeSeek(Agave_Seek *seek) { HTTP_FreeSeek(seek); } + + int Seek(uint64_t byte_position) { return HTTP_Seek(byte_position); } + + /* returns NErr_True / NErr_False, returns whether or not it's seekable by Range headers. + NErr_True doesn't mean 100% certainity that the stream is seekable. + Note that some protocols (e.g. RTSP) might still be seekable by other means than Range headers. */ + int Seekable() { return HTTP_Seekable(); } + + int AudioOpen(const ifc_audioout::Parameters *format, ifc_audioout **out_output) { return HTTP_AudioOpen(format, out_output); } + + enum + { + DISPATCHABLE_VERSION, + }; +protected: + virtual int WASABICALL HTTP_Wake(int mask)=0; + virtual int WASABICALL HTTP_Check(int mask)=0; + virtual int WASABICALL HTTP_Wait(unsigned int milliseconds, int mask)=0; + virtual int WASABICALL HTTP_Sleep(int milliseconds, int mask)=0; + virtual Agave_Seek *WASABICALL HTTP_GetSeek()=0; + virtual void WASABICALL HTTP_FreeSeek(Agave_Seek *seek)=0; + virtual int WASABICALL HTTP_Seek(uint64_t byte_position)=0; + virtual int WASABICALL HTTP_Seekable()=0; + virtual int WASABICALL HTTP_AudioOpen(const ifc_audioout::Parameters *format, ifc_audioout **out_output)=0; + +}; diff --git a/Src/replicant/http/ifc_http_demuxer.h b/Src/replicant/http/ifc_http_demuxer.h new file mode 100644 index 00000000..4616a6ea --- /dev/null +++ b/Src/replicant/http/ifc_http_demuxer.h @@ -0,0 +1,25 @@ +#pragma once +#include "jnetlib/jnetlib_defines.h" +#include "foundation/dispatch.h" +#include "foundation/error.h" +#include "player/svc_output.h" +#include "player/ifc_player.h" +#include "http/ifc_http.h" +#include "player/ifc_playback_parameters.h" + +class ifc_http_demuxer: public Wasabi2::Dispatchable +{ +protected: + ifc_http_demuxer() : Dispatchable(DISPATCHABLE_VERSION) {} + ~ifc_http_demuxer() {} +public: + enum + { + DISPATCHABLE_VERSION, + }; + + int Run(ifc_http *http_parent, ifc_player *player, ifc_playback_parameters *secondary_parameters) { return HTTPDemuxer_Run(http_parent, player, secondary_parameters); } + +protected: + virtual int WASABICALL HTTPDemuxer_Run(ifc_http *http_parent, ifc_player *player, ifc_playback_parameters *secondary_parameters)=0; +}; diff --git a/Src/replicant/http/main.cpp b/Src/replicant/http/main.cpp new file mode 100644 index 00000000..3e146df5 --- /dev/null +++ b/Src/replicant/http/main.cpp @@ -0,0 +1,68 @@ +#include "api.h" +#include "jnetlib/jnetlib.h" +#include "component/ifc_component.h" +#include "service/ifc_servicefactory.h" +#include "foundation/export.h" +#include "nswasabi/singleton.h" +#include "HTTPPlaybackService.h" + +static SingletonService<HTTPPlaybackService, svc_playback> playback_factory; + +// {446BFBF6-8CE9-4697-844E-8386B5037685} +static const GUID http_component_guid = +{ 0x446bfbf6, 0x8ce9, 0x4697, { 0x84, 0x4e, 0x83, 0x86, 0xb5, 0x3, 0x76, 0x85 } }; + + +class HTTPComponent : public ifc_component +{ +public: + HTTPComponent() : ifc_component(http_component_guid) {} + int WASABICALL Component_Initialize(api_service *service); + int WASABICALL Component_RegisterServices(api_service *service); + + void WASABICALL Component_DeregisterServices(api_service *service); + int WASABICALL Component_Quit(api_service *_service_manager); +}; + +static HTTPComponent http_component; +api_service *WASABI2_API_SVC=0; +api_application *WASABI2_API_APP=0; + +int HTTPComponent::Component_Initialize(api_service *service) +{ + int ret = jnl_init(); + if (ret != NErr_Success) + return ret; + + return NErr_Success; +} + +int HTTPComponent::Component_RegisterServices(api_service *service) +{ + WASABI2_API_SVC = service; + + // get application API + WASABI2_API_SVC->GetService(&WASABI2_API_APP); + + playback_factory.Register(WASABI2_API_SVC); + return NErr_Success; +} + +void HTTPComponent::Component_DeregisterServices(api_service *service) +{ + playback_factory.Deregister(WASABI2_API_SVC); + + if (WASABI2_API_APP) + WASABI2_API_APP->Release(); +} + +int HTTPComponent::Component_Quit(api_service *_service_manager) +{ + jnl_quit(); + return NErr_Success; +} + +extern "C" DLLEXPORT ifc_component *GetWasabi2Component() +{ + return &http_component; +} diff --git a/Src/replicant/http/svc_http_demuxer.h b/Src/replicant/http/svc_http_demuxer.h new file mode 100644 index 00000000..ee3a5abb --- /dev/null +++ b/Src/replicant/http/svc_http_demuxer.h @@ -0,0 +1,38 @@ +#pragma once +#include "jnetlib/jnetlib_defines.h" +#include "foundation/dispatch.h" +#include "foundation/error.h" +#include "http/ifc_http_demuxer.h" + +// {5E3551B0-B0FF-4997-89E4-958545C3EC19} +static const GUID demuxer_service_type_guid = +{ 0x5E3551B0, 0xB0FF, 0x4997, { 0x89, 0xE4, 0x95, 0x85, 0x45, 0xC3, 0xEC, 0x19 } }; + +class svc_http_demuxer: public Wasabi2::Dispatchable +{ +protected: + svc_http_demuxer() : Dispatchable(DISPATCHABLE_VERSION) {} + ~svc_http_demuxer() {} +public: + static GUID GetServiceType() { return demuxer_service_type_guid; } + /* returns types to be added to "Accept" HTTP header */ + const char *EnumerateAcceptedTypes(size_t i) { return HTTPDemuxerService_EnumerateAcceptedTypes(i); } + /* returns a string to be added to the user-agent (e.g. Ultravox/2.1) */ + const char *GetUserAgent() { return HTTPDemuxerService_GetUserAgent(); } + /* allows service to do any necessary customization (mainly for adding headers) */ + void CustomizeHTTP(jnl_http_t http) { HTTPDemuxerService_CustomizeHTTP(http); } + + /* if you create a demuxer, you now own http and are expected to call jnl_http_release on it when you are done */ + /* you can return NErr_TryAgain to let everyone else go first, you'll be called again with pass=1 */ + NError CreateDemuxer(nx_uri_t uri, jnl_http_t http, ifc_http_demuxer **demuxer, int pass) { return HTTPDemuxerService_CreateDemuxer(uri, http, demuxer, pass); } + enum + { + DISPATCHABLE_VERSION, + }; + +protected: + virtual const char *WASABICALL HTTPDemuxerService_EnumerateAcceptedTypes(size_t i) = 0; + virtual const char *WASABICALL HTTPDemuxerService_GetUserAgent() = 0; + virtual void WASABICALL HTTPDemuxerService_CustomizeHTTP(jnl_http_t http) = 0; + virtual NError WASABICALL HTTPDemuxerService_CreateDemuxer(nx_uri_t uri, jnl_http_t http, ifc_http_demuxer **demuxer, int pass) = 0; +}; |