media-app.cc

Go to the documentation of this file.
00001 /* -*-  Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
00002 
00003 /*
00004  * media-app.cc
00005  * Copyright (C) 1997 by the University of Southern California
00006  * $Id: media-app.cc,v 1.14 2005/08/25 18:58:10 johnh Exp $
00007  *
00008  * This program is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU General Public License,
00010  * version 2, as published by the Free Software Foundation.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License along
00018  * with this program; if not, write to the Free Software Foundation, Inc.,
00019  * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
00020  *
00021  *
00022  * The copyright of this module includes the following
00023  * linking-with-specific-other-licenses addition:
00024  *
00025  * In addition, as a special exception, the copyright holders of
00026  * this module give you permission to combine (via static or
00027  * dynamic linking) this module with free software programs or
00028  * libraries that are released under the GNU LGPL and with code
00029  * included in the standard release of ns-2 under the Apache 2.0
00030  * license or under otherwise-compatible licenses with advertising
00031  * requirements (or modified versions of such code, with unchanged
00032  * license).  You may copy and distribute such a system following the
00033  * terms of the GNU GPL for this module and the licenses of the
00034  * other code concerned, provided that you include the source code of
00035  * that other code when and as the GNU GPL requires distribution of
00036  * source code.
00037  *
00038  * Note that people who make modified versions of this module
00039  * are not obligated to grant this special exception for their
00040  * modified versions; it is their choice whether to do so.  The GNU
00041  * General Public License gives permission to release a modified
00042  * version without this exception; this exception also makes it
00043  * possible to release a modified version which carries forward this
00044  * exception.
00045  *
00046  */
00047 
00048 //
00049 // Implementation of media application
00050 //
00051 // $Header: /nfs/jade/vint/CVSROOT/ns-2/rap/media-app.cc,v 1.14 2005/08/25 18:58:10 johnh Exp $
00052 
00053 #include <stdarg.h>
00054 
00055 #include "template.h"
00056 #include "media-app.h"
00057 #include "utilities.h"
00058 
00059 
00060 //----------------------------------------------------------------------
00061 // Classes related to a multimedia object
00062 //
00063 // MediaSegment
00064 // MediaSegmentList: segments in a layer
00065 // MediaPage: a stored multimedia object (stream)
00066 //----------------------------------------------------------------------
00067 
00068 MediaSegment::MediaSegment(const HttpMediaData& d) : flags_(0)
00069 {
00070     start_ = d.st();
00071     end_ = d.et();
00072     if (d.is_last())
00073         set_last();
00074     if (d.is_pref())
00075         set_pref();
00076 }
00077 
00078 void MediaSegmentList::add(const MediaSegment& s) 
00079 {
00080     MediaSegment* tmp = (MediaSegment *)head_;
00081     while ((tmp != NULL) && (tmp->before(s))) {
00082         tmp = tmp->next();
00083     }
00084 
00085     // Append at the tail, or the first element in list
00086     if (tmp == NULL) {
00087         length_ += s.datasize();
00088         if ((tail_ != NULL) && ((MediaSegment *)tail_)->overlap(s)) 
00089             // Don't need to merge because it's merged at the end
00090             ((MediaSegment*)tail_)->merge(s);
00091         else {
00092             MediaSegment *p = new MediaSegment(s);
00093             if (head_ == NULL)
00094                 head_ = tail_ = p;
00095             else 
00096                 append(p, tail_);
00097         }
00098         if (getsize() != length_) {
00099             fprintf(stderr, 
00100                 "MediaSegmentList corrupted: Point 1.\n");
00101             abort();
00102         }
00103         return;
00104     }
00105 
00106     // Update total stored length ONLY IF s is not in tmp.
00107     if (tmp->in(s)) {
00108         fprintf(stderr, 
00109             "MediaSegmentList: get a seg (%d %d) which is already in cache!\n",
00110             s.start(), s.end());
00111         fprintf(stderr, "List contents: ");
00112         print();
00113 #if 0 
00114         //Tcl::instance().eval("[Simulator instance] flush-trace");
00115         //abort();
00116 #endif
00117         // XXX Don't abort, simply continue
00118         return;
00119     }
00120 
00121     // Insert a MediaSegment into list. Note: Don't do merge!
00122     if (tmp->overlap(s)) {
00123         length_ += (s.datasize() - tmp->merge(s));
00124     } else {
00125         MediaSegment *p = new MediaSegment(s);
00126         insert(p, tmp);
00127         tmp = p;
00128         length_ += s.datasize();
00129     }
00130 
00131     if (getsize() != length_) {
00132         fprintf(stderr, "MediaSegmentList corrupted: Point 2.\n");
00133         abort();
00134     }
00135 
00136     merge_seg(tmp);
00137 
00138     if (getsize() != length_) {
00139         fprintf(stderr, "MediaSegmentList corrupted: Point 3.\n");
00140         abort();
00141     }
00142 }
00143 
00144 void MediaSegmentList::merge_seg(MediaSegment* tmp)
00145 {
00146     // See if <tmp> can be merged with next segments
00147     MediaSegment *q = tmp->next();
00148     while (q && q->overlap(*tmp)) {
00149 #if 1
00150         if ((tmp->start() == q->start()) && (tmp->end() == q->end())) {
00151             abort();
00152         }
00153 #endif
00154         tmp->merge(*q);
00155         detach(q);
00156         delete q;
00157         q = tmp->next();
00158     }
00159     // See if <tmp> can be merged with previous segments
00160     q = tmp->prev();
00161     while (q && q->overlap(*tmp)) {
00162         tmp->merge(*q);
00163         assert(tail_ != q);
00164         detach(q);
00165         delete q;
00166         q = tmp->prev();
00167     }
00168 }
00169 
00170 int MediaSegmentList::in(const MediaSegment& s)
00171 {
00172     MediaSegment* tmp = (MediaSegment *)head_;
00173     while ((tmp != NULL) && (tmp->before(s)))
00174         tmp = tmp->next();
00175 
00176     // If all segments are before s, or the first segment which isn't 
00177     // before s doesn't overlap with s, s isn't in this list.
00178     if ((tmp == NULL) || !s.in(*tmp))
00179         return 0;
00180     else 
00181         return 1;
00182 }
00183 
00184 // Get the next segment which is not before 's', but with the same size
00185 // as the given 's'. This segment may not overlap with s. 
00186 MediaSegment MediaSegmentList::get_nextseg(const MediaSegment& s) 
00187 {
00188     MediaSegment res(0, 0); // If unsuccessful, return start() = 0
00189 
00190     MediaSegment* tmp = (MediaSegment *)head_;
00191     while ((tmp != NULL) && (tmp->before(s))) 
00192         tmp = tmp->next();
00193     if (tmp == NULL) {
00194         res.set_last();
00195         return res;
00196     }
00197     assert(tmp->end() > s.start());
00198 //  // Don't return a segment which do not *OVERLAP* with s 
00199 //  // (boundary overlap is excluded).
00200 //  if ((tmp->end() <= s.start()) || (tmp->start() >= s.end())) 
00201 //      return res;
00202 
00203     // XXX How to flag that no more data is available in the future??
00204     res = s;
00205     int orig_size = s.datasize();
00206     if (res.start() < tmp->start()) {
00207         // |-------| (s)    ---> time axis
00208         //    |--------| (tmp)
00209         //
00210         // The start time of s is invalid, we need to adjust both 
00211         // the start time (and size if necessary)
00212         res.set_start(tmp->start());
00213         if (tmp->datasize() < orig_size) 
00214             // Not enough data available??
00215             res.set_datasize(tmp->datasize());
00216         else
00217             res.set_datasize(orig_size);
00218     } else if (res.end() > tmp->end()) {
00219         //    |---------| (s)    ---> time axis
00220         // |-------| (tmp)
00221         // 
00222         // The start time in s is valid, but we may need to adjust the 
00223         // end time (i.e., size) of s.
00224         res.set_datasize(tmp->end()-res.start());
00225     }
00226     // Falling through means that the requested segment is available 
00227     // and can be returned as it is.
00228 
00229     assert(res.datasize() <= tmp->datasize());
00230     if ((res.end() == tmp->end()) && (tmp->next() == NULL))
00231         // This is the last data segment of the layer
00232         res.set_last();
00233     return res;
00234 }
00235 
00236 // Note that evicting all segments in this layer may not leave enough 
00237 // space, so we return the number of bytes evicted from this layer
00238 int MediaSegmentList::evict_tail(int size)
00239 {
00240     int sz = size, tz;
00241     MediaSegment *tmp = (MediaSegment *)tail_;
00242     while ((tmp != NULL) && (sz > 0)) {
00243         // Reduce the last segment's size and adjust its playout time
00244         tz = tmp->evict_tail(sz);
00245         length_ -= tz;
00246         sz -= tz; 
00247         if (tmp->datasize() == 0) {
00248             // This segment is empty now
00249             detach(tmp);
00250             delete tmp;
00251             tmp = (MediaSegment *)tail_;
00252         }
00253     }
00254     return size - sz;
00255 }
00256 
00257 // Evicting <size> from the head of the list
00258 int MediaSegmentList::evict_head(int size)
00259 {
00260     int sz = size, tz;
00261     MediaSegment *tmp = (MediaSegment *)head_;
00262     while ((tmp != NULL) && (sz > 0)) {
00263         // Reduce the last segment's size and adjust its playout time
00264         tz = tmp->evict_head(sz);
00265         sz -= tz; 
00266         length_ -= tz;
00267         if (tmp->datasize() == 0) {
00268             // This segment is empty now
00269             detach(tmp);
00270             delete tmp;
00271             tmp = (MediaSegment *)head_;
00272         }
00273     }
00274     return size - sz;
00275 }
00276 
00277 // Evict all segments before <offset> from head and returns the size of 
00278 // evicted segments.
00279 int MediaSegmentList::evict_head_offset(int offset)
00280 {
00281     int sz = 0;
00282     MediaSegment *tmp = (MediaSegment *)head_;
00283     while ((tmp != NULL) && (tmp->start() < offset)) {
00284         if (tmp->end() <= offset) {
00285             // delete whole segment
00286             sz += tmp->datasize();
00287             length_ -= tmp->datasize();
00288             detach(tmp);
00289             delete tmp;
00290             tmp = (MediaSegment *)head_;
00291         } else {
00292             // remove part of the segment
00293             sz += offset - tmp->start();
00294             length_ -= offset - tmp->start();
00295             tmp->set_start(offset);
00296         }
00297     }
00298     if (head_ == NULL)
00299         tail_ = NULL;
00300     return sz;
00301 }
00302 
00303 // Return a list of "holes" between the given offsets
00304 MediaSegmentList MediaSegmentList::check_holes(const MediaSegment& s)
00305 {
00306     MediaSegmentList res;  // empty list
00307     MediaSegment* tmp = (MediaSegment *)head_;
00308     while ((tmp != NULL) && (tmp->before(s)))
00309         tmp = tmp->next();
00310     // If all segments are before s, s is a hole
00311     if (tmp == NULL) {
00312         res.add(s);
00313         return res;
00314     }
00315     // If s is within *tmp, there is no hole
00316     if (s.in(*tmp))
00317         return res;
00318 
00319     // Otherwise return a list of holes
00320     int soff, eoff;
00321     soff = s.start();
00322     eoff = s.end();
00323     while ((tmp != NULL) && (tmp->overlap(s))) {
00324         if (soff < tmp->start()) {
00325             // Only refetches the missing part
00326             res.add(MediaSegment(soff, min(eoff, tmp->start())));
00327 #if 1
00328             // DEBUG ONLY
00329             // Check if these holes are really holes!
00330             if (in(MediaSegment(soff, min(eoff, tmp->start())))) {
00331                 fprintf(stderr, "Wrong hole: (%d %d) ", 
00332                     soff, min(eoff, tmp->start()));
00333                 fprintf(stderr, "tmp(%d %d), s(%d %d)\n",
00334                     tmp->start(), tmp->end(),
00335                     soff, eoff);
00336                 fprintf(stderr, "List content: ");
00337                 print();
00338             }
00339 #endif
00340         }
00341         soff = tmp->end();
00342         tmp = tmp->next();
00343     }
00344     if (soff < eoff) {
00345         res.add(MediaSegment(soff, eoff));
00346 #if 1       
00347         // DEBUG ONLY
00348         // Check if these holes are really holes!
00349         if (in(MediaSegment(soff, eoff))) {
00350             fprintf(stderr, "Wrong hole #2: (%d %d)\n", 
00351                 soff, eoff);
00352             fprintf(stderr, "List content: ");
00353             print();
00354         }
00355 #endif
00356     }
00357 #if 0
00358     check_integrity();
00359 #endif
00360     return res;
00361 }
00362 
00363 void MediaSegmentList::check_integrity()
00364 {
00365     MediaSegment *p, *q;
00366     p = (MediaSegment*)head_;
00367     while (p != NULL) {
00368         q = p; 
00369         p = p->next();
00370         if (p == NULL)
00371             break;
00372         if (!q->before(*p)) {
00373             fprintf(stderr, 
00374                 "Invalid segment added: (%d %d), (%d %d)\n", 
00375                 q->start(), q->end(), p->start(), p->end());
00376             abort();
00377         }
00378     }
00379 }
00380 
00381 // Return the portion in s that is overlap with any segments in this list
00382 // Sort of complementary to check_holes(), but it does not return a list, 
00383 // hence smaller overhead. 
00384 int MediaSegmentList::overlap_size(const MediaSegment& s) const
00385 {
00386     int res = 0;
00387     MediaSegment* tmp = (MediaSegment *)head_;
00388     while ((tmp != NULL) && (tmp->before(s)))
00389         tmp = tmp->next();
00390     // If all segments are before s, there's no overlap
00391     if (tmp == NULL)
00392         return 0;
00393     // If s is within *tmp, entire s overlaps with the list
00394     if (s.in(*tmp))
00395         return s.datasize();
00396     // Otherwise adds all overlapping parts together.
00397     int soff, eoff;
00398     soff = s.start();
00399     eoff = s.end();
00400     while ((tmp != NULL) && (tmp->overlap(s))) {
00401         res += min(eoff, tmp->end()) - max(soff, tmp->start());
00402         soff = tmp->end();
00403         tmp = tmp->next();
00404     }
00405     return res;
00406 }
00407 
00408 // Debug only
00409 void MediaSegmentList::print() 
00410 {
00411     MediaSegment *p = (MediaSegment *)head_;
00412     int i = 0, sz = 0;
00413     while (p != NULL) {
00414         printf("(%d, %d)  ", p->start(), p->end());
00415         sz += p->datasize();
00416         p = p->next();
00417         if (++i % 8 == 0)
00418             printf("\n");
00419     }
00420     printf("\nTotal = %d\n", sz);
00421 }
00422 
00423 // Debug only
00424 int MediaSegmentList::getsize()
00425 {
00426     MediaSegment *p = (MediaSegment *)head_;
00427     int sz = 0;
00428     while (p != NULL) {
00429         sz += p->datasize();
00430         p = p->next();
00431     }
00432     return sz;
00433 }
00434 
00435 // Print into a char array with a given size. Abort if the size is exceeded.
00436 char* MediaSegmentList::dump2buf()
00437 {
00438     char *buf = new char[1024];
00439     char *b = buf;
00440     MediaSegment *p = (MediaSegment *)head_;
00441     int i = 0, sz = 1024;
00442     buf[0] = 0;
00443     while (p != NULL) {
00444         // XXX snprintf() should either be in libc or implemented
00445         // by TclCL (see Tcl2.cc there).
00446         i = snprintf(b, sz, "{%d %d} ", p->start(), p->end());
00447         sz -= i;
00448         // Boundary check: if less than 50 bytes, allocate new buf
00449         if (sz < 50) {
00450             char *tmp = new char[strlen(buf)+1024];
00451             strcpy(tmp, buf);
00452             delete []buf;
00453             buf = tmp;
00454             b = buf + strlen(buf);
00455             sz += 1024;
00456         } else 
00457             b += i;
00458         p = p->next();
00459     }
00460     return buf;
00461 }
00462 
00463 
00464 
00465 HttpMediaData::HttpMediaData(const char* sender, const char* page, int layer, 
00466                  int st, int et) :
00467     HttpData(MEDIA_DATA, 0), layer_(layer), st_(st), et_(et), flags_(0)
00468 {
00469     assert(strlen(page)+1 <= (size_t)HTTP_MAXURLLEN);
00470     strcpy(page_, page);
00471     assert(strlen(sender)+1 <= (size_t)HTTP_MAXURLLEN);
00472     strcpy(sender_, sender);
00473 }
00474 
00475 
00476 
00477 static class MappClass : public TclClass {
00478 public:
00479     MappClass() : TclClass("Application/MediaApp") {}
00480     TclObject* create(int argc, const char*const* argv) {
00481         if (argc > 4) 
00482             return (new MediaApp(argv[4]));
00483         return NULL;
00484     }
00485 } class_mapp;
00486 
00487 MediaApp::MediaApp(const char* page) : 
00488     log_(0), num_layer_(0), last_layer_(0)
00489 {
00490     strcpy(page_, page);
00491 
00492     // Initialize all layer data pointers
00493     for (int i = 0; i < MAX_LAYER; i++)
00494         data_[i].set_start(0); 
00495 
00496     bind("segmentSize_", &seg_size_);
00497 }
00498 
00499 void MediaApp::start()
00500 {
00501     fprintf(stderr, "MediaApp::start() not supported\n");
00502     abort();
00503 }
00504 
00505 void MediaApp::stop()
00506 {
00507     // Called when we want to stop the RAP agent
00508     rap()->stop();
00509 }
00510 
00511 AppData* MediaApp::get_data(int& nbytes, AppData* req) 
00512 {
00513     AppData *res;
00514     if (req == NULL) {
00515         MediaRequest p(MEDIAREQ_GETSEG);
00516         p.set_name(page_);
00517         // We simply rotating the layers from which to send data
00518         if (num_layer_ > 0) {
00519             p.set_layer(last_layer_++);
00520             last_layer_ = last_layer_ % num_layer_;
00521         } else 
00522             p.set_layer(0); 
00523         p.set_st(data_[0].start());
00524         p.set_datasize(seg_size_);
00525         p.set_app(this);
00526         res = target()->get_data(nbytes, &p);
00527     } else 
00528         res = target()->get_data(nbytes, req);
00529 
00530     // Update the current data pointer
00531     assert(res != NULL);
00532     HttpMediaData *p = (HttpMediaData *)res;
00533 
00534     // XXX For now, if the return size is 0, we assume that the 
00535     // transmission stops. Otherwise there is no way to tell the 
00536     // RAP agent that there's no more data to send
00537     if (p->datasize() <= 0) {
00538         // Should NOT advance sending data pointer because 
00539         // if this is a cache which is downloading from a slow
00540         // link, it is possible that the requested data will
00541         // become available in the near future!!
00542         delete p;
00543         return NULL;
00544     } else {
00545         // Set current data pointer to the right ones
00546         // If available data is more than seg_size_, only advance data
00547         // pointer by seg_size_. If less data is available, only 
00548         // advance data by the amount of available data.
00549         //
00550         // XXX Currently the cache above does NOT pack data from 
00551         // discontinugous blocks into one packet. May need to do 
00552         // that later. 
00553         assert((p->datasize() > 0) && (p->datasize() <= seg_size_));
00554         data_[p->layer()].set_start(p->et());
00555         data_[p->layer()].set_datasize(seg_size_);
00556     }
00557     return res;
00558 }
00559 
00560 int MediaApp::command(int argc, const char*const* argv)
00561 {
00562     Tcl& tcl = Tcl::instance();
00563     if (strcmp(argv[1], "log") == 0) {
00564         int mode;
00565         log_ = Tcl_GetChannel(tcl.interp(), 
00566                       (char*)argv[2], &mode);
00567         if (log_ == 0) {
00568             tcl.resultf("%s: invalid log file handle %s\n",
00569                     name(), argv[2]);
00570             return TCL_ERROR;
00571         }
00572         return TCL_OK;
00573     } else if (strcmp(argv[1], "evTrace") == 0) { 
00574         char buf[1024], *p;
00575         if (log_ != 0) {
00576             sprintf(buf, "%.17g ", 
00577                 Scheduler::instance().clock());
00578             p = &(buf[strlen(buf)]);
00579             for (int i = 2; i < argc; i++) {
00580                 strcpy(p, argv[i]);
00581                 p += strlen(argv[i]);
00582                 *(p++) = ' ';
00583             }
00584                 // Stick in a newline.
00585             *(p++) = '\n', *p = 0;
00586             Tcl_Write(log_, buf, p-buf);
00587         }
00588         return TCL_OK;
00589     } else if (strcmp(argv[1], "set-layer") == 0) {
00590         int n = atoi(argv[2]);
00591         if (n >= MAX_LAYER) {
00592             fprintf(stderr, 
00593                 "Too many layers than maximum allowed.\n");
00594             return TCL_ERROR;
00595         }
00596         num_layer_ = n;
00597         return TCL_OK;
00598     }
00599     return Application::command(argc, argv);
00600 }
00601 
00602 void MediaApp::log(const char* fmt, ...)
00603 {
00604     char buf[1024], *p;
00605     char *src = Address::instance().print_nodeaddr(rap()->addr());
00606     sprintf(buf, "%.17g i %s ", Scheduler::instance().clock(), src);
00607     delete []src;
00608     p = &(buf[strlen(buf)]);
00609     va_list ap;
00610     va_start(ap, fmt);
00611     vsprintf(p, fmt, ap);
00612     if (log_ != 0)
00613         Tcl_Write(log_, buf, strlen(buf));
00614 }
00615 
00616 
00617 //----------------------------------------------------------------------
00618 // MediaApp enhanced with quality adaptation
00619 //----------------------------------------------------------------------
00620 void QATimer::expire(Event *)
00621 {
00622     a_->UpdateState();
00623     resched(a_->UpdateInterval());
00624 }
00625 
00626 static class QAClass : public TclClass {
00627 public:
00628     QAClass() : TclClass("Application/MediaApp/QA") {}
00629     TclObject* create(int argc, const char*const* argv) {
00630         if (argc > 4) 
00631             return (new QA((const char *)(argv[4])));
00632         return NULL;
00633     }
00634 } class_qa_app;
00635 
00636 //#define CHECK 1
00637 //#define DBG 1
00638 
00639 QA::QA(const char *page) : MediaApp(page)
00640 {
00641     updTimer_ = new QATimer(this);
00642 
00643     bind("LAYERBW_", &LAYERBW_);
00644     bind("MAXACTIVELAYERS_", &MAXACTIVELAYERS_);
00645     bind("SRTTWEIGHT_", &SRTTWEIGHT_);
00646     bind("SMOOTHFACTOR_", &SMOOTHFACTOR_);
00647     bind("MAXBKOFF_", &MAXBKOFF_);
00648     bind("debug_output_", &debug_);
00649     bind("pref_srtt_", &pref_srtt_);
00650 
00651     for(int j = 0; j < MAX_LAYER; j++) {
00652         buffer_[j] = 0.0;
00653         sending_[j] = 0;
00654         playing_[j] = 0;
00655         drained_[j] = 0.0;
00656         bw_[j] = 0.0;
00657         pref_[j] = 0;
00658     }
00659     poffset_ = 0; 
00660     playTime_ = 0;   // Should initialize it
00661     startTime_ = -1; // Used to tell the first packet
00662 
00663     // Moving average weight for transmission rate average
00664     rate_weight_ = 0.01;
00665     avgrate_ = 0.0;
00666 }
00667 
00668 QA::~QA()
00669 {
00670     if (updTimer_) {
00671         if (updTimer_->status() != TIMER_IDLE)
00672             updTimer_->cancel();
00673         delete updTimer_;
00674     }
00675 }
00676 
00677 void QA::debug(const char* fmt, ...)
00678 {
00679     if (!debug_) 
00680         return;
00681 
00682     char buf[1024], *p;
00683     char *src = Address::instance().print_nodeaddr(rap()->addr());
00684     char *port = Address::instance().print_portaddr(rap()->addr());
00685     sprintf(buf, "# t %.17g i %s.%s QA ", 
00686         Scheduler::instance().clock(), src, port);
00687     delete []port;
00688     delete []src;
00689     p = &(buf[strlen(buf)]);
00690     va_list ap;
00691     va_start(ap, fmt);
00692     vsprintf(p, fmt, ap);
00693     fprintf(stderr, "%s", buf);
00694 }
00695 
00696 void QA::panic(const char* fmt, ...) 
00697 {
00698     char buf[1024], *p;
00699     char *src = Address::instance().print_nodeaddr(rap()->addr());
00700     char *port = Address::instance().print_portaddr(rap()->addr());
00701     sprintf(buf, "# t %.17g i %s.%s QA PANIC ", 
00702         Scheduler::instance().clock(), src, port);
00703     delete []port;
00704     delete []src;
00705     p = &(buf[strlen(buf)]);
00706     va_list ap;
00707     va_start(ap, fmt);
00708     vsprintf(p, fmt, ap);
00709     fprintf(stderr, "%s", buf);
00710 
00711 #if 0
00712     // XXX This is specific to OUR test. Remove it in release!!
00713     Tcl::instance().eval("[Simulator instance] flush-trace");
00714     abort();
00715 #endif
00716 }
00717 
00718 // Stop all timers
00719 void QA::stop()
00720 {
00721     rap()->stop();
00722     if (updTimer_->status() != TIMER_IDLE)
00723         updTimer_->cancel();
00724 }
00725 
00726 // Empty for now
00727 int QA::command(int argc, const char*const* argv)
00728 {
00729     return MediaApp::command(argc, argv);
00730 }
00731 
00732 // When called by RAP, req is NULL. We fill in the next data segment and 
00733 // return its real size in 'size' and return the app data. 
00734 AppData* QA::get_data(int& size, AppData*)
00735 {
00736     int layers, dropped, i, l, idx, bs1, bs2,scenario, done, cnt;
00737     double slope, bufavail, bufneeded, totbufs1, totbufs2, 
00738         optbufs1[MAX_LAYER], optbufs2[MAX_LAYER], bufToDrain;
00739   
00740     static double last_rate = 0.0, last_depart, nextAdjPoint = -1,
00741         FinalDrainArray[MAX_LAYER],
00742         tosend[MAX_LAYER], FinalBuffer[MAX_LAYER];
00743     
00744     static int flag,  /* flag keeps the state of the last phase */
00745         tosendPtr = 0;
00746   
00747     // Get RAP info
00748     double rate = seg_size_ / rap()->ipg();
00749     double srtt =  rap()->srtt();
00750     Scheduler& s = Scheduler::instance();
00751     double now = s.clock();
00752     int anyAck = rap()->anyack();
00753 
00754     assert((num_layer_ > 0) && (num_layer_ < MAX_LAYER));
00755   
00756     // this part is added for the startup
00757     // to send data for the base layer until the first ACK arrives.
00758     // This is because we don't have an estimate for SRTT and slope of inc
00759     // Make sure that SRTT is updated properly when ACK arrives
00760     if (anyAck == 0) {
00761         sending_[0] = 1;
00762         return output(size, 0);
00763         debug("INIT Phase, send packet: layer 0 in send_pkt, \
00764 rate: %.3f, avgrate: %.3f, srtt:%.3f\n", rate, avgrate_, srtt);
00765     }
00766   
00767     layers = 0;
00768     // we can only calc slope when srttt has a right value
00769     // i.e. RAP has received an ACK
00770     slope = seg_size_/srtt;
00771     bufavail = 0.0;
00772 
00773     // XXX Is this a correct initial value????
00774     bufneeded = 0.0; 
00775   
00776     // calculate layers & bufavail
00777     for (i = 0; i < MAX_LAYER; i++) {
00778         layers += sending_[i];
00779         if (sending_[i] == 1) 
00780             bufavail += buffer_[i];
00781         else
00782             /* debug only */
00783             if ((i < MAX_LAYER - 1) && (sending_[i+1] == 1))
00784                 panic("ERROR L%d is not sent but L%d is.\n",
00785                       i, i+1);
00786     }
00787     
00788     // check for startup phase
00789     if((layers == 1) && (playing_[0] != 1)){
00790         // L0 still buffers data, we are in startup phase
00791         // let's check
00792         if (sending_[0] == 0) {
00793             panic("ERROR sending[0]=0 !!!");
00794         }
00795         AppData *res = output(size, 0);
00796         debug("STARTUP, send packet: layer 0\n");
00797         
00798         // Start playout if we have enough data for L0
00799         // The amount of buffered data for startup can be diff
00800         bufneeded = max(4*BufNeed((LAYERBW_-rate/2.0), slope), 
00801                 2*MWM(srtt));
00802 
00803         if (buffer_[0] >= bufneeded) {
00804             playing_[0] = 1;
00805             sending_[0] = 1;  
00806             drained_[0] = 0;  /* srtt*LAYERBW; */
00807             startTime_ = now; // start the playback at the client
00808             playTime_ = now;  // playout time of the receiver. 
00809             debug("... START Playing_ layer 0, buffer[0] = %f!\n",
00810                   buffer_[0]);
00811             // start emulating clients consumption
00812             if (updTimer_->status() == TIMER_IDLE)
00813                 updTimer_->sched(srtt);
00814         }
00815         return(res);
00816     }
00817   
00818     // Store enough buffer before playing a layer. 
00819     // XXX, NOTE: it is hard to do this, when we add a new layer
00820     // the server sets the playout time of the first segment
00821     // to get to the client in time, It is hard to make sure
00822     // that a layer has MRM worth if data before stasting its
00823     // playback because it adds more delay
00824     // the base layer starts when it has enough buffering
00825     // the higher layers are played out when their data is available
00826     // so this is not needed 
00827     //for (i = 0; i < MAX_LAYER; i++) {
00828     // if ((sending_[i] == 1) && (playing_[i] == 0) &&
00829     //    (buffer_[i] > MWM(srtt))) {
00830     //  debug("Resume PLAYING Layer %d, play: %d send: %d\n",
00831     //    i, playing_[i], sending_[i]);
00832     //  playing_[i]=1;
00833     //  drained_[i] = 0; /* XXX, not sure about this yet 
00834     //            * but if we set this to max it causes
00835     //        * a spike at the adding time
00836     //            */
00837     //  /* drained_[i]=LAYERBW*SRTT; */
00838     //}
00839     //}
00840   
00841     // perform the primary drop if we are in drain phase 
00842     if (rate < layers*LAYERBW_) {
00843         bufneeded = (MWM(srtt)*layers) + 
00844             BufNeed((layers*LAYERBW_-rate), slope);
00845         //   debug("tot_bufavail: %7.1f bufneeded: %7.1f, layers: %d",
00846         //          bufavail, bufneeded, layers);
00847         dropped = 0;
00848         // XXX Never ever do primary drop layer 0!!!!
00849         while ((bufneeded > TotalBuf(layers, buffer_)) && 
00850                (layers > 1)) {
00851             debug("** Primary DROPPED L%d, TotBuf(avail:%.1f \
00852 needed:%.1f), buf[%d]: %.2f\n", 
00853                   layers-1, TotalBuf(layers, buffer_), bufneeded,
00854                   layers-1,buffer_[layers-1]);
00855             layers--;
00856             dropped++;
00857             sending_[layers] = 0;
00858             bufneeded = (MWM(srtt)*layers)+ 
00859                 BufNeed(((layers)*LAYERBW_-rate),slope); 
00860         }
00861     } 
00862     
00863     // just for debugging
00864     // here is the case when even the base layer can not be kept
00865     if ((bufneeded > TotalBuf(layers, buffer_)) && (layers == 1)) {
00866         // XXX We should still continue, shouldn't we????
00867         debug("** Not enough buf to keep the base layer, \
00868 TotBuf(avail:%.1f, needed:%.1f), \n",
00869               TotalBuf(layers, buffer_), bufneeded);
00870     }
00871 
00872     if (layers == 0) {
00873         //      panic("** layers =0 !!");
00874         sending_[0] = 1;
00875         playing_[0] = 0;
00876         if (updTimer_->status() != TIMER_IDLE)
00877             updTimer_->cancel();
00878         debug("** RESTART Phase, set playing_[0] to 0 to rebuffer data\n");
00879         return output(size, 0);
00880     }
00881 
00882     // now check to see which phase we are in
00883     if (rate >= layers*LAYERBW_) {
00884 
00885         /******************
00886          ** filling phase **
00887          *******************/
00888 /*      
00889 debug("-->> FILLING, layers: %d now: %.2f, rate: %.3f, avgrate: %.3f, \
00890  srtt:%.3f, slope: %.3f\n",
00891       layers, now, rate, avgrate_, srtt, slope);
00892 */
00893       
00894         last_rate = rate; /* this is used for the next drain phase */
00895         flag = 1;
00896         /* 
00897          * 1) send for any layer that its buffer is below MWM
00898          * MWM is the min amount of buffering required to absorbe 
00899          * jitter
00900          * each active layer must have atleast MWM data at all time
00901          * this also ensures proper bw share, we do NOT explicitly 
00902          * alloc BW share during filling
00903          * Note: since we update state of the buffers on a per-packet 
00904          * basis, we don't need to ensure that each layer gets a share 
00905          * of bandwidth equal to its consumption rate. 
00906          */
00907         for (i=0;i<layers;i++) {
00908             if (buffer_[i] < MWM(srtt)) {
00909                 if ((buffer_[i-1] <= buffer_[i]+seg_size_) &&
00910                     (i > 0))
00911                     idx = i-1;
00912                 else 
00913                     idx = i;
00914 //    debug("A:sending layer %d, less than MWM, t: %.2f\n", 
00915 //      i,now);
00916                 return output(size, idx);
00917             }
00918         }
00919 
00920         /* 
00921          * Main filling algorithm based on the pesudo code 
00922          * find the next optimal state to reach 
00923          */
00924         /* init param */
00925         bs1 = 0;
00926         bs2 = 0;
00927         totbufs1 = 0;
00928         totbufs2 = 0;
00929         for (l=0; l<MAX_LAYER; l++) {
00930             optbufs1[l] = 0.0;
00931             optbufs2[l] = 0.0;
00932         }
00933         
00934         // XXX Note: when per-layer BW is low, and srtt is very small 
00935         // (e.g., in a LAN), the following code will result in that 
00936         // one buffered 
00937         // segment will produce a abort() of "maximum backoff reached".
00938 
00939         /* next scenario 1 state */
00940         while ((totbufs1 <= TotalBuf(layers, buffer_)) && 
00941                (bs1 <= MAXBKOFF_)) {
00942             totbufs1 = 0.0;
00943             bs1++;
00944             for (l=0; l<layers;l++) {
00945                 optbufs1[l] = bufOptScen1(l,layers,rate,slope,
00946                               bs1)+MWM(srtt);
00947                 totbufs1 += optbufs1[l];
00948             }
00949         }
00950 
00951         // bs1 is the min no of back off that we can not handle for 
00952         // s1 now
00953         /* next secenario 2 state */
00954         while ((totbufs2 <= TotalBuf(layers, buffer_)) && 
00955                (bs2 <= MAXBKOFF_)) {
00956             totbufs2 = 0.0;
00957             bs2++;
00958             for (l=0; l<layers;l++) {
00959                 optbufs2[l] = bufOptScen2(l,layers,rate,slope,
00960                               bs2)+MWM(srtt);
00961                 totbufs2 += optbufs2[l];
00962             }
00963         }
00964         
00965         /* 
00966          * NOTE: at this point, totbufs1 could be less than total 
00967          * buffering
00968          * when it is enough for recovery from rate = 0;
00969          * so totbufs1 <= TotalBuf(layers, buffer) is OK
00970          * HOWEVER, in this case, we MUST shoot for scenario 2
00971          */
00972 
00973         /* debug */
00974 /*
00975        if ((totbufs2 <= TotalBuf(layers, buffer_)) && (bs2 <= MAXBKOFF_)) {
00976     panic("# ERROR: totbufs1: %.2f,tot bufs2: %.2f, \
00977  totbuf: %.2f, bs1: %d, bs2: %d, totneededbuf1: %.2f, totneededbuf2: %2f\n",
00978           totbufs1, totbufs2, TotalBuf(layers, buffer_), bs1, bs2,
00979           TotalBuf(layers, optbufs1), TotalBuf(layers, optbufs2));
00980        }
00981 */
00982         /* debug */
00983         if (bs2 >= MAXBKOFF_)
00984             debug("WARNING: MAX No of backoff Reached, bs1: %d, \
00985 bs2: %d\n", bs1, bs2);
00986 
00987         /* Check for adding condition */
00988         //if ((bs1 > SMOOTHFACTOR_) && (bs2 > SMOOTHFACTOR_) && 
00989         //  (layers < MAX_LAYER)) {
00990         if ((bs1 > SMOOTHFACTOR_) && (bs2 > SMOOTHFACTOR_)){
00991             // Check if all layers are already playing
00992             // Assume all streams have the same # of layer: 
00993             // MAX_LAYER
00994             assert(layers <= num_layer_);
00995 
00996             // XXX Only limit the rate when we have all layers 
00997             // playing. There should be a better way to limit the 
00998             // transmission rate earlier! Note that we need RAP to
00999             // fix its IPG as soon as we fix the rate here. Thus, 
01000             // RAP should do that in its IpgTimeout()
01001             // instead of DecreaseIpg(). See rap.cc.
01002             if (layers == num_layer_){
01003 #if 0
01004                 if (rate < num_layer_*LAYERBW_)
01005                     panic("ERROR: rate: %.2f is less than \
01006 MAX BW for all %d layers!\n", rate, layers);
01007 #endif
01008                 // Ask RAP to fix the rate at MAX_LAYER*LAYERBW
01009                 rap()->FixIpg((double)seg_size_/
01010                           (double)(num_layer_*LAYERBW_));
01011                 // Mux the bandwidth evenly among layers  
01012                 return output(size, layers - 1);      
01013             }
01014 
01015             // Calculate the first packet offset in this new layer
01016             int off_start = (int)floor((poffset_ + MWM(srtt)) / 
01017                            seg_size_) * seg_size_;
01018             // XXX Does the application have data between 
01019             // off_start_ and off_start_+MWM(srtt)??
01020       
01021             // XXX If the computed offset falls behind, we just 
01022             // continue to send.
01023             if (data_[layers].start() <= off_start) {
01024                 // Set LayerOffset[newlayer] = 
01025                 //     poffset_ + MWM(srtt) * n: 
01026                 // - n times roundtrip time of data, LET n BE 1
01027                 // Round this offset to whole segment
01028                 data_[layers].set_start(off_start);
01029                 data_[layers].set_datasize(seg_size_);
01030             }
01031             // Make sure that all corresponding data in lower 
01032             // layers have been sent out, i.e., the last byte of 
01033             // current segment of the new layer should be less 
01034             // than the last byte of all lower layers
01035             if (data_[layers].end() > data_[layers-1].start()) 
01036                 // XXX Do not send anything if we don't have
01037                 // data!! Otherwise we'll dramatically increase
01038                 // the sending rate of lower laters. 
01039                 return NULL;
01040                 //  return output(size, layers-1);
01041 
01042             sending_[layers] = 1;
01043             AppData *res = output(size, layers);
01044             if (res == NULL) {
01045                 // Drop the newly added layer because we 
01046                 // don't have data 
01047                 sending_[layers] = 0;
01048                 // However, do prefetching in case we'll add 
01049                 // it again later
01050                 int st = (int)floor((data_[layers].start()+
01051                         pref_srtt_*LAYERBW_)
01052                            /seg_size_+0.5)*seg_size_;
01053                 int et = (int)floor((data_[layers].end()+
01054                         pref_srtt_*LAYERBW_)
01055                            /seg_size_+0.5)*seg_size_;
01056                 if (et > pref_[layers]) {
01057                     pref_[i] = et;
01058                     MediaSegment s(st, et);
01059                     check_availability(i, s);
01060                 }
01061                 for (i = 0; i < layers; i++) 
01062                     if (buffer_[i] < MWM(srtt)) {
01063                         res = output(size, i);
01064                         if (res != NULL)
01065                             break;
01066                     }
01067             } else {
01068                 /* LAYERBW_*srtt;should we drain this */
01069                 drained_[layers]= 0; 
01070                 debug("sending Just ADDED layer %d, t: %.2f\n",
01071                       i, now);
01072             }
01073             return res;
01074         }
01075 
01076         /* 
01077          * Find out which next step is closer
01078          * Second cond is for the cases where totbufs2 becomes 
01079          * saturated
01080          */
01081         scenario = 0; // Initial value
01082         if((totbufs1 <= totbufs2) && 
01083            (totbufs1 > TotalBuf(layers, buffer_))) {
01084             /* go for next scenario 1 with sb1 backoff */
01085             scenario = 1;
01086         } else {
01087             /* go for next scenario 2 with sb2 backoffs */
01088             scenario = 2;
01089         }
01090 
01091         /* decide which layer needs more data */
01092         if (scenario == 1) {
01093             for (l=0; l<layers; l++) {
01094                 if (buffer_[l] >= optbufs1[l]) 
01095                     continue;
01096                 //if (buffer_[l] < optbufs1[l]) {
01097                 if ((buffer_[l-1] <= buffer_[l]+seg_size_) && 
01098                     (l > 0))
01099                     idx = l-1;
01100                 else 
01101                     idx = l;
01102                 // debug("Cs1:sending layer %d to fill buffer, t: %.2f\n", 
01103                 // idx,now);
01104                 return output(size, idx);
01105             }
01106         } else if (scenario == 2) {
01107             l=0;
01108             done = 0;
01109             while ((l<layers) && (!done)){
01110                 if (TotalBuf(layers, buffer_) >= totbufs2) {
01111                     done ++;
01112                 } else {
01113                     if (buffer_[l]<min(optbufs2[l],
01114                                optbufs1[l])) {
01115                         if((buffer_[l-1] <= buffer_[l]+
01116                             seg_size_) && (l>0))
01117                             idx = l-1;
01118                         else 
01119                             idx = l;
01120 //          debug("Cs2:sending layer %d to fill buffer, t: %.2f\n", 
01121 //          idx,now);
01122                         return output(size, idx);
01123                     }
01124                     l++;
01125                 }
01126             } /* while */
01127         } else 
01128             panic("# ERROR: Unknown scenario: %d !!\n", scenario);
01129 
01130         /* special cases when we get out of this for loop */
01131         if(scenario == 1){
01132             panic("# Should not reach here, totbuf: %.2f, \
01133 totbufs1: %.2f, layers: %d\n",
01134                   TotalBuf(layers, buffer_), totbufs1, layers);
01135         }
01136 
01137         if (scenario == 2) {
01138             /* 
01139              * this is the point where we have satisfied buffer 
01140              * requirement for the next scenario 1 already, 
01141              * i.e. the MIN() value.
01142              * so we relax that and shoot for bufs2[l]
01143              */
01144 
01145             /* 
01146              * if scenario 2, repeat the while loop without min 
01147              * cond we have alreddy satisfied the condition for 
01148              * the next scenario 1
01149              */
01150             l=0;
01151             while (l < layers) {
01152                 if (buffer_[l] < optbufs2[l]) {
01153                     if ((buffer_[l-1] <= buffer_[l]+
01154                          seg_size_) && (l>0))
01155                         idx = l-1;
01156                     else 
01157                         idx = l;
01158 //      debug("Cs22:sending layer %d to fill buffer, t: %.2f\n", idx,now);
01159                     return output(size, idx);
01160                 }
01161                 l++;
01162             }/* while */
01163         }
01164 
01165         panic("# Opps, should not reach here, bs1: %d, bs2: %d, \
01166 scen: %d, totbufs1: %.2f, totbufs2: %.2f, totbufavail: %.2f\n", 
01167               bs1, bs2, scenario, totbufs1,
01168               totbufs2, TotalBuf(layers, buffer_));
01169     
01170         /* NEVER REACH HERE */
01171     
01172     } else { /* rate < layers*LAYERBW_ */
01173         /*******************
01174          ** Draining phase **
01175          *******************/
01176 /*
01177     debug("-->> DRAINING, layers: %d rate: %.3f, avgrate: %.3f, srtt:%.3f, \
01178  slope: %.3f\n", 
01179      layers, rate, avgrate_, srtt, seg_size_/srtt);
01180 */
01181 
01182         /*
01183          * At the beginning of a new drain phase OR 
01184          * another drop in rate during a draining phase OR
01185          * dec of slope during a draining phase that results in 
01186          * a new drop 
01187          */
01188 
01189         /*
01190          * 1) the highest priority action at this point is to ensure 
01191          * all surviving layers have min amount of buffering, if not, 
01192          * try to fill that layer
01193          */
01194         double lowest=buffer_[0];
01195         int lowix=0;
01196         for(i=0;i<layers;i++) {
01197             if (lowest>buffer_[i]) {
01198                 lowest=buffer_[i];
01199                 lowix=i;
01200             }
01201         }
01202         if (lowest<MWM(srtt)) {
01203             last_depart = now;
01204 //       debug("A':sending layer %d, below MWM in Drain t: %.2f\n",
01205 //      lowix, now);
01206             return output(size, lowix);
01207         }
01208 
01209         if((nextAdjPoint < 0) || /* first draining phase */
01210            (flag >= 0) || /* after a filling phase */
01211            (now >= nextAdjPoint) || /* end of the curr interval */
01212            ((rate < last_rate) && (flag < 0)) || /* new backoff */
01213            (AllZero(tosend, layers))) /* all pkt are sent */ {
01214 
01215             /* start of a new interval */
01216             /* 
01217              * XXX, should update the nextAdjPoint diff for 
01218              * diff cases 
01219              */
01220             nextAdjPoint = now + srtt;
01221             bufToDrain = LAYERBW_*layers - rate;
01222             /*
01223              * calculate optimal dist. of bufToDrain across all 
01224              * layers. FinalDrainArray[] is the output
01225              * FinalBuffer[] is the final state
01226              */
01227             if (bufToDrain <= 0)
01228                 panic("# ERROR: bufToDrain: %.2f\n", 
01229                       bufToDrain);
01230 
01231             DrainPacket(bufToDrain, FinalDrainArray, layers, rate, 
01232                     srtt, FinalBuffer);
01233 
01234             for(l=0; l<MAX_LAYER; l++){
01235                 tosend[l] = 0;
01236             }
01237 
01238             for(l=0; l<layers; l++){
01239                 tosend[l] = srtt*LAYERBW_ - FinalDrainArray[l];
01240                 // Correct for numerical error
01241                 if (fabs(tosend[l]) < QA_EPSILON)
01242                     tosend[l] = 0.0;
01243             }
01244 
01245             /* 
01246              * XXX, not sure if this is the best thing 
01247              * we might only increase it
01248              */
01249             tosendPtr = 0;
01250 
01251             /* debug only */
01252             if ((bufToDrain <= 0) || 
01253                 AllZero(FinalDrainArray, layers) ||
01254                 AllZero(tosend, layers)) {
01255                 debug("# Error: bufToDrain: %.2f, %d layers, "
01256                       "srtt: %.2f\n", 
01257                       bufToDrain, layers, srtt);
01258                 for (l=0; l<layers; l++)
01259                     debug("# FinalDrainArray[%d]: %.2f, "
01260                           "tosend[%d]: %.2f\n", l, 
01261                           FinalDrainArray[l],l, tosend[l]);
01262             }
01263             /*******/
01264         }
01265 
01266         flag = -1;
01267         last_rate = rate;
01268         done = 0;
01269         cnt = 1;  
01270         while ((!done) && (cnt <= layers)) {
01271             if (tosend[tosendPtr] > 0) {
01272                 if ((buffer_[tosendPtr-1] <= buffer_[tosendPtr]
01273                      + seg_size_) && (tosendPtr > 0))
01274                     idx = tosendPtr-1;
01275                 else 
01276                     idx = tosendPtr;
01277                 tosend[tosendPtr] -= seg_size_;
01278                 if (tosend[tosendPtr] < 0)
01279                     tosend[tosendPtr] = 0;
01280                 return output(size, idx);
01281             }
01282             cnt++;
01283             tosendPtr = (tosendPtr+1) % layers;
01284         }
01285 
01286         // XXX End of Drain Phase
01287         // For now, send a chunk from the base layer. Modify it later!!
01288         return output(size, 0);
01289     } /* if (rate >= layers*LAYERBW_) */
01290 
01291     panic("# QA::get_data() reached the end. \n");
01292     /*NOTREACHED*/
01293     return NULL;
01294 }
01295 
01296 //-----------------------------------------
01297 //-------------- misc routine
01298 //------------------------------------------
01299 
01300 // return 1 is all first "len" element of "arr" are zero
01301 // and 0 otherwise
01302 int QA::AllZero(double *arr, int len)
01303 {
01304     int i;
01305   
01306     for (i=0; i<len; i++)
01307         if (arr[i] != 0.0)
01308             // debug("-- arr[%d}: %f\n", i, arr[i]);
01309             return 0;
01310     return 1;
01311 }
01312 
01313 
01314 //
01315 // Calculate accumulative amount of buffering for the lowest "n" layers
01316 //
01317 double QA::TotalBuf(int n, double *buffer)
01318 {
01319     double totbuf = 0.0;
01320     int i;
01321 
01322     for(i=0; i<n; i++)
01323         totbuf += buffer[i]; 
01324     return totbuf;
01325 }
01326 
01327 // Update buffer_ information for a given layer
01328 // Get an output data packet from applications above
01329 AppData* QA::output(int& size, int layer)
01330 {
01331     int i;
01332     assert((sending_[layer] == 1) || (startTime_ == -1));
01333 
01334     // In order to send out a segment, all corresponding segments of 
01335     // the lower layers must have been sent out
01336     if (layer > 0)
01337         if (data_[layer-1].start() <= data_[layer].start())
01338             return output(size, layer-1);
01339 
01340     // Get and output the data at the current data pointer
01341     MediaRequest q(MEDIAREQ_GETSEG);
01342     q.set_name(page_);
01343     q.set_layer(layer);
01344     q.set_st(data_[layer].start());
01345     q.set_datasize(seg_size_);
01346     q.set_app(this);
01347     AppData* res = target()->get_data(size, &q);
01348 
01349     assert(res != NULL);
01350     HttpMediaData *p = (HttpMediaData *)res; 
01351 
01352     if (p->datasize() <= 0) {
01353         // When the data is not available:
01354         // Should NOT advance sending data pointer because 
01355         // if this is a cache which is downloading from a slow
01356         // link, it is possible that the requested data will
01357         // become available in the near future!!
01358 
01359         // We have already sent out the last segment of the base layer,
01360         // now we are requested for the segment beyond the last one
01361         // in the base layer. In this case, consider the transmission
01362         // is complete and tear down the connection.
01363         if (p->is_finished()) {
01364             rap()->stop();
01365             // XXX Shouldn't this be done inside mcache/mserver??
01366             Tcl::instance().evalf("%s finish-stream %s", 
01367                           target()->name(), name());
01368         } else if (!p->is_last()) {
01369             // If we coulnd't find anything within q, move data 
01370             // pointer forward to skip holes.
01371             MediaSegment tmp(q.et(), q.et()+seg_size_);
01372             check_layers(p->layer(), tmp);
01373             // If we can, advance. Otherwise wait for
01374             // lower layers to advance first.
01375             if (tmp.datasize() > 0) {
01376                 assert(tmp.datasize() <= seg_size_);
01377                 data_[p->layer()].set_start(tmp.start());
01378                 data_[p->layer()].set_end(tmp.end());
01379             }
01380         }
01381         delete p;
01382         return NULL;
01383     }
01384 
01385     // Set current data pointer to the right ones
01386     // If available data is more than seg_size_, only 
01387     // advance data pointer by seg_size_. If less data 
01388     // is available, only advance data by the amount 
01389     // of available data.
01390     //
01391     // XXX Currently the cache above does NOT pack data 
01392     // from discontinugous blocks into one packet. May 
01393     // need to do that later. 
01394 //      if (p->is_last())
01395 //          data_[p->layer()].set_last();
01396     assert((p->datasize() > 0) && (p->datasize() <= seg_size_));
01397     // XXX Before we move data pointer forward, make sure we don't violate
01398     // layer ordering rules. Note we only need to check end_ because 
01399     // start_ is p->et() which is guaranteed to be valid
01400     MediaSegment tmp(p->et(), p->et()+seg_size_);
01401     check_layers(p->layer(), tmp);
01402     if (tmp.datasize() > 0) {
01403         assert(tmp.datasize() <= seg_size_);
01404         data_[p->layer()].set_start(tmp.start());
01405         data_[p->layer()].set_end(tmp.end());
01406     } else {
01407         // Print error messages, do not send anything and wait for 
01408         // next time so that hopefully lower layers will already 
01409         // have advanced.
01410         fprintf(stderr, "# ERROR We cannot advance pointers for "
01411             "segment (%d %d)\n", tmp.start(), tmp.end());
01412         for (i = 0; i < layer; i++) 
01413             fprintf(stderr, "Layer %d, data ptr (%d %d) \n",
01414                 i, data_[i].start(), data_[i].end());
01415         delete p;
01416         return NULL;
01417     }
01418 
01419     // Let me know that we've sent out this segment. This is used
01420     // later to drain data (DrainBuffers())
01421     outlist_[p->layer()].add(MediaSegment(p->st(), p->et()));
01422 
01423     buffer_[layer] += p->datasize();
01424     bw_[layer] += p->datasize();
01425     drained_[layer] -= p->datasize();
01426   
01427     //offset_[layer] += seg_size_;
01428     avgrate_ = rate_weight_*rate() + (1-rate_weight_)*avgrate_;
01429 
01430     // DEBUG check
01431     for (i = 0; i < layer-1; i++)
01432         if (data_[i].end() < data_[i+1].end()) {
01433             for (int j = 0; j < layer; j++)
01434                 fprintf(stderr, "layer i: (%d %d)\n", 
01435                     data_[i].start(), data_[i].end());
01436             panic("# ERROR Wrong layer sending order!!\n");
01437         }
01438 
01439     return res;
01440 }
01441 
01442 void QA::check_layers(int layer, MediaSegment& tmp) {
01443     // XXX While we are moving pointer forward, make sure
01444     // that we are not violating layer boundary constraint
01445     for (int i = layer-1; i >= 0; i--) 
01446         // We cannot go faster than a lower layer!!
01447         if (tmp.end() > data_[i].end())
01448             tmp.set_end(data_[i].end());
01449 }
01450 
01451 //
01452 // This is optimal buffer distribution for scenario 1.
01453 // NOTE: rate is the current rate before the backoff
01454 // Jan 28, 99
01455 //
01456 // This routines performs buffer sharing by giveing max share
01457 // to the lowest layer, i.e. it fills the triangle in a bottom-up
01458 // starting from the base layer. We use this routine instead of bufOpt,
01459 // for all cases during filling phase. Allocation based on diagonal strips
01460 //
01461 double QA::bufOptScen1(int layer, int layers, double currrate, 
01462                double slope, int backoffs)
01463 {
01464     double smallt, larget, side, rate;
01465   
01466     if (backoffs < 0) {
01467         panic("# ERROR: backoff: %d in bufOptScen1\n", 
01468               backoffs);
01469     }
01470     rate = currrate/pow(2,backoffs);
01471     side = LAYERBW_*layers - (rate + layer*LAYERBW_);
01472     if (side <= 0.0) 
01473         return(0.0);
01474     larget = BufNeed(side, slope);
01475     side = LAYERBW_*layers - (rate + (layer+1)*LAYERBW_);
01476     if (side < 0.0) 
01477         side = 0.0;
01478     smallt = BufNeed(side, slope);
01479 
01480     return (larget-smallt);
01481 }
01482 
01483 //
01484 // This routine calculate optimal buffer distribution for a layer
01485 // in scenario 2 based on the 
01486 // 1) current rate, 2) no of layers, 3) no of backoffs
01487 //
01488 // Jan 28, 99bufOptScen1(layer, layers, currrate, slope, backoffs)
01489 //
01490 double QA::bufOptScen2(int layer, int layers, double currrate, 
01491                double slope, int backoffs)
01492 {
01493     double bufopt = 0.0;
01494     int bmin, done;
01495 
01496     if(backoffs < 0) {
01497         panic("# ERROR: backoff: %d in bufOptScen2\n", 
01498             backoffs);
01499     }
01500     if ((currrate/pow(2,backoffs)) >= layers*LAYERBW_)
01501         return(0.0);
01502 
01503     bmin = 0;
01504     done = 0;
01505     while ((!done) && bmin<=backoffs) {
01506         if(currrate/pow(2,bmin) >= LAYERBW_*layers)
01507             bmin++;
01508         else 
01509             done++;
01510     }
01511     // buf required for the first triangle
01512     // we could have dec bmin and go for 1 backoff as well
01513     bufopt = bufOptScen1(layer, layers, currrate/pow(2,bmin), slope, 0);
01514   
01515     // remaining sequential backoffs
01516     bufopt += (backoffs - bmin)*BufNeed(layers*LAYERBW_/2, slope);
01517     return(bufopt);
01518 }
01519 
01520 
01521 //
01522 // This routine returns the optimal distribution of requested-to-drained
01523 // buffer across active layers based on:
01524 // 1) curr rate, 2) curr drain distr(FinalDrainArry), etc
01525 // NOTE, the caller must update FinalDrainArray from 
01526 // 
01527 // Jan 29, 99
01528 //
01529 // DrainArr: return value, used as an incremental chaneg for 
01530 //   FinalDrainArray
01531 // bufAvail:  current buffer_ state
01532 void QA::drain_buf(double* DrainArr, double bufToDrain, 
01533            double* FinalDrainArray, double* bufAvail, 
01534            int layers, double rate, double srtt)
01535 {
01536     double bufReq1, bufReq2, bufs1[MAX_LAYER], bufs2[MAX_LAYER], slope, 
01537         extra, targetArr[MAX_LAYER], maxDrainRemain;
01538     int bs1, bs2, l;
01539 
01540     slope = seg_size_/srtt;
01541     bs1 = MAXBKOFF_ + 1;
01542     bs2 = MAXBKOFF_ + 1;
01543     bufReq1 = bufReq2 = 0;
01544     for(l=0; l<layers; l++){
01545         bufReq1 += bufOptScen1(l, layers, rate, slope, bs1);
01546         bufReq2 += bufOptScen2(l, layers, rate, slope, bs2);
01547     } 
01548 
01549     for(l=0; l<MAX_LAYER; l++){
01550         bufs1[l] = 0;
01551         bufs2[l] = 0;
01552         DrainArr[l] = 0.0;
01553     }
01554 
01555     while(bufReq1 > TotalBuf(layers, bufAvail)){
01556         bufReq1 = 0;
01557         bs1--;
01558         for(l=0; l<layers; l++){
01559             bufs1[l] = bufOptScen1(l, layers, rate, slope, bs1);
01560             bufReq1 += bufs1[l];
01561         } 
01562     }
01563   
01564     while(bufReq2 > TotalBuf(layers, bufAvail)){
01565         bufReq2 = 0;
01566         bs2--;
01567         for(l=0; l<layers; l++){
01568             bufs2[l] =  bufOptScen2(l, layers, rate, slope, bs2);
01569             bufReq2 += bufs2[l];
01570         } 
01571     }
01572 
01573     if (bufReq1 >= bufReq2) {
01574         // drain toward last optimal scenario 1
01575         for (l=layers-1; l>=0; l--){
01576             // we try to drain the maximum amount from
01577             // min no of highest layers
01578             // note that there is a limit on total draining
01579             // from a layer
01580             maxDrainRemain = srtt*LAYERBW_ - FinalDrainArray[l];
01581             if ((bufAvail[l] > bufs1[l] + maxDrainRemain) &&
01582                 (bufToDrain >= maxDrainRemain)) {
01583                 DrainArr[l] = maxDrainRemain;
01584                 bufToDrain -= maxDrainRemain;
01585             } else {
01586                 if(bufAvail[l] > bufs1[l] + maxDrainRemain){
01587                     DrainArr[l] = bufToDrain;
01588                     bufToDrain = 0.0;
01589                 } else {
01590                     DrainArr[l] = bufAvail[l] - bufs1[l];
01591                     bufToDrain -= bufAvail[l] - bufs1[l];
01592 
01593                     /* for debug */
01594                     if(DrainArr[l] < 0.0){
01595 //      panic("# ERROR, DrainArr[%d]: %.2f, bufAvail: %.2f, bufs1: %.2f\n",
01596 //        l, DrainArr[l], bufAvail[l], bufs1[l]);
01597                         DrainArr[l] = 0.0;
01598                     }
01599                 }
01600             }
01601             if(bufToDrain == 0.0)
01602                 return;
01603         }
01604         return;
01605     } else {   /* if (bufReq1 >= bufReq2) */
01606 
01607         // Drain towards he last optima scenario 2 
01608         // We're draining - don't care about the upper bound on 
01609         // scenario 2.
01610         // Have to calculate all the layers together to get this max 
01611         // thing to work
01612         extra = 0.0;
01613         // Calculate the extra buffering 
01614         for (l=0; l<layers; l++) {
01615             if(bufs1[l] > bufs2[l])
01616                 extra += bufs1[l] - bufs2[l];
01617         }
01618 
01619         for (l=layers-1; l>=0; l--)
01620             if(bufs1[l] >= bufs2[l])
01621                 targetArr[l] = bufs1[l];
01622             else
01623                 if (bufs2[l] - bufs1[l] >= extra) {
01624                     targetArr[l] = bufs2[l] - extra;
01625                     extra = 0;
01626                 } else {
01627                     // there is enough extra to compenstae the dif
01628                     if (extra > 0) {
01629                         targetArr[l] = bufs2[l];
01630                         extra -= bufs2[l] - bufs1[l];
01631                     } else 
01632                         panic("# ERROR Should not \
01633 reach here, extra: %.2f, bufs2: %.2f, bufs1: %.2f, L%d\n", 
01634                         extra, bufs2[l], bufs1[l], l);
01635                 }
01636     } /* end of if (bufReq1 >= bufReq2) */
01637    
01638     // drain toward last optimal scenario 2
01639     for (l=layers-1; l>=0; l--) {
01640         // we try to drain the maximum amount from
01641         // min no of highest layers
01642         // note that there is a limit on total draining
01643         // from a layer
01644         maxDrainRemain = srtt*LAYERBW_ - FinalDrainArray[l];
01645         if ((bufAvail[l] > targetArr[l] + maxDrainRemain) &&
01646             (bufToDrain >= maxDrainRemain)) {
01647             DrainArr[l] = maxDrainRemain;
01648             bufToDrain -= maxDrainRemain;
01649         } else {
01650             if(bufAvail[l] > targetArr[l] + maxDrainRemain){
01651                 DrainArr[l] = bufToDrain;
01652                 bufToDrain = 0.0;
01653             } else {
01654                 DrainArr[l] = bufAvail[l] - targetArr[l];
01655                 bufToDrain -= bufAvail[l] - targetArr[l];
01656     
01657                 // for debug 
01658                 if (DrainArr[l] < 0.0) {
01659 //    panic("# ERROR, DrainArr[%d]: %.2f, bufAvail: %.2f, bufs1: %.2f\n",
01660 //      l, DrainArr[l], bufAvail[l], bufs1[l]);
01661                     DrainArr[l] = 0;
01662                 }
01663             }
01664         }
01665         if (bufToDrain == 0.0)
01666             return;
01667     } /* end of for */
01668     return;
01669 }
01670 
01671 
01672 //
01673 // This routine calculate an optimal distribution of a given 
01674 // amount of buffered data to drain.
01675 // the main algorithm is in drain_buf() and this one mainly init
01676 // the input and calls that routine ad then update FinalDrainArray,
01677 // based on its old value and return value for DrainArr.
01678 //
01679 // FinalDrainArray: output
01680 // FinalBuffer: output, expected buf state at the end of the interval
01681 void QA::DrainPacket(double bufToDrain, double* FinalDrainArray, int layers,
01682              double rate, double srtt, double* FinalBuffer)
01683 {
01684     double DrainArr[MAX_LAYER],  bufAvail[MAX_LAYER], TotBufAvail;
01685     int l,cnt;
01686   
01687     for(l=0; l<MAX_LAYER; l++){
01688         FinalDrainArray[l] = 0.0;
01689         bufAvail[l] = buffer_[l];
01690     }
01691 
01692     TotBufAvail = TotalBuf(layers, bufAvail);
01693     cnt = 0;
01694     while ((bufToDrain > 0) && (cnt < 10)) {
01695         // debug("bufToDrain%d: %.2f\n", cnt, bufToDrain);
01696         drain_buf(DrainArr, bufToDrain, FinalDrainArray, bufAvail, 
01697               layers, rate, srtt);
01698         
01699         for(l=0; l<layers; l++){
01700             bufToDrain -= DrainArr[l];
01701             TotBufAvail -= DrainArr[l];
01702             FinalDrainArray[l] += DrainArr[l];
01703             bufAvail[l] -= DrainArr[l];
01704             FinalBuffer[l] = buffer_[l] - FinalDrainArray[l];
01705         }
01706         cnt++;
01707     }
01708 }
01709 
01710 void QA::check_availability(int layer, const MediaSegment& s) 
01711 {
01712     int dummy; 
01713     MediaRequest p(MEDIAREQ_CHECKSEG);
01714     p.set_name(page_);
01715     p.set_layer(layer);
01716     p.set_st(s.start());
01717     p.set_et(s.end());
01718     p.set_app(this);
01719     // Ask cache/server to do prefetching if necessary.
01720     target()->get_data(dummy, &p);
01721 }
01722 
01723 /* 
01724  * This routine is called once every SRTT to drain some data from
01725  * recv's buffer and src's image from recv's buf.
01726  */
01727 void QA::DrainBuffers()
01728 {
01729     int i, j, layers = 0;
01730     Scheduler& s = Scheduler::instance();
01731     double now = s.clock();
01732     // interval since last drain
01733     double interval = now - playTime_;
01734     playTime_ = now;  // update playTime
01735 
01736     if ((layers > 1) && (playing_[0] != 1)) {
01737         panic("ERROR in DrainBuffer: layers>0 but L0 isn't playing\n");
01738     }
01739 
01740     // Updating playout offset, but do nothing if we are in the initial
01741     // startup filling phase! This offset measures the playing progress
01742     // of the client side. It is actually the playing offset of the lowest
01743     // layer.
01744 
01745     // This is the real amount of data to be drained from layers
01746     int todrain[MAX_LAYER]; 
01747     // Expected offset of base layer after draining, without considering 
01748     // holes in data. This has to be satisfied, otherwise base layer will
01749     // be dropped and an error condition will be raised.
01750     poffset_ += (int)floor(interval*LAYERBW_+0.5);
01751 
01752     // Started from MAX_LAYER to make debugging easier
01753     for (i = MAX_LAYER-1; i >= 0; i--) {
01754         // If this layer is not being played, don't drain anything
01755         if (sending_[i] == 0) {
01756             todrain[i] = 0;
01757             drained_[i] = 0.0;
01758             continue; 
01759         }
01760         todrain[i] = outlist_[i].evict_head_offset(poffset_);
01761         assert(todrain[i] >= 0);
01762         buffer_[i] -= todrain[i];
01763         // A buffer must have more than one byte
01764         if ((int)buffer_[i] <= 0) {
01765             debug("Buffer %d ran dry: %.2f after draining, DROP\n",
01766                   i, buffer_[i]);
01767             playing_[i] = 0;
01768             sending_[i] = 0;
01769             buffer_[i] = 0;
01770         
01771             /* Drop all higher layers if they still have data */
01772             for (j = i+1; j < MAX_LAYER; j++)
01773                 if (sending_[j] == 1) {
01774 /*
01775                     panic("# ERROR: layer %d \
01776 is playing with %.2f buf but layer %d ran dry with %.2f buf\n",
01777                           j, buffer_[j], i, buffer_[i]);
01778 */
01779                     debug("# DROP layer %d: it \
01780 is playing with %.2f buf but layer %d ran dry with %.2f buf\n",
01781                           j, buffer_[j], i, buffer_[i]);
01782                     sending_[j] = 0;
01783                     playing_[j] = 0;
01784                     buffer_[j] = 0;
01785                 }
01786             // We don't need to set it to -1. The old address 
01787             // will be used to see if we are sending old data if 
01788             // that later is added again
01789             //
01790             // XXX Where is this -1 mark ever used????
01791             // data_[i].set_start(-1); // drop layer i
01792         } else {
01793             // Prefetch for this layer. Round to whole segment
01794             int st = (int)floor((poffset_+pref_srtt_*LAYERBW_)
01795                         /seg_size_+0.5)*seg_size_;
01796             int et = (int)floor((poffset_+(pref_srtt_+interval)*
01797                        LAYERBW_)/seg_size_+0.5)*seg_size_;
01798             if (et > pref_[i]) {
01799                 pref_[i] = et;
01800                 MediaSegment s(st, et);
01801                 check_availability(i, s);
01802             }
01803         }
01804     } /* end of for */
01805 }
01806 
01807 // This routine dumps info into a file
01808 // format of each line is as follows:
01809 //  time tot-rate avg-rate per-layer-bw[MAXLAYER] tot-bw drain-rate[MAXLAYER] 
01810 //  & cumulative-buffer[MAXLAYER] & no-of-layers 
01811 // ADDED: use the old value of SRTT for bw/etc estimation !!! Jan 26
01812 // XXX: need to be more compressed add more hooks to for ctrling from  
01813 // tcl level
01814 void QA::DumpInfo(double t, double last_t, double rate, 
01815           double avgrate, double srtt)
01816 {
01817 #define MAXLEN 2000
01818     int i,j;
01819     char s1[MAXLEN], s2[MAXLEN], tmp[MAXLEN];
01820     static double last_srtt = 0, t1,t2 = 0;
01821 #undef MAXLEN
01822 
01823     double  tot_bw = 0.0, interval, diff;
01824 //  if(rate > 1000000.0){
01825 //      debug("WARNING rate: %f is too large\n", rate);
01826 //  }
01827     
01828     interval = t - last_t ;
01829   
01830     if((t2 != last_t) && (t2 > 0)){
01831         diff = interval - last_srtt;
01832         if ((diff > 0.001) || (diff < -0.001)) {
01833             if (last_t == 0) 
01834                 // Startup phase
01835                 return;
01836 /*
01837             debug("WARNING: last_srtt: %.4f != \
01838 interval: %.4f, diff: %f t1: %f, t2: %f, last_t: %f, t: %f\n",
01839                 last_srtt, interval, diff, t1, t2, last_t, t);
01840 */
01841             //abort();
01842         }
01843     } else 
01844         /* for the first call to init */
01845         last_srtt = srtt;
01846 
01847     t1 = last_t;
01848     t2 = t;
01849 
01850     if (interval <= 0.0) {
01851         panic("# ERROR interval is negative\n");
01852     }
01853 
01854     sprintf(s1, " %.2f %.2f %.2f X", last_t, rate, avgrate); 
01855     sprintf(s2, " %.2f %.2f %.2f X", t, rate, avgrate);
01856     
01857     j = 0;
01858     for (i = 0; i < MAX_LAYER; i++) 
01859       //if (playing_[i] == 1)
01860       if (sending_[i] == 1)
01861         j++;
01862     //no of layers being playback
01863     sprintf(tmp, " %d", j*LAYERBW_);
01864     strcat(s1, tmp);
01865     strcat(s2, tmp);
01866 
01867     for (i = 0; i < MAX_LAYER; i++) {
01868         sprintf(tmp, " %.2g ", (bw_[i]/interval)+i*10000.0);
01869         strcat(s1,tmp);
01870         strcat(s2,tmp);
01871 
01872         tot_bw += bw_[i]/interval;
01873         bw_[i] = 0;
01874     }
01875 
01876     sprintf(tmp, " %.2f X", tot_bw );
01877     strcat(s1,tmp);
01878     strcat(s2,tmp);
01879   
01880     j = 0;
01881     for (i = 0; i < MAX_LAYER; i++) {
01882       //if (playing_[i] == 1) {
01883       if(sending_[i] ==1){
01884         j++;
01885         // drained_[] can be neg when allocated buf for this 
01886         // layer is more than consumed data
01887         if (drained_[i] < 0.0) {
01888           // this means that this layer was drained 
01889           // with max rate
01890           drained_[i] = 0.0;
01891         } 
01892         // XXX, we could have used interval*LAYERBW_ - bw_[i]
01893         // that was certainly better
01894         sprintf(tmp, " %.2f ", 
01895             (drained_[i]/interval)+i*10000.0);
01896         strcat(s1,tmp);
01897         strcat(s2,tmp);
01898         // Note that drained[] shows the amount of data that 
01899         // is used from buffered data, i.e. rd[i]
01900         // This must be srtt instead of interval because this 
01901         // is for next dumping.
01902         drained_[i]=srtt*LAYERBW_;
01903       } else {
01904         sprintf(tmp, " %.2f ", i*10000.0);
01905         strcat(s1,tmp);
01906         strcat(s2,tmp);
01907         drained_[i] = 0.0;
01908       }
01909     }
01910 
01911     for (i=0;i<MAX_LAYER;i++) {
01912         sprintf(tmp, " %.2f", buffer_[i]+i*10000);
01913         strcat(s1,tmp);
01914         strcat(s2,tmp);
01915     }
01916     
01917     log("QA %s \n", s1);
01918     log("QA %s \n", s2);
01919     fflush(stdout);
01920 }
01921 
01922 // This routine models draining of buffers at the recv
01923 // it periodically updates state of buffers
01924 // Ir must be called once and then it reschedules itself
01925 // it is first called after playout is started!
01926 void QA::UpdateState()
01927 {
01928     double last_ptime = playTime_; // Last time to drain buffer
01929     DrainBuffers();
01930     DumpInfo(Scheduler::instance().clock(), last_ptime, 
01931          rate(), avgrate_, rap()->srtt());
01932 }
01933 
01934 

Generated on Tue Mar 6 16:47:47 2007 for ns2 Network Simulator 2.29 by  doxygen 1.4.6