Automated merge with ssh://rick@hg.lindenlab.com/skolb/media

master
Rick Pasetto 2009-12-10 17:34:55 -08:00
commit 0a8d9fa5b5
6 changed files with 930 additions and 476 deletions

View File

@ -5504,6 +5504,28 @@
<string>F32</string>
<key>Value</key>
<real>5.0</real>
</map>
<key>PrimMediaMaxSortedQueueSize</key>
<map>
<key>Comment</key>
<string>Maximum number of objects the viewer will load media for initially</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<integer>100000</integer>
</map>
<key>PrimMediaMaxRoundRobinQueueSize</key>
<map>
<key>Comment</key>
<string>Maximum number of objects the viewer will continuously update media for</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<integer>100000</integer>
</map>
<key>ProbeHardwareOnStartup</key>
<map>

View File

@ -61,6 +61,408 @@
const F32 LLMediaDataClient::QUEUE_TIMER_DELAY = 1.0; // seconds(s)
const F32 LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY = 5.0; // secs
const U32 LLMediaDataClient::MAX_RETRIES = 4;
const U32 LLMediaDataClient::MAX_SORTED_QUEUE_SIZE = 10000;
const U32 LLMediaDataClient::MAX_ROUND_ROBIN_QUEUE_SIZE = 10000;
//////////////////////////////////////////////////////////////////////////////////////
//
// LLMediaDataClient
//
//////////////////////////////////////////////////////////////////////////////////////
LLMediaDataClient::LLMediaDataClient(F32 queue_timer_delay,
F32 retry_timer_delay,
U32 max_retries,
U32 max_sorted_queue_size,
U32 max_round_robin_queue_size)
: mQueueTimerDelay(queue_timer_delay),
mRetryTimerDelay(retry_timer_delay),
mMaxNumRetries(max_retries),
mMaxSortedQueueSize(max_sorted_queue_size),
mMaxRoundRobinQueueSize(max_round_robin_queue_size),
mQueueTimerIsRunning(false),
mCurrentQueueIsTheSortedQueue(true)
{
}
LLMediaDataClient::~LLMediaDataClient()
{
stopQueueTimer();
// This should clear the queue, and hopefully call all the destructors.
LL_DEBUGS("LLMediaDataClient") << "~LLMediaDataClient destructor: queue: " <<
(isEmpty() ? "<empty> " : "<not empty> ") << LL_ENDL;
mSortedQueue.clear();
mRoundRobinQueue.clear();
}
bool LLMediaDataClient::isEmpty() const
{
return mSortedQueue.empty() && mRoundRobinQueue.empty();
}
bool LLMediaDataClient::isInQueue(const LLMediaDataClientObject::ptr_t &object)
{
return (LLMediaDataClient::findOrRemove(mSortedQueue, object, false/*remove*/, LLMediaDataClient::Request::ANY).notNull()
|| (LLMediaDataClient::findOrRemove(mRoundRobinQueue, object, false/*remove*/, LLMediaDataClient::Request::ANY).notNull()));
}
bool LLMediaDataClient::removeFromQueue(const LLMediaDataClientObject::ptr_t &object)
{
bool removedFromSortedQueue = LLMediaDataClient::findOrRemove(mSortedQueue, object, true/*remove*/, LLMediaDataClient::Request::ANY).notNull();
bool removedFromRoundRobinQueue = LLMediaDataClient::findOrRemove(mRoundRobinQueue, object, true/*remove*/, LLMediaDataClient::Request::ANY).notNull();
return removedFromSortedQueue || removedFromRoundRobinQueue;
}
//static
LLMediaDataClient::request_ptr_t LLMediaDataClient::findOrRemove(request_queue_t &queue, const LLMediaDataClientObject::ptr_t &obj, bool remove, LLMediaDataClient::Request::Type type)
{
request_ptr_t result;
request_queue_t::iterator iter = queue.begin();
request_queue_t::iterator end = queue.end();
while (iter != end)
{
if (obj->getID() == (*iter)->getObject()->getID() && (type == LLMediaDataClient::Request::ANY || type == (*iter)->getType()))
{
result = *iter;
if (remove) queue.erase(iter);
break;
}
iter++;
}
return result;
}
void LLMediaDataClient::request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload)
{
if (object.isNull() || ! object->hasMedia()) return;
// Push the object on the queue
enqueue(new Request(getCapabilityName(), payload, object, this));
}
void LLMediaDataClient::enqueue(const Request *request)
{
if (request->isNew())
{
// Add to sorted queue
if (LLMediaDataClient::findOrRemove(mSortedQueue, request->getObject(), true/*remove*/, request->getType()).notNull())
{
LL_DEBUGS("LLMediaDataClient") << "REMOVING OLD request for " << *request << " ALREADY THERE!" << LL_ENDL;
}
LL_DEBUGS("LLMediaDataClient") << "Queuing SORTED request for " << *request << LL_ENDL;
// Sadly, we have to const-cast because items put into the queue are not const
mSortedQueue.push_back(const_cast<LLMediaDataClient::Request*>(request));
LL_DEBUGS("LLMediaDataClient") << "SORTED queue:" << mSortedQueue << LL_ENDL;
}
else {
if (mRoundRobinQueue.size() > mMaxRoundRobinQueueSize)
{
LL_INFOS_ONCE("LLMediaDataClient") << "RR QUEUE MAXED OUT!!!" << LL_ENDL;
LL_DEBUGS("LLMediaDataClient") << "Not queuing " << *request << LL_ENDL;
return;
}
// ROUND ROBIN: if it is there, and it is a GET request, leave it. If not, put at front!
request_ptr_t existing_request;
if (request->getType() == Request::GET)
{
existing_request = LLMediaDataClient::findOrRemove(mRoundRobinQueue, request->getObject(), false/*remove*/, request->getType());
}
if (existing_request.isNull())
{
LL_DEBUGS("LLMediaDataClient") << "Queuing RR request for " << *request << LL_ENDL;
// Push the request on the pending queue
// Sadly, we have to const-cast because items put into the queue are not const
mRoundRobinQueue.push_front(const_cast<LLMediaDataClient::Request*>(request));
LL_DEBUGS("LLMediaDataClient") << "RR queue:" << mRoundRobinQueue << LL_ENDL;
}
else
{
LL_DEBUGS("LLMediaDataClient") << "ALREADY THERE: NOT Queuing request for " << *request << LL_ENDL;
existing_request->markSent(false);
}
}
// Start the timer if not already running
startQueueTimer();
}
void LLMediaDataClient::startQueueTimer()
{
if (! mQueueTimerIsRunning)
{
LL_DEBUGS("LLMediaDataClient") << "starting queue timer (delay=" << mQueueTimerDelay << " seconds)" << LL_ENDL;
// LLEventTimer automagically takes care of the lifetime of this object
new QueueTimer(mQueueTimerDelay, this);
}
else {
LL_DEBUGS("LLMediaDataClient") << "not starting queue timer (it's already running, right???)" << LL_ENDL;
}
}
void LLMediaDataClient::stopQueueTimer()
{
mQueueTimerIsRunning = false;
}
bool LLMediaDataClient::processQueueTimer()
{
sortQueue();
if(!isEmpty())
{
LL_INFOS("LLMediaDataClient") << "QueueTimer::tick() started, SORTED queue size is: " << mSortedQueue.size()
<< ", RR queue size is: " << mRoundRobinQueue.size() << LL_ENDL;
LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() started, SORTED queue is: " << mSortedQueue << LL_ENDL;
LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() started, RR queue is: " << mRoundRobinQueue << LL_ENDL;
}
serviceQueue();
LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, SORTED queue size is: " << mSortedQueue.size()
<< ", RR queue size is: " << mRoundRobinQueue.size() << LL_ENDL;
LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, SORTED queue is: " << mSortedQueue << LL_ENDL;
LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, RR queue is: " << mRoundRobinQueue << LL_ENDL;
return isEmpty();
}
void LLMediaDataClient::sortQueue()
{
if(!mSortedQueue.empty())
{
// Score all items first
request_queue_t::iterator iter = mSortedQueue.begin();
request_queue_t::iterator end = mSortedQueue.end();
while (iter != end)
{
(*iter)->updateScore();
iter++;
}
// Re-sort the list...
// NOTE: should this be a stable_sort? If so we need to change to using a vector.
mSortedQueue.sort(LLMediaDataClient::compareRequests);
// ...then cull items over the max
U32 size = mSortedQueue.size();
if (size > mMaxSortedQueueSize)
{
U32 num_to_cull = (size - mMaxSortedQueueSize);
LL_INFOS("LLMediaDataClient") << "sorted queue MAXED OUT! Culling "
<< num_to_cull << " items" << LL_ENDL;
while (num_to_cull-- > 0)
{
mSortedQueue.pop_back();
}
}
}
}
// static
bool LLMediaDataClient::compareRequests(const request_ptr_t &o1, const request_ptr_t &o2)
{
if (o2.isNull()) return true;
if (o1.isNull()) return false;
return ( o1->getScore() > o2->getScore() );
}
void LLMediaDataClient::serviceQueue()
{
request_queue_t *queue_p = getCurrentQueue();
// quick retry loop for cases where we shouldn't wait for the next timer tick
while(true)
{
if (queue_p->empty())
{
LL_DEBUGS("LLMediaDataClient") << "queue empty: " << (*queue_p) << LL_ENDL;
break;
}
// Peel one off of the items from the queue, and execute request
request_ptr_t request = queue_p->front();
llassert(!request.isNull());
const LLMediaDataClientObject *object = (request.isNull()) ? NULL : request->getObject();
llassert(NULL != object);
// Check for conditions that would make us just pop and rapidly loop through
// the queue.
if(request.isNull() ||
request->isMarkedSent() ||
NULL == object ||
object->isDead() ||
!object->hasMedia())
{
if (request.isNull())
{
LL_INFOS("LLMediaDataClient") << "Skipping NULL request" << LL_ENDL;
}
else {
LL_INFOS("LLMediaDataClient") << "Skipping : " << *request << " "
<< ((request->isMarkedSent()) ? " request is marked sent" :
((NULL == object) ? " object is NULL " :
((object->isDead()) ? "object is dead" :
((!object->hasMedia()) ? "object has no media!" : "BADNESS!")))) << LL_ENDL;
}
queue_p->pop_front();
continue; // jump back to the start of the quick retry loop
}
// Next, ask if this is "interesting enough" to fetch. If not, just stop
// and wait for the next timer go-round. Only do this for the sorted
// queue.
if (mCurrentQueueIsTheSortedQueue && !object->isInterestingEnough())
{
LL_DEBUGS("LLMediaDataClient") << "Not fetching " << *request << ": not interesting enough" << LL_ENDL;
break;
}
// Finally, try to send the HTTP message to the cap url
std::string url = request->getCapability();
bool maybe_retry = false;
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, createResponder(request));
}
else {
LL_INFOS("LLMediaDataClient") << "NOT Sending request for " << *request << ": empty cap url!" << LL_ENDL;
maybe_retry = true;
}
bool exceeded_retries = request->getRetryCount() > mMaxNumRetries;
if (maybe_retry && ! exceeded_retries) // Try N times before giving up
{
// We got an empty cap, but in that case we will retry again next
// timer fire.
request->incRetryCount();
}
else {
if (exceeded_retries)
{
LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for "
<< mMaxNumRetries << " tries...popping object id " << object->getID() << LL_ENDL;
// XXX Should we bring up a warning dialog??
}
queue_p->pop_front();
if (! mCurrentQueueIsTheSortedQueue) {
// Round robin
request->markSent(true);
mRoundRobinQueue.push_back(request);
}
}
// end of quick loop -- any cases where we want to loop will use 'continue' to jump back to the start.
break;
}
swapCurrentQueue();
}
void LLMediaDataClient::swapCurrentQueue()
{
// Swap
mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue;
// If its empty, swap back
if (getCurrentQueue()->empty())
{
mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue;
}
}
LLMediaDataClient::request_queue_t *LLMediaDataClient::getCurrentQueue()
{
return (mCurrentQueueIsTheSortedQueue) ? &mSortedQueue : &mRoundRobinQueue;
}
// dump the queue
std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q)
{
int i = 0;
LLMediaDataClient::request_queue_t::const_iterator iter = q.begin();
LLMediaDataClient::request_queue_t::const_iterator end = q.end();
while (iter != end)
{
s << "\t" << i << "]: " << (*iter)->getObject()->getID().asString();
iter++;
i++;
}
return s;
}
//////////////////////////////////////////////////////////////////////////////////////
//
// LLMediaDataClient::QueueTimer
// Queue of LLMediaDataClientObject smart pointers to request media for.
//
//////////////////////////////////////////////////////////////////////////////////////
LLMediaDataClient::QueueTimer::QueueTimer(F32 time, LLMediaDataClient *mdc)
: LLEventTimer(time), mMDC(mdc)
{
mMDC->setIsRunning(true);
}
LLMediaDataClient::QueueTimer::~QueueTimer()
{
LL_DEBUGS("LLMediaDataClient") << "~QueueTimer" << LL_ENDL;
mMDC->setIsRunning(false);
mMDC = NULL;
}
// virtual
BOOL LLMediaDataClient::QueueTimer::tick()
{
if (mMDC.isNull()) return TRUE;
return mMDC->processQueueTimer();
}
//////////////////////////////////////////////////////////////////////////////////////
//
// LLMediaDataClient::Responder::RetryTimer
//
//////////////////////////////////////////////////////////////////////////////////////
LLMediaDataClient::Responder::RetryTimer::RetryTimer(F32 time, Responder *mdr)
: LLEventTimer(time), mResponder(mdr)
{
}
// virtual
LLMediaDataClient::Responder::RetryTimer::~RetryTimer()
{
LL_DEBUGS("LLMediaDataClient") << "~RetryTimer" << *(mResponder->getRequest()) << LL_ENDL;
// XXX This is weird: Instead of doing the work in tick() (which re-schedules
// a timer, which might be risky), do it here, in the destructor. Yes, it is very odd.
// 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();
// Release the ref to the responder.
mResponder = NULL;
}
// virtual
BOOL LLMediaDataClient::Responder::RetryTimer::tick()
{
// Don't fire again
return TRUE;
}
//////////////////////////////////////////////////////////////////////////////////////
//
@ -69,16 +471,18 @@ const U32 LLMediaDataClient::MAX_RETRIES = 4;
//////////////////////////////////////////////////////////////////////////////////////
/*static*/U32 LLMediaDataClient::Request::sNum = 0;
LLMediaDataClient::Request::Request(const std::string &cap_name,
LLMediaDataClient::Request::Request(const char *cap_name,
const LLSD& sd_payload,
LLMediaDataClientObject *obj,
LLMediaDataClient *mdc)
: mCapName(cap_name),
mPayload(sd_payload),
mObject(obj),
mNum(++sNum),
mRetryCount(0),
mMDC(mdc)
: mCapName(cap_name),
mPayload(sd_payload),
mObject(obj),
mNum(++sNum),
mRetryCount(0),
mMDC(mdc),
mMarkedSent(false),
mScore((F64)0.0)
{
}
@ -89,7 +493,7 @@ LLMediaDataClient::Request::~Request()
mObject = NULL;
}
std::string LLMediaDataClient::Request::getCapability() const
{
return getObject()->getCapabilityUrl(getCapName());
@ -99,11 +503,11 @@ std::string LLMediaDataClient::Request::getCapability() const
// discover it.
LLMediaDataClient::Request::Type LLMediaDataClient::Request::getType() const
{
if (mCapName == "ObjectMediaNavigate")
if (0 == strcmp(mCapName, "ObjectMediaNavigate"))
{
return NAVIGATE;
}
else if (mCapName == "ObjectMedia")
else if (0 == strcmp(mCapName, "ObjectMedia"))
{
const std::string &verb = mPayload["verb"];
if (verb == "GET")
@ -133,10 +537,13 @@ const char *LLMediaDataClient::Request::getTypeAsString() const
case NAVIGATE:
return "NAVIGATE";
break;
case ANY:
return "ANY";
break;
}
return "";
}
void LLMediaDataClient::Request::reEnqueue() const
{
@ -147,7 +554,7 @@ void LLMediaDataClient::Request::reEnqueue() const
F32 LLMediaDataClient::Request::getRetryTimerDelay() const
{
return (mMDC == NULL) ? LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY :
mMDC->mRetryTimerDelay;
mMDC->mRetryTimerDelay;
}
U32 LLMediaDataClient::Request::getMaxNumRetries() const
@ -155,52 +562,38 @@ U32 LLMediaDataClient::Request::getMaxNumRetries() const
return (mMDC == NULL) ? LLMediaDataClient::MAX_RETRIES : mMDC->mMaxNumRetries;
}
void LLMediaDataClient::Request::markSent(bool flag)
{
if (mMarkedSent != flag)
{
mMarkedSent = flag;
if (!mMarkedSent)
{
mNum = ++sNum;
}
}
}
void LLMediaDataClient::Request::updateScore()
{
F64 tmp = mObject->getMediaInterest();
if (tmp != mScore)
{
LL_DEBUGS("LLMediaDataClient") << "Score for " << mObject->getID() << " changed from " << mScore << " to " << tmp << LL_ENDL;
mScore = tmp;
}
}
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> ";
s << "request: num=" << r.getNum()
<< " type=" << r.getTypeAsString()
<< " ID=" << r.getObject()->getID()
<< " #retries=" << r.getRetryCount();
return s;
}
//////////////////////////////////////////////////////////////////////////////////////
//
// LLMediaDataClient::Responder::RetryTimer
//
//////////////////////////////////////////////////////////////////////////////////////
LLMediaDataClient::Responder::RetryTimer::RetryTimer(F32 time, Responder *mdr)
: LLEventTimer(time), mResponder(mdr)
{
}
// virtual
LLMediaDataClient::Responder::RetryTimer::~RetryTimer()
{
LL_DEBUGS("LLMediaDataClient") << "~RetryTimer" << *(mResponder->getRequest()) << LL_ENDL;
// XXX This is weird: Instead of doing the work in tick() (which re-schedules
// a timer, which might be risky), do it here, in the destructor. Yes, it is very odd.
// 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();
// Release the ref to the responder.
mResponder = NULL;
}
// virtual
BOOL LLMediaDataClient::Responder::RetryTimer::tick()
{
// Don't fire again
return TRUE;
}
//////////////////////////////////////////////////////////////////////////////////////
//
// LLMediaDataClient::Responder
@ -208,7 +601,7 @@ BOOL LLMediaDataClient::Responder::RetryTimer::tick()
//////////////////////////////////////////////////////////////////////////////////////
LLMediaDataClient::Responder::Responder(const request_ptr_t &request)
: mRequest(request)
: mRequest(request)
{
}
@ -224,20 +617,20 @@ void LLMediaDataClient::Responder::error(U32 status, const std::string& reason)
if (status == HTTP_SERVICE_UNAVAILABLE)
{
F32 retry_timeout = mRequest->getRetryTimerDelay();
mRequest->incRetryCount();
if (mRequest->getRetryCount() < mRequest->getMaxNumRetries())
{
LL_INFOS("LLMediaDataClient") << *mRequest << "got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL;
LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL;
// Start timer (instances are automagically tracked by
// InstanceTracker<> and LLEventTimer)
new RetryTimer(F32(retry_timeout/*secs*/), this);
}
else {
LL_INFOS("LLMediaDataClient") << *mRequest << "got SERVICE_UNAVAILABLE...retry count " <<
mRequest->getRetryCount() << " exceeds " << mRequest->getMaxNumRetries() << ", not retrying" << LL_ENDL;
LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retry count " <<
mRequest->getRetryCount() << " exceeds " << mRequest->getMaxNumRetries() << ", not retrying" << LL_ENDL;
}
}
else {
@ -246,288 +639,10 @@ void LLMediaDataClient::Responder::error(U32 status, const std::string& reason)
}
}
/*virtual*/
void LLMediaDataClient::Responder::result(const LLSD& content)
{
LL_INFOS("LLMediaDataClient") << *mRequest << "result : " << ll_print_sd(content) << LL_ENDL;
}
//////////////////////////////////////////////////////////////////////////////////////
//
// LLMediaDataClient::Comparator
//
//////////////////////////////////////////////////////////////////////////////////////
// static
bool LLMediaDataClient::compareRequests(const request_ptr_t &o1, const request_ptr_t &o2)
{
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 = getObjectScore(o1->getObject());
F64 o2_score = getObjectScore(o2->getObject());
return ( o1_score > o2_score );
}
// static
F64 LLMediaDataClient::getObjectScore(const LLMediaDataClientObject::ptr_t &obj)
{
// *TODO: make this less expensive?
F64 dist = obj->getDistanceFromAvatar() + 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 = obj->getTotalMediaInterest() + 1.0;
return interest/dist;
}
//////////////////////////////////////////////////////////////////////////////////////
//
// LLMediaDataClient::PriorityQueue
// Queue of LLMediaDataClientObject smart pointers to request media for.
//
//////////////////////////////////////////////////////////////////////////////////////
// dump the queue
std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q)
{
int i = 0;
LLMediaDataClient::request_queue_t::const_iterator iter = q.begin();
LLMediaDataClient::request_queue_t::const_iterator end = q.end();
while (iter != end)
{
s << "\t" << i << "]: " << (*iter)->getObject()->getID().asString();
iter++;
i++;
}
return s;
}
// find the given object in the queue.
bool LLMediaDataClient::find(const LLMediaDataClientObject::ptr_t &obj) const
{
request_queue_t::const_iterator iter = pRequestQueue->begin();
request_queue_t::const_iterator end = pRequestQueue->end();
while (iter != end)
{
if (obj->getID() == (*iter)->getObject()->getID())
{
return true;
}
iter++;
}
return false;
}
//////////////////////////////////////////////////////////////////////////////////////
//
// LLMediaDataClient::QueueTimer
// Queue of LLMediaDataClientObject smart pointers to request media for.
//
//////////////////////////////////////////////////////////////////////////////////////
LLMediaDataClient::QueueTimer::QueueTimer(F32 time, LLMediaDataClient *mdc)
: LLEventTimer(time), mMDC(mdc)
{
mMDC->setIsRunning(true);
}
LLMediaDataClient::QueueTimer::~QueueTimer()
{
LL_DEBUGS("LLMediaDataClient") << "~QueueTimer" << LL_ENDL;
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;
}
request_queue_t &queue = *(mMDC->pRequestQueue);
if(!queue.empty())
{
LL_INFOS("LLMediaDataClient") << "QueueTimer::tick() started, queue is: " << queue << LL_ENDL;
// Re-sort the list every time...
// XXX Is this really what we want?
queue.sort(LLMediaDataClient::compareRequests);
}
// quick retry loop for cases where we shouldn't wait for the next timer tick
while(true)
{
if (queue.empty())
{
LL_DEBUGS("LLMediaDataClient") << "queue empty: " << queue << LL_ENDL;
return TRUE;
}
// Peel one off of the items from the queue, and execute request
request_ptr_t request = queue.front();
llassert(!request.isNull());
const LLMediaDataClientObject *object = (request.isNull()) ? NULL : request->getObject();
bool performed_request = false;
bool error = false;
llassert(NULL != object);
if(object->isDead())
{
// This object has been marked dead. Pop it and move on to the next item in the queue immediately.
LL_INFOS("LLMediaDataClient") << "Skipping " << *request << ": object is dead!" << LL_ENDL;
queue.pop_front();
continue; // jump back to the start of the quick retry loop
}
if (NULL != object && 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 (request.isNull())
{
LL_WARNS("LLMediaDataClient") << "Not Sending request: NULL request!" << LL_ENDL;
}
else if (NULL == object)
{
LL_WARNS("LLMediaDataClient") << "Not Sending request for " << *request << " NULL object!" << LL_ENDL;
}
else if (!object->hasMedia())
{
LL_WARNS("LLMediaDataClient") << "Not Sending request for " << *request << " hasMedia() is false!" << LL_ENDL;
}
error = true;
}
bool exceeded_retries = request->getRetryCount() > mMDC->mMaxNumRetries;
if (performed_request || exceeded_retries || error) // Try N times before giving up
{
if (exceeded_retries)
{
LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for "
<< mMDC->mMaxNumRetries << " tries...popping object id " << object->getID() << LL_ENDL;
// XXX Should we bring up a warning dialog??
}
queue.pop_front();
}
else {
request->incRetryCount();
}
// end of quick retry loop -- any cases where we want to loop will use 'continue' to jump back to the start.
break;
}
LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, queue is now: " << (*(mMDC->pRequestQueue)) << LL_ENDL;
return queue.empty();
}
void LLMediaDataClient::startQueueTimer()
{
if (! mQueueTimerIsRunning)
{
LL_INFOS("LLMediaDataClient") << "starting queue timer (delay=" << mQueueTimerDelay << " seconds)" << LL_ENDL;
// LLEventTimer automagically takes care of the lifetime of this object
new QueueTimer(mQueueTimerDelay, this);
}
else {
LL_DEBUGS("LLMediaDataClient") << "not starting queue timer (it's already running, right???)" << LL_ENDL;
}
}
void LLMediaDataClient::stopQueueTimer()
{
mQueueTimerIsRunning = false;
}
void LLMediaDataClient::request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload)
{
if (object.isNull() || ! 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_back(const_cast<LLMediaDataClient::Request*>(request));
LL_DEBUGS("LLMediaDataClient") << "Queue:" << (*pRequestQueue) << LL_ENDL;
// Start the timer if not already running
startQueueTimer();
}
//////////////////////////////////////////////////////////////////////////////////////
//
// LLMediaDataClient
//
//////////////////////////////////////////////////////////////////////////////////////
LLMediaDataClient::LLMediaDataClient(F32 queue_timer_delay,
F32 retry_timer_delay,
U32 max_retries)
: mQueueTimerDelay(queue_timer_delay),
mRetryTimerDelay(retry_timer_delay),
mMaxNumRetries(max_retries),
mQueueTimerIsRunning(false)
{
pRequestQueue = new request_queue_t();
}
LLMediaDataClient::~LLMediaDataClient()
{
stopQueueTimer();
// This should clear the queue, and hopefully call all the destructors.
LL_DEBUGS("LLMediaDataClient") << "~LLMediaDataClient destructor: queue: " <<
(pRequestQueue->empty() ? "<empty> " : "<not empty> ") << (*pRequestQueue) << LL_ENDL;
delete pRequestQueue;
pRequestQueue = NULL;
}
bool LLMediaDataClient::isEmpty() const
{
return (NULL == pRequestQueue) ? true : pRequestQueue->empty();
}
bool LLMediaDataClient::isInQueue(const LLMediaDataClientObject::ptr_t &object) const
{
return (NULL == pRequestQueue) ? false : find(object);
LL_DEBUGS("LLMediaDataClient") << *mRequest << " result : " << ll_print_sd(content) << LL_ENDL;
}
//////////////////////////////////////////////////////////////////////////////////////
@ -569,7 +684,7 @@ void LLObjectMediaDataClient::updateMedia(LLMediaDataClientObject *object)
}
sd_payload[LLTextureEntry::OBJECT_MEDIA_DATA_KEY] = object_media_data;
LL_INFOS("LLMediaDataClient") << "update media data: " << object->getID() << " " << ll_print_sd(sd_payload) << LL_ENDL;
LL_DEBUGS("LLMediaDataClient") << "update media data: " << object->getID() << " " << ll_print_sd(sd_payload) << LL_ENDL;
request(object, sd_payload);
}
@ -581,7 +696,7 @@ void LLObjectMediaDataClient::Responder::result(const LLSD& content)
llassert(type == LLMediaDataClient::Request::GET || type == LLMediaDataClient::Request::UPDATE)
if (type == LLMediaDataClient::Request::GET)
{
LL_INFOS("LLMediaDataClient") << *(getRequest()) << "GET returned: " << ll_print_sd(content) << LL_ENDL;
LL_DEBUGS("LLMediaDataClient") << *(getRequest()) << " GET returned: " << ll_print_sd(content) << LL_ENDL;
// Look for an error
if (content.has("error"))
@ -598,12 +713,13 @@ void LLObjectMediaDataClient::Responder::result(const LLSD& content)
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;
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]);
getRequest()->getObject()->updateObjectMediaData(content[LLTextureEntry::OBJECT_MEDIA_DATA_KEY],
content[LLTextureEntry::MEDIA_VERSION_KEY]);
}
}
else if (type == LLMediaDataClient::Request::UPDATE)

View File

@ -55,15 +55,19 @@ public:
// Does this object have media?
virtual bool hasMedia() const = 0;
// Update the object's media data to the given array
virtual void updateObjectMediaData(LLSD const &media_data_array) = 0;
// Return the distance from the object to the avatar
virtual F64 getDistanceFromAvatar() const = 0;
virtual void updateObjectMediaData(LLSD const &media_data_array, const std::string &version_string) = 0;
// Return the total "interest" of the media (on-screen area)
virtual F64 getTotalMediaInterest() const = 0;
virtual F64 getMediaInterest() const = 0;
// Return the given cap url
virtual std::string getCapabilityUrl(const std::string &name) const = 0;
// Return whether the object has been marked dead
virtual bool isDead() const = 0;
// Returns a media version number for the object
virtual U32 getMediaVersion() const = 0;
// Returns whether the object is "interesting enough" to fetch
virtual bool isInterestingEnough() const = 0;
// Returns whether we've seen this object yet or not
virtual bool isNew() const = 0;
// smart pointer
typedef LLPointer<LLMediaDataClientObject> ptr_t;
@ -79,11 +83,15 @@ public:
const static F32 QUEUE_TIMER_DELAY;// = 1.0; // seconds(s)
const static F32 UNAVAILABLE_RETRY_TIMER_DELAY;// = 5.0; // secs
const static U32 MAX_RETRIES;// = 4;
const static U32 MAX_SORTED_QUEUE_SIZE;// = 10000;
const static U32 MAX_ROUND_ROBIN_QUEUE_SIZE;// = 10000;
// Constructor
LLMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY,
F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY,
U32 max_retries = MAX_RETRIES);
U32 max_retries = MAX_RETRIES,
U32 max_sorted_queue_size = MAX_SORTED_QUEUE_SIZE,
U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE);
// Make the request
void request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload);
@ -94,7 +102,13 @@ public:
bool isEmpty() const;
// Returns true iff the given object is in the queue
bool isInQueue(const LLMediaDataClientObject::ptr_t &object) const;
bool isInQueue(const LLMediaDataClientObject::ptr_t &object);
// Remove the given object from the queue. Returns true iff the given object is removed.
bool removeFromQueue(const LLMediaDataClientObject::ptr_t &object);
// Called only by the Queue timer and tests (potentially)
bool processQueueTimer();
protected:
// Destructor
@ -107,11 +121,12 @@ protected:
enum Type {
GET,
UPDATE,
NAVIGATE
NAVIGATE,
ANY
};
Request(const std::string &cap_name, const LLSD& sd_payload, LLMediaDataClientObject *obj, LLMediaDataClient *mdc);
const std::string &getCapName() const { return mCapName; }
Request(const char *cap_name, const LLSD& sd_payload, LLMediaDataClientObject *obj, LLMediaDataClient *mdc);
const char *getCapName() const { return mCapName; }
const LLSD &getPayload() const { return mPayload; }
LLMediaDataClientObject *getObject() const { return mObject; }
@ -132,6 +147,12 @@ protected:
F32 getRetryTimerDelay() const;
U32 getMaxNumRetries() const;
bool isNew() const { return mObject.notNull() ? mObject->isNew() : false; }
void markSent(bool flag);
bool isMarkedSent() const { return mMarkedSent; }
void updateScore();
F64 getScore() const { return mScore; }
public:
friend std::ostream& operator<<(std::ostream &s, const Request &q);
@ -139,14 +160,16 @@ protected:
virtual ~Request(); // use unref();
private:
std::string mCapName;
const char *mCapName;
LLSD mPayload;
LLMediaDataClientObject::ptr_t mObject;
// Simple tracking
const U32 mNum;
U32 mNum;
static U32 sNum;
U32 mRetryCount;
F64 mScore;
bool mMarkedSent;
// Back pointer to the MDC...not a ref!
LLMediaDataClient *mMDC;
};
@ -184,18 +207,23 @@ protected:
};
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:
virtual void sortQueue();
virtual void serviceQueue();
private:
typedef std::list<request_ptr_t> request_queue_t;
void enqueue(const Request*);
// Return whether the given object is/was in the queue
static LLMediaDataClient::request_ptr_t findOrRemove(request_queue_t &queue, const LLMediaDataClientObject::ptr_t &obj, bool remove, Request::Type type);
// Comparator for sorting
static bool compareRequests(const request_ptr_t &o1, const request_ptr_t &o2);
@ -215,34 +243,40 @@ private:
// back-pointer
LLPointer<LLMediaDataClient> mMDC;
};
// Return whether the given object is in the queue
bool find(const LLMediaDataClientObject::ptr_t &obj) const;
void startQueueTimer();
void stopQueueTimer();
void setIsRunning(bool val) { mQueueTimerIsRunning = val; }
void swapCurrentQueue();
request_queue_t *getCurrentQueue();
const F32 mQueueTimerDelay;
const F32 mRetryTimerDelay;
const U32 mMaxNumRetries;
const U32 mMaxSortedQueueSize;
const U32 mMaxRoundRobinQueueSize;
bool mQueueTimerIsRunning;
request_queue_t *pRequestQueue;
request_queue_t mSortedQueue;
request_queue_t mRoundRobinQueue;
bool mCurrentQueueIsTheSortedQueue;
};
// MediaDataResponder specific for the ObjectMedia cap
// MediaDataClient specific for the ObjectMedia cap
class LLObjectMediaDataClient : public LLMediaDataClient
{
public:
LLObjectMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY,
F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY,
U32 max_retries = MAX_RETRIES)
U32 max_retries = MAX_RETRIES,
U32 max_sorted_queue_size = MAX_SORTED_QUEUE_SIZE,
U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE)
: LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries)
{}
~LLObjectMediaDataClient() {}
virtual ~LLObjectMediaDataClient() {}
void fetchMedia(LLMediaDataClientObject *object);
void updateMedia(LLMediaDataClientObject *object);
@ -264,7 +298,7 @@ protected:
};
// MediaDataResponder specific for the ObjectMediaNavigate cap
// MediaDataClient specific for the ObjectMediaNavigate cap
class LLObjectMediaNavigateClient : public LLMediaDataClient
{
public:
@ -273,10 +307,12 @@ public:
LLObjectMediaNavigateClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY,
F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY,
U32 max_retries = MAX_RETRIES)
U32 max_retries = MAX_RETRIES,
U32 max_sorted_queue_size = MAX_SORTED_QUEUE_SIZE,
U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE)
: LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries)
{}
~LLObjectMediaNavigateClient() {}
virtual ~LLObjectMediaNavigateClient() {}
void navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url);

View File

@ -92,7 +92,7 @@ static LLFastTimer::DeclareTimer FTM_GEN_VOLUME("Generate Volumes");
class LLMediaDataClientObjectImpl : public LLMediaDataClientObject
{
public:
LLMediaDataClientObjectImpl(LLVOVolume *obj) : mObject(obj) {}
LLMediaDataClientObjectImpl(LLVOVolume *obj, bool isNew) : mObject(obj), mNew(isNew) {}
LLMediaDataClientObjectImpl() { mObject = NULL; }
virtual U8 getMediaDataCount() const
@ -128,14 +128,19 @@ public:
virtual bool hasMedia() const
{ return mObject->hasMedia(); }
virtual void updateObjectMediaData(LLSD const &data)
{ mObject->updateObjectMediaData(data); }
virtual F64 getDistanceFromAvatar() const
{ return mObject->getRenderPosition().length(); }
virtual void updateObjectMediaData(LLSD const &data, const std::string &version_string)
{ mObject->updateObjectMediaData(data, version_string); }
virtual F64 getTotalMediaInterest() const
{ return mObject->getTotalMediaInterest(); }
virtual F64 getMediaInterest() const
{
F64 tmp = mObject->getTotalMediaInterest();
return (tmp < 0.0) ? mObject->getPixelArea() : tmp;
}
virtual bool isInterestingEnough() const
{
// TODO: use performance manager to control this
return true;
}
virtual std::string getCapabilityUrl(const std::string &name) const
{ return mObject->getRegion()->getCapability(name); }
@ -143,8 +148,15 @@ public:
virtual bool isDead() const
{ return mObject->isDead(); }
virtual U32 getMediaVersion() const
{ return LLTextureEntry::getVersionFromMediaVersionString(mObject->getMediaURL()); }
virtual bool isNew() const
{ return mNew; }
private:
LLPointer<LLVOVolume> mObject;
bool mNew;
};
@ -165,6 +177,7 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re
mSpotLightPriority = 0.f;
mMediaImplList.resize(getNumTEs());
mLastFetchedMediaVersion = -1;
}
LLVOVolume::~LLVOVolume()
@ -190,7 +203,9 @@ void LLVOVolume::markDead()
{
if (!mDead)
{
// TODO: tell LLMediaDataClient to remove this object from its queue
LLMediaDataClientObject::ptr_t obj = new LLMediaDataClientObjectImpl(const_cast<LLVOVolume*>(this), false);
sObjectMediaClient->removeFromQueue(obj);
sObjectMediaNavigateClient->removeFromQueue(obj);
// Detach all media impls from this object
for(U32 i = 0 ; i < mMediaImplList.size() ; i++)
@ -210,8 +225,12 @@ void LLVOVolume::initClass()
const F32 queue_timer_delay = gSavedSettings.getF32("PrimMediaRequestQueueDelay");
const F32 retry_timer_delay = gSavedSettings.getF32("PrimMediaRetryTimerDelay");
const U32 max_retries = gSavedSettings.getU32("PrimMediaMaxRetries");
sObjectMediaClient = new LLObjectMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries);
sObjectMediaNavigateClient = new LLObjectMediaNavigateClient(queue_timer_delay, retry_timer_delay, max_retries);
const U32 max_sorted_queue_size = gSavedSettings.getU32("PrimMediaMaxSortedQueueSize");
const U32 max_round_robin_queue_size = gSavedSettings.getU32("PrimMediaMaxRoundRobinQueueSize");
sObjectMediaClient = new LLObjectMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries,
max_sorted_queue_size, max_round_robin_queue_size);
sObjectMediaNavigateClient = new LLObjectMediaNavigateClient(queue_timer_delay, retry_timer_delay,
max_retries, max_sorted_queue_size, max_round_robin_queue_size);
}
// static
@ -406,7 +425,7 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,
// If the media changed at all, request new media data
LL_DEBUGS("MediaOnAPrim") << "Media update: " << getID() << ": retval=" << retval << " Media URL: " <<
((mMedia) ? mMedia->mMediaURL : std::string("")) << LL_ENDL;
requestMediaDataUpdate();
requestMediaDataUpdate(retval & MEDIA_FLAGS_CHANGED);
}
else {
LL_INFOS("MediaOnAPrim") << "Ignoring media update for: " << getID() << " Media URL: " <<
@ -1698,16 +1717,16 @@ LLVector3 LLVOVolume::getApproximateFaceNormal(U8 face_id)
return result;
}
void LLVOVolume::requestMediaDataUpdate()
void LLVOVolume::requestMediaDataUpdate(bool isNew)
{
sObjectMediaClient->fetchMedia(new LLMediaDataClientObjectImpl(this));
sObjectMediaClient->fetchMedia(new LLMediaDataClientObjectImpl(this, isNew));
}
bool LLVOVolume::isMediaDataBeingFetched() const
{
// I know what I'm doing by const_casting this away: this is just
// a wrapper class that is only going to do a lookup.
return sObjectMediaClient->isInQueue(new LLMediaDataClientObjectImpl(const_cast<LLVOVolume*>(this)));
return sObjectMediaClient->isInQueue(new LLMediaDataClientObjectImpl(const_cast<LLVOVolume*>(this), false));
}
void LLVOVolume::cleanUpMediaImpls()
@ -1725,18 +1744,25 @@ void LLVOVolume::cleanUpMediaImpls()
}
}
void LLVOVolume::updateObjectMediaData(const LLSD &media_data_array)
void LLVOVolume::updateObjectMediaData(const LLSD &media_data_array, const std::string &media_version)
{
// media_data_array is an array of media entry maps
// media_version is the version string in the response.
U32 fetched_version = LLTextureEntry::getVersionFromMediaVersionString(media_version);
//llinfos << "updating:" << this->getID() << " " << ll_pretty_print_sd(media_data_array) << llendl;
LLSD::array_const_iterator iter = media_data_array.beginArray();
LLSD::array_const_iterator end = media_data_array.endArray();
U8 texture_index = 0;
for (; iter != end; ++iter, ++texture_index)
// Only update it if it is newer!
if ( (S32)fetched_version > mLastFetchedMediaVersion)
{
syncMediaData(texture_index, *iter, false/*merge*/, false/*ignore_agent*/);
mLastFetchedMediaVersion = fetched_version;
//llinfos << "updating:" << this->getID() << " " << ll_pretty_print_sd(media_data_array) << llendl;
LLSD::array_const_iterator iter = media_data_array.beginArray();
LLSD::array_const_iterator end = media_data_array.endArray();
U8 texture_index = 0;
for (; iter != end; ++iter, ++texture_index)
{
syncMediaData(texture_index, *iter, false/*merge*/, false/*ignore_agent*/);
}
}
}
@ -1904,7 +1930,7 @@ void LLVOVolume::mediaNavigated(LLViewerMediaImpl *impl, LLPluginClassMedia* plu
llinfos << "broadcasting navigate with URI " << new_location << llendl;
sObjectMediaNavigateClient->navigate(new LLMediaDataClientObjectImpl(this), face_index, new_location);
sObjectMediaNavigateClient->navigate(new LLMediaDataClientObjectImpl(this, false), face_index, new_location);
}
}
@ -1968,7 +1994,7 @@ void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin,
void LLVOVolume::sendMediaDataUpdate()
{
sObjectMediaClient->updateMedia(new LLMediaDataClientObjectImpl(this));
sObjectMediaClient->updateMedia(new LLMediaDataClientObjectImpl(this, false));
}
void LLVOVolume::removeMediaImpl(S32 texture_index)
@ -2063,7 +2089,7 @@ viewer_media_t LLVOVolume::getMediaImpl(U8 face_id) const
F64 LLVOVolume::getTotalMediaInterest() const
{
F64 interest = (F64)0.0;
F64 interest = (F64)-1.0; // means not interested;
int i = 0;
const int end = getNumTEs();
for ( ; i < end; ++i)
@ -2071,6 +2097,7 @@ F64 LLVOVolume::getTotalMediaInterest() const
const viewer_media_t &impl = getMediaImpl(i);
if (!impl.isNull())
{
if (interest == (F64)-1.0) interest = (F64)0.0;
interest += impl->getInterest();
}
}

View File

@ -238,7 +238,7 @@ public:
// Update this object's media data with the given media data array
// (typically this is only called upon a response from a server request)
void updateObjectMediaData(const LLSD &media_data_array);
void updateObjectMediaData(const LLSD &media_data_array, const std::string &media_version);
// Bounce back media at the given index to its current URL (or home URL, if current URL is empty)
void mediaNavigateBounceBack(U8 texture_index);
@ -270,13 +270,16 @@ public:
// Returns 'true' iff the media data for this object is in flight
bool isMediaDataBeingFetched() const;
// Returns the "last fetched" media version, or -1 if not fetched yet
S32 getLastFetchedMediaVersion() const { return mLastFetchedMediaVersion; }
protected:
S32 computeLODDetail(F32 distance, F32 radius);
BOOL calcLOD();
LLFace* addFace(S32 face_index);
void updateTEData();
void requestMediaDataUpdate();
void requestMediaDataUpdate(bool isNew);
void cleanUpMediaImpls();
void addMediaImpl(LLViewerMediaImpl* media_impl, S32 texture_index) ;
void removeMediaImpl(S32 texture_index) ;
@ -300,6 +303,7 @@ private:
LLPointer<LLViewerFetchedTexture> mSculptTexture;
LLPointer<LLViewerFetchedTexture> mLightTexture;
media_list_t mMediaImplList;
S32 mLastFetchedMediaVersion; // as fetched from the server, starts as -1
// statics
public:

View File

@ -75,15 +75,13 @@
<string>baz</string> \
</array>"
#define _DATA_URLS(ID,DIST,INT,URL1,URL2) " \
#define _DATA_URLS(ID,INTEREST,NEW,URL1,URL2) " \
<llsd> \
<map> \
<key>uuid</key> \
<string>" ID "</string> \
<key>distance</key> \
<real>" DIST "</real> \
<key>interest</key> \
<real>" INT "</real> \
<real>" INTEREST "</real> \
<key>cap_urls</key> \
<map> \
<key>ObjectMedia</key> \
@ -93,21 +91,26 @@
</map> \
<key>media_data</key> \
" MEDIA_DATA " \
<key>is_dead</key> \
<boolean>false</boolean> \
<key>is_new</key> \
<boolean>" NEW "</boolean> \
</map> \
</llsd>"
#define _DATA(ID,DIST,INT) _DATA_URLS(ID,DIST,INT,FAKE_OBJECT_MEDIA_CAP_URL,FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL)
#define _DATA(ID,INTEREST,NEW) _DATA_URLS(ID,INTEREST,NEW,FAKE_OBJECT_MEDIA_CAP_URL,FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL)
const char *DATA = _DATA(VALID_OBJECT_ID,"1.0","1.0");
const char *DATA = _DATA(VALID_OBJECT_ID,"1.0","true");
#define STR(I) boost::lexical_cast<std::string>(I)
#define LOG_TEST(N) LL_DEBUGS("LLMediaDataClient") << "\n" << \
"================================================================================\n" << \
"===================================== TEST " #N " ===================================\n" << \
"==================================== TEST " #N " ===================================\n" << \
"================================================================================\n" << LL_ENDL;
LLSD *gPostRecords = NULL;
F64 gMinimumInterestLevel = (F64)0.0;
// stubs:
void LLHTTPClient::post(
@ -125,21 +128,20 @@ void LLHTTPClient::post(
gPostRecords->append(record);
// Magic URL that triggers a 503:
LLSD result;
result[LLTextureEntry::OBJECT_ID_KEY] = body[LLTextureEntry::OBJECT_ID_KEY];
if ( url == FAKE_OBJECT_MEDIA_CAP_URL_503 )
{
responder->error(HTTP_SERVICE_UNAVAILABLE, "fake reason");
return;
}
else if (url == FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR)
{
LLSD result;
LLSD error;
error["code"] = LLObjectMediaNavigateClient::ERROR_PERMISSION_DENIED_CODE;
result["error"] = error;
responder->result(result);
}
else {
responder->result(LLSD());
}
}
responder->result(result);
}
const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f;
@ -152,13 +154,12 @@ public:
std::istringstream d(data);
LLSDSerialize::fromXML(mRep, d);
mNumBounceBacks = 0;
mDead = false;
// std::cout << ll_pretty_print_sd(mRep) << std::endl;
// std::cout << "ID: " << getID() << std::endl;
}
LLMediaDataClientObjectTest(const LLSD &rep)
: mRep(rep), mNumBounceBacks(0), mDead(false) {}
: mRep(rep), mNumBounceBacks(0) {}
~LLMediaDataClientObjectTest()
{ LL_DEBUGS("LLMediaDataClient") << "~LLMediaDataClientObjectTest" << LL_ENDL; }
@ -169,43 +170,47 @@ public:
virtual LLUUID getID() const
{ return mRep["uuid"]; }
virtual void mediaNavigateBounceBack(U8 index)
{
mNumBounceBacks++;
}
{ mNumBounceBacks++; }
virtual bool hasMedia() const
{ return mRep.has("media_data"); }
virtual void updateObjectMediaData(LLSD const &media_data_array)
{ mRep["media_data"] = media_data_array; }
virtual F64 getDistanceFromAvatar() const
{ return (LLSD::Real)mRep["distance"]; }
virtual F64 getTotalMediaInterest() const
virtual void updateObjectMediaData(LLSD const &media_data_array, const std::string &media_version)
{ mRep["media_data"] = media_data_array; mRep["media_version"] = media_version; }
virtual F64 getMediaInterest() const
{ return (LLSD::Real)mRep["interest"]; }
virtual bool isInterestingEnough() const
{ return getMediaInterest() > gMinimumInterestLevel; }
virtual std::string getCapabilityUrl(const std::string &name) const
{ return mRep["cap_urls"][name]; }
virtual bool isDead() const
{ return mDead; }
void setDistanceFromAvatar(F64 val)
{ mRep["distance"] = val; }
{ return mRep["is_dead"]; }
void setTotalMediaInterest(F64 val)
virtual U32 getMediaVersion() const
{ return (LLSD::Integer)mRep["media_version"]; }
virtual bool isNew() const
{ return mRep["is_new"]; }
void setMediaInterest(F64 val)
{ mRep["interest"] = val; }
int getNumBounceBacks() const
{ return mNumBounceBacks; }
void markDead()
{ mDead = true; }
{ mRep["is_dead"] = true; }
void markOld()
{ mRep["is_new"] = false; }
private:
LLSD mRep;
int mNumBounceBacks;
bool mDead;
};
// This special timer delay should ensure that the timer will fire on the very
@ -224,10 +229,11 @@ namespace tut
{
mediadataclient() {
gPostRecords = &mLLSD;
gMinimumInterestLevel = (F64)0.0;
//LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
//LLError::setClassLevel("LLMediaDataClient", LLError::LEVEL_DEBUG);
//LLError::setTagLevel("MediaOnAPrim", LLError::LEVEL_DEBUG);
// LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
// LLError::setClassLevel("LLMediaDataClient", LLError::LEVEL_DEBUG);
// LLError::setTagLevel("MediaOnAPrim", LLError::LEVEL_DEBUG);
}
LLSD mLLSD;
};
@ -378,11 +384,11 @@ namespace tut
LOG_TEST(4);
LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest(
_DATA(VALID_OBJECT_ID_1,"3.0","1.0"));
_DATA(VALID_OBJECT_ID_1,"1.0","true"));
LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(
_DATA(VALID_OBJECT_ID_2,"1.0","1.0"));
_DATA(VALID_OBJECT_ID_2,"3.0","true"));
LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest(
_DATA(VALID_OBJECT_ID_3,"2.0","1.0"));
_DATA(VALID_OBJECT_ID_3,"2.0","true"));
{
LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD);
const char *ORDERED_OBJECT_IDS[] = { VALID_OBJECT_ID_2, VALID_OBJECT_ID_3, VALID_OBJECT_ID_1 };
@ -428,8 +434,7 @@ namespace tut
LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest(
_DATA_URLS(VALID_OBJECT_ID,
"1.0",
"1.0",
"1.0","true",
FAKE_OBJECT_MEDIA_CAP_URL_503,
FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL));
int num_refs_start = o->getNumRefs();
@ -484,8 +489,7 @@ namespace tut
LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest(
_DATA_URLS(VALID_OBJECT_ID,
"1.0",
"1.0",
"1.0","true",
FAKE_OBJECT_MEDIA_CAP_URL,
FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR));
{
@ -517,9 +521,9 @@ namespace tut
LOG_TEST(7);
LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest(
_DATA(VALID_OBJECT_ID_1,"3.0","1.0"));
_DATA(VALID_OBJECT_ID_1,"3.0","true"));
LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(
_DATA(VALID_OBJECT_ID_2,"1.0","1.0"));
_DATA(VALID_OBJECT_ID_2,"1.0","true"));
int num_refs_start = o1->getNumRefs();
{
LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD);
@ -551,10 +555,10 @@ namespace tut
// Test queue handling of objects that are marked dead.
LOG_TEST(8);
LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_1,"1.0","1.0"));
LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_2,"2.0","1.0"));
LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_3,"3.0","1.0"));
LLMediaDataClientObject::ptr_t o4 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_4,"4.0","1.0"));
LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_1,"4.0","true"));
LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_2,"3.0","true"));
LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_3,"2.0","true"));
LLMediaDataClientObject::ptr_t o4 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_4,"1.0","true"));
{
LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD);
@ -616,10 +620,11 @@ namespace tut
//
LOG_TEST(9);
LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_1,"10.0","1.0"));
LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_2,"20.0","1.0"));
LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_3,"30.0","1.0"));
LLMediaDataClientObject::ptr_t o4 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_4,"40.0","1.0"));
LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_1,"40.0","true"));
LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_2,"30.0","true"));
LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_3,"20.0","true"));
LLMediaDataClientObjectTest *object4 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_4,"10.0","true"));
LLMediaDataClientObject::ptr_t o4 = object4;
{
LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD);
@ -630,53 +635,52 @@ namespace tut
mdc->fetchMedia(o3);
mdc->fetchMedia(o4);
int test_num = 0;
int tick_num = 0;
ensure(STR(test_num) + ". is in queue 1", mdc->isInQueue(o1));
ensure(STR(test_num) + ". is in queue 2", mdc->isInQueue(o2));
ensure(STR(test_num) + ". is in queue 3", mdc->isInQueue(o3));
ensure(STR(test_num) + ". is in queue 4", mdc->isInQueue(o4));
ensure(STR(test_num) + ". post records", gPostRecords->size(), 0);
ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 0);
::pump_timers();
++test_num;
++tick_num;
// The first tick should remove the first one
ensure(STR(test_num) + ". is not in queue 1", !mdc->isInQueue(o1));
ensure(STR(test_num) + ". is in queue 2", mdc->isInQueue(o2));
ensure(STR(test_num) + ". is in queue 3", mdc->isInQueue(o3));
ensure(STR(test_num) + ". is in queue 4", mdc->isInQueue(o4));
ensure(STR(test_num) + ". post records", gPostRecords->size(), 1);
ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 1);
// Now, pretend that object 4 moved relative to the avatar such
// that it is now closest
static_cast<LLMediaDataClientObjectTest*>(
static_cast<LLMediaDataClientObject*>(o4))->setDistanceFromAvatar(5.0);
object4->setMediaInterest(50.0);
::pump_timers();
++test_num;
++tick_num;
// The second tick should still pick off item 2, but then re-sort
// have picked off object 4
ensure(STR(test_num) + ". is in queue 2", mdc->isInQueue(o2));
ensure(STR(test_num) + ". is in queue 3", mdc->isInQueue(o3));
ensure(STR(test_num) + ". is not in queue 4", !mdc->isInQueue(o4));
ensure(STR(test_num) + ". post records", gPostRecords->size(), 2);
ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is not in queue 4", !mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 2);
::pump_timers();
++test_num;
++tick_num;
// The third tick should pick off object 2
ensure(STR(test_num) + ". is not in queue 2", !mdc->isInQueue(o2));
ensure(STR(test_num) + ". is in queue 3", mdc->isInQueue(o3));
ensure(STR(test_num) + ". post records", gPostRecords->size(), 3);
ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 3);
// The fourth tick should pick off object 3
::pump_timers();
++test_num;
++tick_num;
ensure(STR(test_num) + ". is not in queue 3", !mdc->isInQueue(o3));
ensure(STR(test_num) + ". post records", gPostRecords->size(), 4);
ensure(STR(tick_num) + ". is not in queue 3", !mdc->isInQueue(o3));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 4);
ensure("queue empty", mdc->isEmpty());
}
@ -686,4 +690,249 @@ namespace tut
ensure("refcount of o4", o4->getNumRefs(), 1);
}
template<> template<>
void mediadataclient_object_t::test<10>()
{
//
// Test using the "round-robin" queue
//
LOG_TEST(10);
LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_1,"1.0","true"));
LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_2,"2.0","true"));
LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_3,"3.0","false"));
LLMediaDataClientObject::ptr_t o4 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_4,"4.0","false"));
{
LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD);
// queue up all 4 objects. The first two should be in the sorted
// queue [2 1], the second in the round-robin queue. The queues
// are serviced interleaved, so we should expect:
// 2, 4, 1, 3
mdc->fetchMedia(o1);
mdc->fetchMedia(o2);
mdc->fetchMedia(o3);
mdc->fetchMedia(o4);
int tick_num = 0;
// 0
ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 0);
::pump_timers();
++tick_num;
// 1 The first tick should remove object 2
ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 1);
ensure(STR(tick_num) + ". post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_2));
::pump_timers();
++tick_num;
// 2 The second tick should send object 4, but it will still be
// "in the queue"
ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 2);
ensure(STR(tick_num) + ". post object id", (*gPostRecords)[1]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_4));
::pump_timers();
++tick_num;
// 3 The third tick should remove object 1
ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 3);
ensure(STR(tick_num) + ". post object id", (*gPostRecords)[2]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_1));
::pump_timers();
++tick_num;
// 4 The fourth tick should send object 3, but it will still be
// "in the queue"
ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 4);
ensure(STR(tick_num) + ". post object id", (*gPostRecords)[3]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_3));
::pump_timers();
++tick_num;
// 5 The fifth tick should now identify objects 3 and 4 as no longer
// needing "updating", and remove them from the queue
ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is not in queue 3", !mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is not in queue 4", !mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 4);
::pump_timers();
// Whew....better be empty
ensure("queue empty", mdc->isEmpty());
}
ensure("refcount of o1", o1->getNumRefs(), 1);
ensure("refcount of o2", o2->getNumRefs(), 1);
ensure("refcount of o3", o3->getNumRefs(), 1);
ensure("refcount of o4", o4->getNumRefs(), 1);
}
template<> template<>
void mediadataclient_object_t::test<11>()
{
//
// Test LLMediaDataClient's destructor
//
LOG_TEST(11);
LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest(DATA);
int num_refs_start = o->getNumRefs();
{
LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD);
mdc->fetchMedia(o);
// must tick enough times to clear refcount of mdc
::pump_timers();
}
// Make sure everyone's destroyed properly
ensure("REF COUNT", o->getNumRefs(), num_refs_start);
}
template<> template<>
void mediadataclient_object_t::test<12>()
{
//
// Test the "not interesting enough" call
//
LOG_TEST(12);
LLMediaDataClientObjectTest *object1 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_1,"1.0","true"));
LLMediaDataClientObject::ptr_t o1 = object1;
LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_2,"2.0","true"));
LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_3,"3.0","true"));
LLMediaDataClientObject::ptr_t o4 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_4,"4.0","true"));
{
LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD);
// queue up all 4 objects. The first two are "interesting enough".
// Firing the timer 4 times should therefore leave them.
// Note that they should be sorted 4,3,2,1
// Then, we'll make one "interesting enough", fire the timer a few
// times, and make sure only it gets pulled off the queue
gMinimumInterestLevel = 2.5;
mdc->fetchMedia(o1);
mdc->fetchMedia(o2);
mdc->fetchMedia(o3);
mdc->fetchMedia(o4);
int tick_num = 0;
// 0
ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 0);
::pump_timers();
++tick_num;
// 1 The first tick should remove object 4
ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is not in queue 4", !mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 1);
ensure(STR(tick_num) + ". post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_4));
::pump_timers();
++tick_num;
// 2 The second tick should send object 3
ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is not in queue 3", !mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is not in queue 4", !mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 2);
ensure(STR(tick_num) + ". post object id", (*gPostRecords)[1]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_3));
::pump_timers();
++tick_num;
// 3 The third tick should not pull off anything
ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is not in queue 3", !mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is not in queue 4", !mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 2);
::pump_timers();
++tick_num;
// 4 The fourth tick (for good measure) should not pull off anything
ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is not in queue 3", !mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is not in queue 4", !mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 2);
// Okay, now futz with object 1's interest, such that it is now
// "interesting enough"
object1->setMediaInterest((F64)5.0);
// This should sort so that the queue is now [1 2]
::pump_timers();
++tick_num;
// 5 The fifth tick should now identify objects 3 and 4 as no longer
// needing "updating", and remove them from the queue
ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is not in queue 3", !mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is not in queue 4", !mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 3);
ensure(STR(tick_num) + ". post object id", (*gPostRecords)[2]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_1));
::pump_timers();
++tick_num;
// 6 The sixth tick should not pull off anything
ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is not in queue 3", !mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is not in queue 4", !mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 3);
::pump_timers();
++tick_num;
// Whew....better NOT be empty ... o2 should still be there
ensure("queue not empty", !mdc->isEmpty());
// But, we need to clear the queue, or else we won't destroy MDC...
// this is a strange interplay between the queue timer and the MDC
ensure("o2 couldn't be removed from queue", mdc->removeFromQueue(o2));
// tick
::pump_timers();
}
ensure("refcount of o1", o1->getNumRefs(), 1);
ensure("refcount of o2", o2->getNumRefs(), 1);
ensure("refcount of o3", o3->getNumRefs(), 1);
ensure("refcount of o4", o4->getNumRefs(), 1);
}
}