aboutsummaryrefslogtreecommitdiff
path: root/Src/replicant/http
diff options
context:
space:
mode:
Diffstat (limited to 'Src/replicant/http')
-rw-r--r--Src/replicant/http/HTTPPlayback.cpp325
-rw-r--r--Src/replicant/http/HTTPPlayback.h37
-rw-r--r--Src/replicant/http/HTTPPlaybackService.cpp28
-rw-r--r--Src/replicant/http/HTTPPlaybackService.h16
-rw-r--r--Src/replicant/http/api.h8
-rw-r--r--Src/replicant/http/http.vcxproj156
-rw-r--r--Src/replicant/http/ifc_http.h68
-rw-r--r--Src/replicant/http/ifc_http_demuxer.h25
-rw-r--r--Src/replicant/http/main.cpp68
-rw-r--r--Src/replicant/http/svc_http_demuxer.h38
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;
+};