Mercurial > projects > hoofbaby
diff deps/Platinum/Source/Devices/FrameStreamer/PltFrameServer.cpp @ 0:3425707ddbf6
Initial import (hopefully this mercurial stuff works...)
author | fraserofthenight |
---|---|
date | Mon, 06 Jul 2009 08:06:28 -0700 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/deps/Platinum/Source/Devices/FrameStreamer/PltFrameServer.cpp Mon Jul 06 08:06:28 2009 -0700 @@ -0,0 +1,412 @@ +/***************************************************************** +| +| Platinum - Frame Server +| +| Copyright (c) 2004-2008, Plutinosoft, LLC. +| All rights reserved. +| http://www.plutinosoft.com +| +| This program is free software; you can redistribute it and/or +| modify it under the terms of the GNU General Public License +| as published by the Free Software Foundation; either version 2 +| of the License, or (at your option) any later version. +| +| OEMs, ISVs, VARs and other distributors that combine and +| distribute commercially licensed software with Platinum software +| and do not wish to distribute the source code for the commercially +| licensed software under version 2, or (at your option) any later +| version, of the GNU General Public License (the "GPL") must enter +| into a commercial license agreement with Plutinosoft, LLC. +| +| This program is distributed in the hope that it will be useful, +| but WITHOUT ANY WARRANTY; without even the implied warranty of +| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +| GNU General Public License for more details. +| +| You should have received a copy of the GNU General Public License +| along with this program; see the file LICENSE.txt. If not, write to +| the Free Software Foundation, Inc., +| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +| http://www.gnu.org/licenses/gpl-2.0.html +| +****************************************************************/ + +/*---------------------------------------------------------------------- +| includes ++---------------------------------------------------------------------*/ +#include "PltUPnP.h" +#include "PltMediaItem.h" +#include "PltService.h" +#include "PltDidl.h" +#include "PltFrameStream.h" +#include "PltFrameServer.h" + +NPT_SET_LOCAL_LOGGER("platinum.media.server.frame") + +/*---------------------------------------------------------------------- +| constants ++---------------------------------------------------------------------*/ +#define BOUNDARY "BOUNDARYGOAWAY" + +/*---------------------------------------------------------------------- +| PLT_SocketPolicyServer ++---------------------------------------------------------------------*/ +class PLT_SocketPolicyServer : public NPT_Thread +{ +public: + PLT_SocketPolicyServer(const char* policy, NPT_UInt32 port = 0) : + m_Policy(policy), + m_Port(port), + m_Aborted(false) {} + + ~PLT_SocketPolicyServer() { + Stop(); + } + + NPT_Result Start() { + NPT_Result result = NPT_FAILURE; + + // bind + // randomly try a port for our http server + int retries = 100; + do { + int random = NPT_System::GetRandomInteger(); + int port = (unsigned short)(50000 + (random % 15000)); + + result = m_Socket.Bind( + NPT_SocketAddress(NPT_IpAddress::Any, m_Port?m_Port:port), + true); + + if (NPT_SUCCEEDED(result) || m_Port) + break; + } while (--retries > 0); + + if (NPT_FAILED(result) || retries == 0) return NPT_FAILURE; + + // remember that we're bound + NPT_SocketInfo info; + m_Socket.GetInfo(info); + m_Port = info.local_address.GetPort(); + + return NPT_Thread::Start(); + } + + NPT_Result Stop() { + m_Aborted = true; + m_Socket.Disconnect(); + + return Wait(); + } + + void Run() { + do { + // wait for a connection + NPT_Socket* client = NULL; + NPT_LOG_FINE_1("waiting for connection on port %d...", m_Port); + NPT_Result result = m_Socket.WaitForNewClient(client, NPT_TIMEOUT_INFINITE); + if (NPT_FAILED(result) || client == NULL) return; + + NPT_SocketInfo client_info; + client->GetInfo(client_info); + NPT_LOG_FINE_2("client connected (%s)", + client_info.local_address.ToString().GetChars(), + client_info.remote_address.ToString().GetChars()); + + // get the output stream + NPT_OutputStreamReference output; + client->GetOutputStream(output); + + NPT_MemoryStream* policy = new NPT_MemoryStream(); + policy->Write(m_Policy.GetChars(), m_Policy.GetLength()); + NPT_InputStreamReference input(policy); + + NPT_StreamToStreamCopy(*input, *output); + + + delete client; + } while (!m_Aborted); + } + + NPT_TcpServerSocket m_Socket; + NPT_String m_Policy; + NPT_UInt32 m_Port; + bool m_Aborted; +}; + +/*---------------------------------------------------------------------- +| PLT_FrameServer::PLT_FrameServer ++---------------------------------------------------------------------*/ +PLT_FrameServer::PLT_FrameServer(PLT_FrameBuffer& frame_buffer, + const char* www_root, + const char* friendly_name, + bool show_ip, + const char* uuid, + NPT_UInt16 port) : + PLT_FileMediaServer(www_root, + friendly_name, + show_ip, + uuid, + port), + m_FrameBuffer(frame_buffer), + m_PolicyServer(NULL) +{ +} + +/*---------------------------------------------------------------------- +| PLT_FrameServer::~PLT_FrameServer ++---------------------------------------------------------------------*/ +PLT_FrameServer::~PLT_FrameServer() +{ + delete m_PolicyServer; +} + +/*---------------------------------------------------------------------- +| PLT_FrameServer::SetupDevice ++---------------------------------------------------------------------*/ +NPT_Result +PLT_FrameServer::SetupDevice() +{ + // FIXME: hack for now: find the first valid non local ip address + // to use in item resources. TODO: we should advertise all ips as + // multiple resources instead. + NPT_List<NPT_String> ips; + PLT_UPnPMessageHelper::GetIPAddresses(ips); + if (ips.GetItemCount() == 0) return NPT_ERROR_INTERNAL; + + // set the base paths for content + m_StreamBaseUri = NPT_HttpUrl(*ips.GetFirstItem(), GetPort(), "/screensplitr"); + + // start the xml socket policy server for flash + m_PolicyServer = new PLT_SocketPolicyServer( + "<cross-domain-policy><allow-access-from domain=\"192.168.1.101\" to-ports=\"5900\" /></cross-domain-policy>", + 8989); + NPT_CHECK_SEVERE(m_PolicyServer->Start()); + + return PLT_FileMediaServer::SetupDevice(); +} + +/*---------------------------------------------------------------------- +| PLT_FrameServer::ProcessHttpRequest ++---------------------------------------------------------------------*/ +NPT_Result +PLT_FrameServer::ProcessHttpRequest(NPT_HttpRequest& request, + const NPT_HttpRequestContext& context, + NPT_HttpResponse& response) +{ + if (request.GetUrl().GetPath().StartsWith(m_StreamBaseUri.GetPath())) { + return ProcessStreamRequest(request, context, response); + } + + return PLT_FileMediaServer::ProcessHttpRequest(request, context, response); +} + +/*---------------------------------------------------------------------- +| PLT_FrameServer::ProcessStreamRequest ++---------------------------------------------------------------------*/ +NPT_Result +PLT_FrameServer::ProcessStreamRequest(NPT_HttpRequest& request, + const NPT_HttpRequestContext& context, + NPT_HttpResponse& response) +{ + NPT_COMPILER_UNUSED(context); + + NPT_LOG_FINE("Received Request:"); + PLT_LOG_HTTP_MESSAGE(NPT_LOG_LEVEL_FINE, &request); + + if (request.GetMethod().Compare("GET")) { + response.SetStatus(500, "Internal Server Error"); + return NPT_SUCCESS; + } + + response.SetProtocol(NPT_HTTP_PROTOCOL_1_0); + response.GetHeaders().SetHeader(NPT_HTTP_HEADER_CONNECTION, "close"); + response.GetHeaders().SetHeader("Cache-Control", "no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0"); + response.GetHeaders().SetHeader("Pragma", "no-cache"); + response.GetHeaders().SetHeader("Expires", "Tue, 4 Jan 2000 02:43:05 GMT"); + + NPT_HttpEntity* entity = new NPT_HttpEntity(); + entity->SetContentType("multipart/x-mixed-replace;boundary=" BOUNDARY); + + NPT_InputStreamReference body(new PLT_InputFrameStream(m_FrameBuffer, BOUNDARY)); + entity->SetInputStream(body, false); + + return response.SetEntity(entity); +} + +/*---------------------------------------------------------------------- +| PLT_FrameServer::OnBrowseMetadata ++---------------------------------------------------------------------*/ +NPT_Result +PLT_FrameServer::OnBrowseMetadata(PLT_ActionReference& action, + const char* object_id, + const PLT_HttpRequestContext& context) +{ + NPT_String didl; + PLT_MediaObjectReference item; + + item = BuildFromID(object_id, &context.GetLocalAddress()); + if (item.IsNull()) { + /* error */ + NPT_LOG_WARNING("PLT_FrameServer::OnBrowse - ObjectID not found."); + action->SetError(701, "No Such Object."); + return NPT_FAILURE; + } + + NPT_String filter; + NPT_CHECK_SEVERE(action->GetArgumentValue("Filter", filter)); + + NPT_String tmp; + NPT_CHECK_SEVERE(PLT_Didl::ToDidl(*item.AsPointer(), filter, tmp)); + + /* add didl header and footer */ + didl = didl_header + tmp + didl_footer; + + NPT_CHECK_SEVERE(action->SetArgumentValue("Result", didl)); + NPT_CHECK_SEVERE(action->SetArgumentValue("NumberReturned", "1")); + NPT_CHECK_SEVERE(action->SetArgumentValue("TotalMatches", "1")); + + // update ID may be wrong here, it should be the one of the container? + // TODO: We need to keep track of the overall updateID of the CDS + NPT_CHECK_SEVERE(action->SetArgumentValue("UpdateId", "1")); + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_FrameServer::OnBrowseDirectChildren ++---------------------------------------------------------------------*/ +NPT_Result +PLT_FrameServer::OnBrowseDirectChildren(PLT_ActionReference& action, + const char* object_id, + const PLT_HttpRequestContext& context) +{ + /* Only root allowed */ + if (!NPT_StringsEqual(object_id, "0")) { + /* error */ + NPT_LOG_WARNING("PLT_FrameServer::OnBrowse - ObjectID not found."); + action->SetError(701, "No Such Object."); + return NPT_FAILURE; + } + + NPT_String startingInd; + NPT_CHECK_SEVERE(action->GetArgumentValue("StartingIndex", startingInd)); + + NPT_UInt32 start_index; + if (NPT_FAILED(startingInd.ToInteger(start_index))) { + action->SetError(412, "Precondition failed"); + return NPT_FAILURE; + } + + NPT_String filter; + NPT_CHECK_SEVERE(action->GetArgumentValue("Filter", filter)); + + NPT_String didl = didl_header; + PLT_MediaObjectReference item; + + unsigned long num_returned = 0; + unsigned long total_matches = 0; + if (start_index == 0) { + item = BuildFromID("1", &context.GetLocalAddress()); + if (!item.IsNull()) { + NPT_String tmp; + NPT_CHECK_SEVERE(PLT_Didl::ToDidl(*item.AsPointer(), filter, tmp)); + didl += tmp; + + num_returned++; + total_matches++; + } + }; + + didl += didl_footer; + + NPT_CHECK_SEVERE(action->SetArgumentValue("Result", didl)); + NPT_CHECK_SEVERE(action->SetArgumentValue("NumberReturned", NPT_String::FromInteger(num_returned))); + NPT_CHECK_SEVERE(action->SetArgumentValue("TotalMatches", NPT_String::FromInteger(total_matches))); + NPT_CHECK_SEVERE(action->SetArgumentValue("UpdateId", "1")); + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_FrameServer::BuildResourceUri ++---------------------------------------------------------------------*/ +NPT_String +PLT_FrameServer::BuildResourceUri(const NPT_HttpUrl& base_uri, + const char* host, + const char* file_path) +{ + NPT_HttpUrl uri = base_uri; + + if (host) uri.SetHost(host); + + if (file_path) { + uri.SetPath(uri.GetPath() + file_path); + } + + return uri.ToStringWithDefaultPort(0); +} + +/*---------------------------------------------------------------------- +| PLT_FrameServer::BuildFromID ++---------------------------------------------------------------------*/ +PLT_MediaObject* +PLT_FrameServer::BuildFromID(const char* id, + const NPT_SocketAddress* req_local_address /* = NULL */) +{ + PLT_MediaItemResource resource; + PLT_MediaObject* object = NULL; + + if (NPT_StringsEqual(id, "1")) { + object = new PLT_MediaItem(); + object->m_ObjectID = "1"; + object->m_ParentID = "0"; + + /* Set the title using the filename for now */ + object->m_Title = "ScreenSplitr"; + + /* Set the protocol Info from the extension */ + resource.m_ProtocolInfo = "http-get:*:video/x-motion-jpg:DLNA.ORG_OP=01"; + + /* Set the resource file size */ + //resource.m_Size = info.m_Size; + + // get list of ip addresses + NPT_List<NPT_String> ips; + NPT_CHECK_LABEL_SEVERE(PLT_UPnPMessageHelper::GetIPAddresses(ips), failure); + + // if we're passed an interface where we received the request from + // move the ip to the top + if (req_local_address && req_local_address->GetIpAddress().ToString() != "0.0.0.0") { + ips.Remove(req_local_address->GetIpAddress().ToString()); + ips.Insert(ips.GetFirstItem(), req_local_address->GetIpAddress().ToString()); + } + + // iterate through list and build list of resources + NPT_List<NPT_String>::Iterator ip = ips.GetFirstItem(); + while (ip) { + /* prepend the base URI and url encode it */ + //resource.m_Uri = NPT_Uri::Encode(uri.ToString(), NPT_Uri::UnsafeCharsToEncode); + resource.m_Uri = BuildResourceUri(m_StreamBaseUri, *ip, NULL); + + object->m_ObjectClass.type = "object.item.videoItem.movie"; + object->m_Resources.Add(resource); + + ++ip; + } + } else if (NPT_StringsEqual(id, "0")) { + object = new PLT_MediaContainer; + + object->m_ObjectID = "0"; + object->m_ParentID = "-1"; + object->m_Title = "Root"; + ((PLT_MediaContainer*)object)->m_ChildrenCount = 1; + + object->m_ObjectClass.type = "object.container"; + } + + return object; + +failure: + delete object; + return NULL; +}