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;
+}