summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
authorRick Pasetto <rick@lindenlab.com>2009-10-05 17:21:08 +0000
committerRick Pasetto <rick@lindenlab.com>2009-10-05 17:21:08 +0000
commit349eb393bc0666e323feff3b3a7eacab4f9bba84 (patch)
tree6b18e6c30d7ba6472561a1d3504739ad24aecf69 /indra/newview
parent3e29163b47719c55ef3e48adbc57a0ce733768e3 (diff)
DEV-40650: Refactor llmediadataresponder and llmediadatafetcher into a single reusable class
CC Review #7 (monroe) LLMediaDataResponder and LLMediaDataFetcher were helpful classes that interacted with each other, but they were not general enough to cleanly be used for all media service interaction. This change refactors these classes into one (in fact, it is closer to a complete rewrite): LLMediaDataClient. This class has the following design points: - You subclass from it when you want to specialize the responder, and then subclass from LLMediaDataClient::Responder if desired - It has a few inner classes: - LLMediaDataClient::Request, which now holds all of the data pertaining to a request, including retry counts - LLMediaDataClient::Responder, which is now the LLHTTPClient::Responder - LLMediaDataClient::PriorityQueue, which is now a STL priority_queue of Request objects. - LLMediaDataClient::QueueTimer, which is the timer that fires to peel off queue items - LLMediaDataClient::Responder::RetryTimer, which is the timer that is used when 503 errors are received. The encapsulation of these inner classes is a lot cleaner and better reflects the scope of their responsibilities. By and large, the logic hasn't really changed much. However, now there are two subclasses of LLMediaDataClient: one for the ObjectMedia cap and the other for the ObjectMediaNavigate cap. (I decided it was overkill to make three subclasses, one each for GET, UPDATE, and NAVIGATE, but we could still do that). LLVOVolume now instantiates both of these classes as statics (and destroys them on shutdown). They now have very simple API: - LLObjectMediaDataClient::fetchMedia(LLVOVolume*) fetches the media for the given object - LLObjectMediaDataClient::updateMedia(LLVOVolume*) sends an UPDATE of the media from the given object - LLObjectMediaNavigateClient::navigate(LLVOVolume*, U8 texture_index, const std::string &url) navigates the given face (texture_index) on the given object to the given url.
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/CMakeLists.txt6
-rw-r--r--indra/newview/app_settings/settings.xml2
-rwxr-xr-xindra/newview/llmediadataclient.cpp627
-rwxr-xr-xindra/newview/llmediadataclient.h263
-rw-r--r--indra/newview/llviewerobject.cpp1
-rw-r--r--indra/newview/llvovolume.cpp53
-rw-r--r--indra/newview/llvovolume.h7
7 files changed, 910 insertions, 49 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 14a37981a6..d368dd5497 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -264,8 +264,7 @@ set(viewer_SOURCE_FILES
llmaniptranslate.cpp
llmapresponders.cpp
llmediactrl.cpp
- llmediadataresponder.cpp
- llmediadatafetcher.cpp
+ llmediadataclient.cpp
llmediaremotectrl.cpp
llmemoryview.cpp
llmenucommands.cpp
@@ -735,8 +734,7 @@ set(viewer_HEADER_FILES
llmanipscale.h
llmaniptranslate.h
llmapresponders.h
- llmediadataresponder.h
- llmediadatafetcher.h
+ llmediadataclient.h
llmediaremotectrl.h
llmemoryview.h
llmenucommands.h
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 94a5f5c52b..7f96e0761e 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -5294,7 +5294,7 @@
<key>Value</key>
<integer>13</integer>
</map>
- <key>PrimMediaFetchQueueDelay</key>
+ <key>PrimMediaRequestQueueDelay</key>
<map>
<key>Comment</key>
<string>Timer delay for fetching media from the queue (in seconds).</string>
diff --git a/indra/newview/llmediadataclient.cpp b/indra/newview/llmediadataclient.cpp
new file mode 100755
index 0000000000..e3d3f1a96b
--- /dev/null
+++ b/indra/newview/llmediadataclient.cpp
@@ -0,0 +1,627 @@
+/**
+ * @file llmediadataclient.cpp
+ * @brief class for queueing up requests for media data
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmediadataclient.h"
+
+#include <boost/lexical_cast.hpp>
+
+#include "llhttpstatuscodes.h"
+#include "llnotifications.h"
+#include "llsdutil.h"
+#include "llmediaentry.h"
+#include "lltextureentry.h"
+#include "llviewerregion.h"
+#include "llvovolume.h"
+
+//
+// When making a request
+// - obtain the "overall interest score" of the object.
+// This would be the sum of the impls' interest scores.
+// - put the request onto a queue sorted by this score
+// (highest score at the front of the queue)
+// - On a timer, once a second, pull off the head of the queue and send
+// the request.
+// - Any request that gets a 503 still goes through the retry logic
+//
+
+// Some helpful logging macros
+
+//////////////////////////////////////////////////////////////////////////////////////
+//
+// LLMediaDataClient::Request
+//
+//////////////////////////////////////////////////////////////////////////////////////
+/*static*/U32 LLMediaDataClient::Request::sNum = 0;
+
+LLMediaDataClient::Request::Request(const std::string &cap_name,
+ const LLSD& sd_payload,
+ LLVOVolume *obj,
+ LLMediaDataClient *mdc)
+ : mCapName(cap_name),
+ mPayload(sd_payload),
+ mObject(obj),
+ mNum(++sNum),
+ mRetryCount(0),
+ mMDC(mdc)
+{
+}
+
+LLMediaDataClient::Request::~Request()
+{
+ mMDC = NULL;
+ mObject = NULL;
+}
+
+
+std::string LLMediaDataClient::Request::getCapability() const
+{
+ return getObject()->getRegion()->getCapability(getCapName());
+}
+
+// Helper function to get the "type" of request, which just pokes around to
+// discover it.
+LLMediaDataClient::Request::Type LLMediaDataClient::Request::getType() const
+{
+ if (mCapName == "ObjectMediaNavigate")
+ {
+ return NAVIGATE;
+ }
+ else if (mCapName == "ObjectMedia")
+ {
+ const std::string &verb = mPayload["verb"];
+ if (verb == "GET")
+ {
+ return GET;
+ }
+ else if (verb == "UPDATE")
+ {
+ return UPDATE;
+ }
+ }
+ llassert(false);
+ return GET;
+}
+
+const char *LLMediaDataClient::Request::getTypeAsString() const
+{
+ Type t = getType();
+ switch (t)
+ {
+ case GET:
+ return "GET";
+ break;
+ case UPDATE:
+ return "UPDATE";
+ break;
+ case NAVIGATE:
+ return "NAVIGATE";
+ break;
+ }
+ return "";
+}
+
+
+void LLMediaDataClient::Request::reEnqueue() const
+{
+ // I sure hope this doesn't deref a bad pointer:
+ mMDC->enqueue(this);
+}
+
+std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &r)
+{
+ s << "<request>"
+ << "<num>" << r.getNum() << "</num>"
+ << "<type>" << r.getTypeAsString() << "</type>"
+ << "<object_id>" << r.getObject()->getID() << "</object_id>"
+ << "<num_retries>" << r.getRetryCount() << "</num_retries>"
+ << "</request> ";
+ return s;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////
+//
+// LLMediaDataClient::Responder::RetryTimer
+//
+//////////////////////////////////////////////////////////////////////////////////////
+
+LLMediaDataClient::Responder::RetryTimer::RetryTimer(F32 time, Responder *mdr)
+ : LLEventTimer(time), mResponder(mdr)
+{
+}
+
+// virtual
+LLMediaDataClient::Responder::RetryTimer::~RetryTimer()
+{
+ mResponder = NULL;
+}
+
+// virtual
+BOOL LLMediaDataClient::Responder::RetryTimer::tick()
+{
+ // Instead of retrying, we just put the request back onto the queue
+ LL_INFOS("LLMediaDataClient") << "RetryTimer fired for: " << *(mResponder->getRequest()) << "retrying" << LL_ENDL;
+ mResponder->getRequest()->reEnqueue();
+ // Don't fire again
+ return TRUE;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////
+//
+// LLMediaDataClient::Responder
+//
+//////////////////////////////////////////////////////////////////////////////////////
+
+LLMediaDataClient::Responder::Responder(const request_ptr_t &request)
+ : mRequest(request)
+{
+}
+
+LLMediaDataClient::Responder::~Responder()
+{
+ mRequest = NULL;
+}
+
+/*virtual*/
+void LLMediaDataClient::Responder::error(U32 status, const std::string& reason)
+{
+ extern LLControlGroup gSavedSettings;
+
+ if (status == HTTP_SERVICE_UNAVAILABLE)
+ {
+ F32 retry_timeout = gSavedSettings.getF32("PrimMediaRetryTimerDelay");
+ if (retry_timeout <= 0.0)
+ {
+ retry_timeout = (F32)UNAVAILABLE_RETRY_TIMER_DELAY;
+ }
+ LL_INFOS("LLMediaDataClient") << *mRequest << "got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL;
+
+ mRequest->incRetryCount();
+
+ // Start timer (instances are automagically tracked by
+ // InstanceTracker<> and LLEventTimer)
+ new RetryTimer(F32(retry_timeout/*secs*/), this);
+ }
+ else {
+ std::string msg = boost::lexical_cast<std::string>(status) + ": " + reason;
+ LL_INFOS("LLMediaDataClient") << *mRequest << " error(" << msg << ")" << LL_ENDL;
+ LLSD args;
+ args["ERROR"] = msg;
+ LLNotifications::instance().add("ObjectMediaFailure", args);
+ }
+}
+
+
+/*virtual*/
+void LLMediaDataClient::Responder::result(const LLSD& content)
+{
+ LL_INFOS("LLMediaDataClient") << *mRequest << "result : " << ll_pretty_print_sd(content) << LL_ENDL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////
+//
+// LLMediaDataClient::Comparator
+//
+//////////////////////////////////////////////////////////////////////////////////////
+
+bool LLMediaDataClient::Comparator::operator() (const request_ptr_t &o1, const request_ptr_t &o2) const
+{
+ if (o2.isNull()) return true;
+ if (o1.isNull()) return false;
+
+ // The score is intended to be a measure of how close an object is or
+ // how much screen real estate (interest) it takes up
+ // Further away = lower score.
+ // Lesser interest = lower score
+ // For instance, here are some cases:
+ // 1: Two items with no impl, closest one wins
+ // 2: Two items with an impl: interest should rule, but distance is
+ // still taken into account (i.e. something really close might take
+ // precedence over a large item far away)
+ // 3: One item with an impl, another without: item with impl wins
+ // (XXX is that what we want?)
+ // Calculate the scores for each.
+ F64 o1_score = Comparator::getObjectScore(o1->getObject());
+ F64 o2_score = Comparator::getObjectScore(o2->getObject());
+
+ return ( o1_score > o2_score );
+}
+
+// static
+F64 LLMediaDataClient::Comparator::getObjectScore(const ll_vo_volume_ptr_t &obj)
+{
+ // *TODO: make this less expensive?
+ F32 dist = obj->getRenderPosition().length() + 0.1; // avoids div by 0
+ // square the distance so that they are in the same "unit magnitude" as
+ // the interest (which is an area)
+ dist *= dist;
+ F64 interest = (F64)1;
+ int i = 0;
+ int end = obj->getNumTEs();
+ for ( ; i < end; ++i)
+ {
+ const viewer_media_t &impl = obj->getMediaImpl(i);
+ if (!impl.isNull())
+ {
+ interest += impl->getInterest();
+ }
+ }
+
+ return interest/dist;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+//
+// LLMediaDataClient::PriorityQueue
+// Queue of LLVOVolume smart pointers to request media for.
+//
+//////////////////////////////////////////////////////////////////////////////////////
+
+// dump the queue
+std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::PriorityQueue &q)
+{
+ int i = 0;
+ std::vector<LLMediaDataClient::request_ptr_t>::const_iterator iter = q.c.begin();
+ std::vector<LLMediaDataClient::request_ptr_t>::const_iterator end = q.c.end();
+ while (iter < end)
+ {
+ s << "\t" << i << "]: " << (*iter)->getObject()->getID().asString();
+ iter++;
+ i++;
+ }
+ return s;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+//
+// LLMediaDataClient::QueueTimer
+// Queue of LLVOVolume smart pointers to request media for.
+//
+//////////////////////////////////////////////////////////////////////////////////////
+
+LLMediaDataClient::QueueTimer::QueueTimer(F32 time, LLMediaDataClient *mdc)
+ : LLEventTimer(time), mMDC(mdc)
+{
+ mMDC->setIsRunning(true);
+}
+
+LLMediaDataClient::QueueTimer::~QueueTimer()
+{
+ mMDC->setIsRunning(false);
+ mMDC = NULL;
+}
+
+// virtual
+BOOL LLMediaDataClient::QueueTimer::tick()
+{
+ if (NULL == mMDC->pRequestQueue)
+ {
+ // Shutting down? stop.
+ LL_DEBUGS("LLMediaDataClient") << "queue gone" << LL_ENDL;
+ return TRUE;
+ }
+
+ LLMediaDataClient::PriorityQueue &queue = *(mMDC->pRequestQueue);
+
+ if (queue.empty())
+ {
+ LL_DEBUGS("LLMediaDataClient") << "queue empty: " << queue << LL_ENDL;
+ return TRUE;
+ }
+
+ LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() started, queue is: " << queue << LL_ENDL;
+
+ // Peel one off of the items from the queue, and execute request
+ request_ptr_t request = queue.top();
+ llassert(!request.isNull());
+ const ll_vo_volume_ptr_t &object = request->getObject();
+ bool performed_request = false;
+ llassert(!object.isNull());
+ if (!object.isNull() && object->hasMedia())
+ {
+ std::string url = request->getCapability();
+ if (!url.empty())
+ {
+ const LLSD &sd_payload = request->getPayload();
+ LL_INFOS("LLMediaDataClient") << "Sending request for " << *request << LL_ENDL;
+
+ // Call the subclass for creating the responder
+ LLHTTPClient::post(url, sd_payload, mMDC->createResponder(request));
+ performed_request = true;
+ }
+ else {
+ LL_INFOS("LLMediaDataClient") << "NOT Sending request for " << *request << ": empty cap url!" << LL_ENDL;
+ }
+ }
+ else {
+ if (!object->hasMedia())
+ {
+ LL_INFOS("LLMediaDataClient") << "Not Sending request for " << *request << " hasMedia() is false!" << LL_ENDL;
+ }
+ }
+ bool exceeded_retries = request->getRetryCount() > LLMediaDataClient::MAX_RETRIES;
+ if (performed_request || exceeded_retries) // Try N times before giving up
+ {
+ if (exceeded_retries)
+ {
+ LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for " << LLMediaDataClient::MAX_RETRIES << " tries...popping object id " << object->getID() << LL_ENDL;
+ // XXX Should we bring up a warning dialog??
+ }
+ queue.pop();
+ }
+ else {
+ request->incRetryCount();
+ }
+ LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, queue is now: " << (*(mMDC->pRequestQueue)) << LL_ENDL;
+
+ return queue.empty();
+}
+
+void LLMediaDataClient::startQueueTimer()
+{
+ if (! mQueueTimerIsRunning)
+ {
+ extern LLControlGroup gSavedSettings;
+ F32 queue_timer_delay = gSavedSettings.getF32("PrimMediaRequestQueueDelay");
+ if (queue_timer_delay <= 0.0f)
+ {
+ queue_timer_delay = (F32)LLMediaDataClient::QUEUE_TIMER_DELAY;
+ }
+ LL_INFOS("LLMediaDataClient") << "starting queue timer (delay=" << queue_timer_delay << " seconds)" << LL_ENDL;
+ // LLEventTimer automagically takes care of the lifetime of this object
+ new QueueTimer(queue_timer_delay, this);
+ }
+}
+
+void LLMediaDataClient::stopQueueTimer()
+{
+ mQueueTimerIsRunning = false;
+}
+
+void LLMediaDataClient::request(LLVOVolume *object, const LLSD &payload)
+{
+ if (NULL == object || ! object->hasMedia()) return;
+
+ // Push the object on the priority queue
+ enqueue(new Request(getCapabilityName(), payload, object, this));
+}
+
+void LLMediaDataClient::enqueue(const Request *request)
+{
+ LL_INFOS("LLMediaDataClient") << "Queuing request for " << *request << LL_ENDL;
+ // Push the request on the priority queue
+ // Sadly, we have to const-cast because items put into the queue are not const
+ pRequestQueue->push(const_cast<LLMediaDataClient::Request*>(request));
+ LL_DEBUGS("LLMediaDataClient") << "Queue:" << (*pRequestQueue) << LL_ENDL;
+ // Start the timer if not already running
+ startQueueTimer();
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+//
+// LLMediaDataClient
+//
+//////////////////////////////////////////////////////////////////////////////////////
+
+LLMediaDataClient::LLMediaDataClient()
+{
+ pRequestQueue = new PriorityQueue();
+}
+
+
+LLMediaDataClient::~LLMediaDataClient()
+{
+ stopQueueTimer();
+
+ // This should clear the queue, and hopefully call all the destructors.
+ while (! pRequestQueue->empty()) pRequestQueue->pop();
+
+ delete pRequestQueue;
+ pRequestQueue = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+//
+// LLObjectMediaDataClient
+// Subclass of LLMediaDataClient for the ObjectMedia cap
+//
+//////////////////////////////////////////////////////////////////////////////////////
+
+LLMediaDataClient::Responder *LLObjectMediaDataClient::createResponder(const request_ptr_t &request) const
+{
+ return new LLObjectMediaDataClient::Responder(request);
+}
+
+const char *LLObjectMediaDataClient::getCapabilityName() const
+{
+ return "ObjectMedia";
+}
+
+void LLObjectMediaDataClient::fetchMedia(LLVOVolume *object)
+{
+ LLSD sd_payload;
+ sd_payload["verb"] = "GET";
+ sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID();
+ request(object, sd_payload);
+}
+
+void LLObjectMediaDataClient::updateMedia(LLVOVolume *object)
+{
+ LLSD sd_payload;
+ sd_payload["verb"] = "UPDATE";
+ sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID();
+ LLSD object_media_data;
+ for (int i=0; i < object->getNumTEs(); i++) {
+ LLTextureEntry *texture_entry = object->getTE(i);
+ llassert((texture_entry->getMediaData() != NULL) == texture_entry->hasMedia());
+ const LLSD &media_data =
+ (texture_entry->getMediaData() == NULL) ? LLSD() : texture_entry->getMediaData()->asLLSD();
+ object_media_data.append(media_data);
+ }
+ sd_payload[LLTextureEntry::OBJECT_MEDIA_DATA_KEY] = object_media_data;
+
+ LL_INFOS("LLMediaDataClient") << "update media data: " << object->getID() << " " << ll_pretty_print_sd(sd_payload) << LL_ENDL;
+
+ request(object, sd_payload);
+}
+
+/*virtual*/
+void LLObjectMediaDataClient::Responder::result(const LLSD& content)
+{
+ const LLMediaDataClient::Request::Type type = getRequest()->getType();
+ llassert(type == LLMediaDataClient::Request::GET || type == LLMediaDataClient::Request::UPDATE)
+ if (type == LLMediaDataClient::Request::GET)
+ {
+ LL_INFOS("LLMediaDataClient") << *(getRequest()) << "GET returned: " << ll_pretty_print_sd(content) << LL_ENDL;
+
+ // Look for an error
+ if (content.has("error"))
+ {
+ const LLSD &error = content["error"];
+ LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error getting media data for object: code=" <<
+ error["code"].asString() << ": " << error["message"].asString() << LL_ENDL;
+
+ // XXX Warn user?
+ }
+ else {
+ // Check the data
+ const LLUUID &object_id = content[LLTextureEntry::OBJECT_ID_KEY];
+ if (object_id != getRequest()->getObject()->getID())
+ {
+ // NOT good, wrong object id!!
+ LL_WARNS("LLMediaDataClient") << *(getRequest()) << "DROPPING response with wrong object id (" << object_id << ")" << LL_ENDL;
+ return;
+ }
+
+ // Otherwise, update with object media data
+ getRequest()->getObject()->updateObjectMediaData(content[LLTextureEntry::OBJECT_MEDIA_DATA_KEY]);
+ }
+ }
+ else if (type == LLMediaDataClient::Request::UPDATE)
+ {
+ // just do what our superclass does
+ LLMediaDataClient::Responder::result(content);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+//
+// LLObjectMediaNavigateClient
+// Subclass of LLMediaDataClient for the ObjectMediaNavigate cap
+//
+//////////////////////////////////////////////////////////////////////////////////////
+LLMediaDataClient::Responder *LLObjectMediaNavigateClient::createResponder(const request_ptr_t &request) const
+{
+ return new LLObjectMediaNavigateClient::Responder(request);
+}
+
+const char *LLObjectMediaNavigateClient::getCapabilityName() const
+{
+ return "ObjectMediaNavigate";
+}
+
+void LLObjectMediaNavigateClient::navigate(LLVOVolume *object, U8 texture_index, const std::string &url)
+{
+ LLSD sd_payload;
+ sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID();
+ sd_payload[LLMediaEntry::CURRENT_URL_KEY] = url;
+ sd_payload[LLTextureEntry::TEXTURE_INDEX_KEY] = (LLSD::Integer)texture_index;
+ request(object, sd_payload);
+}
+
+/*virtual*/
+void LLObjectMediaNavigateClient::Responder::error(U32 status, const std::string& reason)
+{
+ // Bounce back (unless HTTP_SERVICE_UNAVAILABLE, in which case call base
+ // class
+ if (status == HTTP_SERVICE_UNAVAILABLE)
+ {
+ LLMediaDataClient::Responder::error(status, reason);
+ }
+ else {
+ // bounce the face back
+ bounceBack();
+ LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: http code=" << status << LL_ENDL;
+ }
+}
+
+/*virtual*/
+void LLObjectMediaNavigateClient::Responder::result(const LLSD& content)
+{
+ LL_DEBUGS("LLMediaDataClient") << *(getRequest()) << " NAVIGATE returned " << ll_pretty_print_sd(content) << LL_ENDL;
+
+ if (content.has("error"))
+ {
+ const LLSD &error = content["error"];
+ int error_code = error["code"];
+
+ if (ERROR_PERMISSION_DENIED_CODE == error_code)
+ {
+ LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Navigation denied: bounce back" << LL_ENDL;
+ // bounce the face back
+ bounceBack();
+ }
+ else {
+ LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: code=" <<
+ error["code"].asString() << ": " << error["message"].asString() << LL_ENDL;
+ }
+ // XXX Warn user?
+ }
+ else {
+ // just do what our superclass does
+ LLMediaDataClient::Responder::result(content);
+ }
+}
+
+
+void LLObjectMediaNavigateClient::Responder::bounceBack()
+{
+ const LLSD &payload = getRequest()->getPayload();
+ U8 texture_index = (U8)(LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY];
+ viewer_media_t impl = getRequest()->getObject()->getMediaImpl(texture_index);
+ // Find the media entry for this navigate
+ LLMediaEntry* mep = NULL;
+ LLTextureEntry *te = getRequest()->getObject()->getTE(texture_index);
+ if(te)
+ {
+ mep = te->getMediaData();
+ }
+
+ if (mep && impl)
+ {
+ impl->navigateTo(mep->getCurrentURL());
+ }
+}
diff --git a/indra/newview/llmediadataclient.h b/indra/newview/llmediadataclient.h
new file mode 100755
index 0000000000..59c4334251
--- /dev/null
+++ b/indra/newview/llmediadataclient.h
@@ -0,0 +1,263 @@
+/**
+ * @file llmediadataclient.h
+ * @brief class for queueing up requests to the media service
+ *
+ * $LicenseInfo:firstyear=2007&license=viewergpl$
+ *
+ * Copyright (c) 2007-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLMEDIADATACLIENT_H
+#define LL_LLMEDIADATACLIENT_H
+
+#include "llhttpclient.h"
+#include <queue>
+#include "llrefcount.h"
+#include "lltimer.h"
+
+// Forward decls
+class LLVOVolume;
+
+typedef LLPointer<LLVOVolume> ll_vo_volume_ptr_t;
+
+// This object creates a priority queue for requests.
+// Abstracts the Cap URL, the request, and the responder
+class LLMediaDataClient : public LLRefCount
+{
+public:
+ LOG_CLASS(LLMediaDataClient);
+
+ const static int QUEUE_TIMER_DELAY = 1; // seconds(s)
+ const static int MAX_RETRIES = 4;
+
+ // Constructor
+ LLMediaDataClient();
+
+ // Make the request
+ void request(LLVOVolume *object, const LLSD &payload);
+
+protected:
+ // Destructor
+ virtual ~LLMediaDataClient(); // use unref
+
+ // Request
+ class Request : public LLRefCount
+ {
+ public:
+ enum Type {
+ GET,
+ UPDATE,
+ NAVIGATE
+ };
+
+ Request(const std::string &cap_name, const LLSD& sd_payload, LLVOVolume *obj, LLMediaDataClient *mdc);
+ const std::string &getCapName() const { return mCapName; }
+ const LLSD &getPayload() const { return mPayload; }
+ LLVOVolume *getObject() const { return mObject; }
+
+ U32 getNum() const { return mNum; }
+
+ U32 getRetryCount() const { return mRetryCount; }
+ void incRetryCount() { mRetryCount++; }
+
+ // Note: may return empty string!
+ std::string getCapability() const;
+
+ Type getType() const;
+ const char *getTypeAsString() const;
+
+ // Re-enqueue thyself
+ void reEnqueue() const;
+
+ public:
+ friend std::ostream& operator<<(std::ostream &s, const Request &q);
+
+ protected:
+ virtual ~Request(); // use unref();
+
+ private:
+ std::string mCapName;
+ LLSD mPayload;
+ ll_vo_volume_ptr_t mObject;
+ // Simple tracking
+ const U32 mNum;
+ static U32 sNum;
+ U32 mRetryCount;
+
+ // Back pointer to the MDC...not a ref!
+ LLMediaDataClient *mMDC;
+ };
+ typedef LLPointer<Request> request_ptr_t;
+
+ // Responder
+ class Responder : public LLHTTPClient::Responder
+ {
+ static const int UNAVAILABLE_RETRY_TIMER_DELAY = 5; // secs
+
+ public:
+ Responder(const request_ptr_t &request);
+ //If we get back an error (not found, etc...), handle it here
+ virtual void error(U32 status, const std::string& reason);
+ //If we get back a normal response, handle it here. Default just logs it.
+ virtual void result(const LLSD& content);
+
+ const request_ptr_t &getRequest() const { return mRequest; }
+
+ protected:
+ virtual ~Responder();
+
+ private:
+
+ class RetryTimer : public LLEventTimer
+ {
+ public:
+ RetryTimer(F32 time, Responder *);
+ virtual ~RetryTimer();
+ virtual BOOL tick();
+ private:
+ // back-pointer
+ boost::intrusive_ptr<Responder> mResponder;
+ };
+
+ request_ptr_t mRequest;
+ };
+
+protected:
+
+ void enqueue(const Request*);
+
+ // Subclasses must override this factory method to return a new responder
+ virtual Responder *createResponder(const request_ptr_t &request) const = 0;
+
+ // Subclasses must override to return a cap name
+ virtual const char *getCapabilityName() const = 0;
+
+private:
+
+ // Comparator for PriorityQueue
+ class Comparator
+ {
+ public:
+ bool operator() (const request_ptr_t &o1, const request_ptr_t &o2) const;
+ private:
+ static F64 getObjectScore(const ll_vo_volume_ptr_t &obj);
+ };
+
+ class PriorityQueue : public std::priority_queue<
+ request_ptr_t,
+ std::vector<request_ptr_t>,
+ Comparator >
+ {
+ public:
+ friend std::ostream& operator<<(std::ostream &s, const PriorityQueue &q);
+ };
+
+ friend std::ostream& operator<<(std::ostream &s, const Request &q);
+ friend std::ostream& operator<<(std::ostream &s, const PriorityQueue &q);
+
+ class QueueTimer : public LLEventTimer
+ {
+ public:
+ QueueTimer(F32 time, LLMediaDataClient *mdc);
+ virtual BOOL tick();
+ protected:
+ virtual ~QueueTimer();
+ private:
+ // back-pointer
+ LLPointer<LLMediaDataClient> mMDC;
+ };
+
+ void startQueueTimer();
+ void stopQueueTimer();
+ void setIsRunning(bool val) { mQueueTimerIsRunning = val; }
+
+ bool mQueueTimerIsRunning;
+
+ PriorityQueue *pRequestQueue;
+};
+
+
+// MediaDataResponder specific for the ObjectMedia cap
+class LLObjectMediaDataClient : public LLMediaDataClient
+{
+public:
+ LLObjectMediaDataClient() {}
+ ~LLObjectMediaDataClient() {}
+
+ void fetchMedia(LLVOVolume *object);
+ void updateMedia(LLVOVolume *object);
+
+protected:
+ // Subclasses must override this factory method to return a new responder
+ virtual Responder *createResponder(const request_ptr_t &request) const;
+
+ // Subclasses must override to return a cap name
+ virtual const char *getCapabilityName() const;
+
+ class Responder : public LLMediaDataClient::Responder
+ {
+ public:
+ Responder(const request_ptr_t &request)
+ : LLMediaDataClient::Responder(request) {}
+ virtual void result(const LLSD &content);
+ };
+};
+
+
+// MediaDataResponder specific for the ObjectMediaNavigate cap
+class LLObjectMediaNavigateClient : public LLMediaDataClient
+{
+ // NOTE: from llmediaservice.h
+ static const int ERROR_PERMISSION_DENIED_CODE = 8002;
+
+public:
+ LLObjectMediaNavigateClient() {}
+ ~LLObjectMediaNavigateClient() {}
+
+ void navigate(LLVOVolume *object, U8 texture_index, const std::string &url);
+
+protected:
+ // Subclasses must override this factory method to return a new responder
+ virtual Responder *createResponder(const request_ptr_t &request) const;
+
+ // Subclasses must override to return a cap name
+ virtual const char *getCapabilityName() const;
+
+ class Responder : public LLMediaDataClient::Responder
+ {
+ public:
+ Responder(const request_ptr_t &request)
+ : LLMediaDataClient::Responder(request) {}
+ virtual void error(U32 status, const std::string& reason);
+ virtual void result(const LLSD &content);
+ private:
+ void bounceBack();
+ };
+
+};
+
+
+#endif // LL_LLMEDIADATACLIENT_H
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 05011a1568..1f4f1322fd 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -47,7 +47,6 @@
#include "llframetimer.h"
#include "llinventory.h"
#include "llmaterialtable.h"
-#include "llmediadataresponder.h"
#include "llmutelist.h"
#include "llnamevalue.h"
#include "llprimitive.h"
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index e69779b2dc..583246c23e 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -46,7 +46,6 @@
#include "llvolumemessage.h"
#include "material_codes.h"
#include "message.h"
-#include "llmediadataresponder.h"
#include "llpluginclassmedia.h" // for code in the mediaEvent handler
#include "object_flags.h"
#include "llagentconstants.h"
@@ -69,7 +68,7 @@
#include "pipeline.h"
#include "llsdutil.h"
#include "llmediaentry.h"
-#include "llmediadatafetcher.h"
+#include "llmediadataclient.h"
#include "llagent.h"
const S32 MIN_QUIET_FRAMES_COALESCE = 30;
@@ -86,6 +85,8 @@ F32 LLVOVolume::sLODFactor = 1.f;
F32 LLVOVolume::sLODSlopDistanceFactor = 0.5f; //Changing this to zero, effectively disables the LOD transition slop
F32 LLVOVolume::sDistanceFactor = 1.0f;
S32 LLVOVolume::sNumLODChanges = 0;
+LLPointer<LLObjectMediaDataClient> LLVOVolume::sObjectMediaClient = NULL;
+LLPointer<LLObjectMediaNavigateClient> LLVOVolume::sObjectMediaNavigateClient = NULL;
static LLFastTimer::DeclareTimer FTM_GEN_TRIANGLES("Generate Triangles");
static LLFastTimer::DeclareTimer FTM_GEN_VOLUME("Generate Volumes");
@@ -133,13 +134,15 @@ LLVOVolume::~LLVOVolume()
// static
void LLVOVolume::initClass()
{
- LLMediaDataFetcher::initClass();
+ sObjectMediaClient = new LLObjectMediaDataClient();
+ sObjectMediaNavigateClient = new LLObjectMediaNavigateClient();
}
// static
void LLVOVolume::cleanupClass()
{
- LLMediaDataFetcher::cleanupClass();
+ sObjectMediaClient = NULL;
+ sObjectMediaNavigateClient = NULL;
}
U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,
@@ -1621,7 +1624,7 @@ bool LLVOVolume::hasMedia() const
void LLVOVolume::requestMediaDataUpdate()
{
- LLMediaDataFetcher::fetchMedia(this);
+ sObjectMediaClient->fetchMedia(this);
}
void LLVOVolume::cleanUpMediaImpls()
@@ -1752,21 +1755,7 @@ void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin,
llinfos << "broadcasting navigate with URI " << new_location << llendl;
- // Post the navigate to the cap
- std::string cap = getRegion()->getCapability("ObjectMediaNavigate");
- if(cap.empty())
- {
- // XXX *TODO: deal with no cap! It may happen! (retry?)
- LL_WARNS("Media") << "Can't broadcast navigate event -- ObjectMediaNavigate cap is not available" << LL_ENDL;
- return;
- }
-
- // If we got here, the cap is available. Index through all faces that have this media and send the navigate message.
- LLSD sd;
- sd["object_id"] = mID;
- sd["current_url"] = new_location;
- sd["texture_index"] = face_index;
- LLHTTPClient::post(cap, sd, new LLMediaDataResponder("ObjectMediaNavigate", sd, this));
+ sObjectMediaNavigateClient->navigate(this, face_index, new_location);
}
}
break;
@@ -1790,29 +1779,9 @@ void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin,
}
-void LLVOVolume::sendMediaDataUpdate() const
+void LLVOVolume::sendMediaDataUpdate()
{
- std::string url = getRegion()->getCapability("ObjectMedia");
- if (!url.empty())
- {
- LLSD sd_payload;
- sd_payload["verb"] = "UPDATE";
- sd_payload[LLTextureEntry::OBJECT_ID_KEY] = mID;
- LLSD object_media_data;
- for (int i=0; i < getNumTEs(); i++) {
- LLTextureEntry *texture_entry = getTE(i);
- llassert((texture_entry->getMediaData() != NULL) == texture_entry->hasMedia());
- const LLSD &media_data =
- (texture_entry->getMediaData() == NULL) ? LLSD() : texture_entry->getMediaData()->asLLSD();
- object_media_data.append(media_data);
- }
- sd_payload[LLTextureEntry::OBJECT_MEDIA_DATA_KEY] = object_media_data;
-
- llinfos << "Sending media data: " << getID() << " " << ll_pretty_print_sd(sd_payload) << llendl;
-
- LLHTTPClient::post(url, sd_payload, new LLMediaDataResponder("ObjectMedia", sd_payload, this));
- }
- // XXX *TODO: deal with no cap! It may happen! (retry?)
+ sObjectMediaClient->updateMedia(this);
}
void LLVOVolume::removeMediaImpl(S32 texture_index)
diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h
index 9a79b620d5..250c3ed917 100644
--- a/indra/newview/llvovolume.h
+++ b/indra/newview/llvovolume.h
@@ -45,6 +45,8 @@
class LLViewerTextureAnim;
class LLDrawPool;
class LLSelectNode;
+class LLObjectMediaDataClient;
+class LLObjectMediaNavigateClient;
typedef std::vector<viewer_media_t> media_list_t;
@@ -238,7 +240,7 @@ public:
void syncMediaData(S32 te, const LLSD &media_data, bool merge, bool ignore_agent);
// Send media data update to the simulator.
- void sendMediaDataUpdate() const;
+ void sendMediaDataUpdate();
viewer_media_t getMediaImpl(U8 face_id) const;
S32 getFaceIndexWithMediaImpl(const LLViewerMediaImpl* media_impl, S32 start_face_id);
@@ -283,6 +285,9 @@ public:
static F32 sLODFactor; // LOD scale factor
static F32 sDistanceFactor; // LOD distance factor
+ static LLPointer<LLObjectMediaDataClient> sObjectMediaClient;
+ static LLPointer<LLObjectMediaNavigateClient> sObjectMediaNavigateClient;
+
protected:
static S32 sNumLODChanges;