def checkBuffering(self, _arg): #exactly what we are doing?!?!?!? ''' Checks if the playback is going to buffering. Estimates the time required to complete the download of the current segment and verifies that it is less than the playout buffer lenght. In the case of "warning buffering", it deletes the current segment download, calculates the control action and sets the new level. This feature is available only with persistent connection. ''' #FIXME Can't use it without persistent connection if self.rate_calc.rate and self.cur_index > self.inactive_cycle: remaining_secs = float(self.remaining_data/self.rate_calc.rate) debug(DEBUG+1,"%s checkBuffering: rate %s/s, remaining_data %s, remaining_secs %.3f, queued_time %.2f ", self, format_bytes(self.rate_calc.rate), format_bytes(self.remaining_data), remaining_secs, self.media_engine.getQueuedTime()) #Can cancel download only if the current level is greater than 0 if self.media_engine.getQueuedTime() < remaining_secs and self.getCurrentLevel() > 0: self.connection.stop() self.stop_segment_request = time.time() #update stop reqest when warning buffering occurs self.bwe = self.rate_calc.rate #Passing player parameters at the controller to calculate the control action self.updateFeedback(flag_check_buffering=True) #calc control action self.controller.setControlAction(self.controller.calcControlAction()) # set new level if self.cur_index > self.inactive_cycle: self.setLevel(self.controller.getControlAction()) debug(0,"%s WARNING BUFFERING!!! Delete and reload segment at level: %d", self, self.getCurrentLevel()) else: return
def fetchNextSegment(self,calledWithPort): ''' Schedules the download of the next segment at current level ''' #print ("MY PID IS "+str(os.getpid())) test=calledWithPort; playlist = self.parser.playlists[self.getCurrentLevel()] debug(DEBUG+1, '%s fetchNextSegment level: %d cur_index: %d', self, self.getCurrentLevel(), self.getCurrentSegmentIndex()) # if self.getCurrentSegmentIndex() < playlist['start_index']: self.setCurrentSegmentIndex(playlist['start_index']) if self.getCurrentSegmentIndex() > playlist['end_index']: # else live video (ONLY HLS!!) if playlist['is_live'] and self.parser.getPlaylistType() == 'HLS': debug(DEBUG, '%s fetchNextSegment cur_index %d', self, self.getCurrentSegmentIndex()) self.parser.updateLevelSegmentsList(self.getCurrentLevel()).addCallback(self._updatePlaylistDone) # if video is vod else: debug(DEBUG, '%s fetchNextSegment last index', self) return cur_index=self.getCurrentSegmentIndex() levels = self.parser.getLevels() url_segment = playlist['segments'][cur_index]['url'] byterange = playlist['segments'][cur_index]['byterange'] if byterange != '': debug(DEBUG, '%s fetchNextSegment level: %d (%s/s) %d/%d : %s (byterange=%s)', self, self.getCurrentLevel(), format_bytes(float(levels[self.getCurrentLevel()]['rate'])), self.getCurrentSegmentIndex(), playlist['end_index'], url_segment, byterange) else: debug(DEBUG, '%s fetchNextSegment level: %d (%s/s) %d/%d : %s', self, self.getCurrentLevel(), format_bytes(float(levels[self.getCurrentLevel()]['rate'])), self.getCurrentSegmentIndex(), playlist['end_index'], url_segment) if self.controller.isBuffering(): idle_duration = 0.0 #fetch segment after the last segment download is completed print colored('GETTING AN IDLE DURATION OF '+str(idle_duration),'green') else: #FIXME #uncomment idle_duration again! #idle_duration =0.0 idle_duration = self.controller.getIdleDuration() print colored('GETTING AN IDLE DURATION OF '+str(idle_duration),'green') # load the next segment #if calledWithPort: # if calledWithPort==getLocalport(): # reactor.callLater(idle_duration, self.startDownload, url_segment, byterange) # print("port seen from fetchNextSegment= "+str(getLocalport())) # print('called with port was '+ str(getLocalport())) # else: # print('RACE CONDITION!!!!!!') # print("port seen from fetchNextSegment= "+str(getLocalport())) # return #else: # print('fetchNextSegment was not called from ZMQ') print("calling start download with test= "+str(test)) reactor.callLater(idle_duration, self.startDownload, url_segment, byterange)
def _onDataReceiving(self, connection, data_diff, remaining_data): ''' Does something before segment download is completed (used with persistent connection) ''' self.remaining_data = remaining_data debug(DEBUG+1, '%s _onDataReceiving: %s %s', self, format_bytes(data_diff), format_bytes(remaining_data)) self.rate_calc.update(data_diff)
def calcControlAction(self): video_rates = self.feedback['rates'] T_tilde = self.feedback['last_download_time'] tau = self.feedback['fragment_duration'] x_tilde = self.feedback['cur_rate'] * self.feedback[ 'fragment_duration'] / T_tilde #bandwidth estimate sample on the last downloaded segment T_hat_old = self.getIdleDuration() #Actual inter-request time T = max(T_hat_old, T_tilde) self.T_old = T k = self.k w = self.w #in B/s beta = self.beta x_hat = max( 0, self.x_hat_last + T * k * (w - max(0, self.x_hat_last - x_tilde))) self.x_hat_last = x_hat control_action = self.__ewma_filter(x_hat) r = self.quantizeSpecialRate(control_action) B = self.feedback['queued_time'] T_hat = r * float(tau) / control_action + beta * (float(B) - float(self.Q)) self.setIdleDuration(T_hat - T_tilde) debug( DEBUG, "%s calcControlAction: ca: %s/s r: %s/s x_tilde: %s/s x_hat: %s/s T_tilde: %.2f T_hat: %.2f T: %.2f", self, format_bytes(control_action), format_bytes(r), format_bytes(x_tilde), format_bytes(x_hat), T_tilde, T_hat, T) return r
def playNextGotRequest(self, data, factory): ''' Updates feedbacks, calculates the control action and sets level of the next segment. :param data: downloaded data :param factory: the twisted factory (used without persistent connection) ''' self.stop_segment_request = time.time() download_time = (self.stop_segment_request - self.start_segment_request) self.last_downloaded_time = download_time self.bwe = len(data)/download_time self.last_fragment_size = len(data) self.downloaded_bytes += len(data) self.downloaded_segments += 1 debug(DEBUG, '%s __got_request: bwe: %s/s (fragment size: %s)', self, format_bytes(self.bwe), format_bytes(len(data))) self.queuedTime = self.media_engine.getQueuedTime() + self.parser.getFragmentDuration() self.queuedBytes = self.media_engine.getQueuedBytes() + len(data) self.media_engine.pushData(data, self.parser.getFragmentDuration(), self.getCurrentLevel(), self.parser._getCapsDemuxer()) del data self.cur_index += 1 #Do something before calculating new control action self._onNewSegment() #Passing player parameters at the controller to calculate the control action self.updateFeedback(flag_check_buffering=False) #calc control action self.controller.setControlAction(self.controller.calcControlAction()) # set new level if self.getDownloadedSegments() > self.getInactiveCycles(): if self.enable_stress_test: self.stressTest() else: self.setLevel(self.controller.getControlAction()) self.fetchNextSegment()
def checkBuffering(self, _arg): ''' Checks if the playback is going to buffering. Estimates the time required to complete the download of the current segment and verifies that it is less than the playout buffer lenght. In the case of "warning buffering", it deletes the current segment download, calculates the control action and sets the new level. This feature is available only with persistent connection. ''' #FIXME Can't use it without persistent connection if self.rate_calc.rate and self.cur_index > self.inactive_cycle: remaining_secs = float(self.remaining_data/self.rate_calc.rate) debug(DEBUG+1,"%s checkBuffering: rate %s/s, remaining_data %s, remaining_secs %.3f, queued_time %.2f ", self, format_bytes(self.rate_calc.rate), format_bytes(self.remaining_data), remaining_secs, self.media_engine.getQueuedTime()) #Can cancel download only if the current level is greater than 0 if self.media_engine.getQueuedTime() < remaining_secs and self.getCurrentLevel() > 0: self.connection.stop() self.stop_segment_request = time.time() #update stop reqest when warning buffering occurs self.bwe = self.rate_calc.rate #Passing player parameters at the controller to calculate the control action self.updateFeedback(flag_check_buffering=True) #calc control action self.controller.setControlAction(self.controller.calcControlAction()) # set new level if self.cur_index > self.inactive_cycle: self.setLevel(self.controller.getControlAction()) debug(0,"%s WARNING BUFFERING!!! Delete and reload segment at level: %d", self, self.getCurrentLevel()) else: return
def fetchNextSegment(self): ''' Schedules the download of the next segment at current level ''' playlist = self.parser.playlists[self.getCurrentLevel()] debug(DEBUG + 1, '%s fetchNextSegment level: %d cur_index: %d', self, self.getCurrentLevel(), self.getCurrentSegmentIndex()) # if self.getCurrentSegmentIndex() < playlist['start_index']: self.setCurrentSegmentIndex(playlist['start_index']) if self.getCurrentSegmentIndex() > playlist['end_index']: # else live video (ONLY HLS!!) if playlist['is_live'] and self.parser.getPlaylistType() == 'HLS': debug(DEBUG, '%s fetchNextSegment cur_index %d', self, self.getCurrentSegmentIndex()) self.parser.updateLevelSegmentsList( self.getCurrentLevel()).addCallback( self._updatePlaylistDone) # if video is vod else: debug(DEBUG, '%s fetchNextSegment last index', self) self.terminated = True #process = subprocess.Popen(os.getpid()) #process.send_signal(signal.SIGINT) #sys.exit() return cur_index = self.getCurrentSegmentIndex() levels = self.parser.getLevels() url_segment = playlist['segments'][cur_index]['url'] byterange = playlist['segments'][cur_index]['byterange'] if byterange != '': debug( DEBUG, '%s fetchNextSegment level: %d (%s/s) %d/%d : %s (byterange=%s)', self, self.getCurrentLevel(), format_bytes(float(levels[self.getCurrentLevel()]['rate'])), self.getCurrentSegmentIndex(), playlist['end_index'], url_segment, byterange) else: debug(DEBUG, '%s fetchNextSegment level: %d (%s/s) %d/%d : %s', self, self.getCurrentLevel(), format_bytes(float(levels[self.getCurrentLevel()]['rate'])), self.getCurrentSegmentIndex(), playlist['end_index'], url_segment) if self.controller.feedback['queued_time'] < self.controller.feedback[ 'max_buffer_time']: idle_duration = 0.0 #fetch segment after the last segment download is completed else: idle_duration = max( 0.0, self.controller.feedback['queued_time'] - self.controller.feedback['max_buffer_time'] - self.controller.feedback['last_download_time']) # if bandwidth is varried per segment set new bandwidth if self.mu is not None and self.sigma is not None: self.setNewBandwidth() # load the next segment reactor.callLater(idle_duration, self.startDownload, url_segment, cur_index, byterange)
def calcControlAction(self): T = self.feedback['last_download_time'] cur = self.feedback['cur_rate'] tau = self.feedback['fragment_duration'] x = cur * tau / T y = self.__ewma_filter(x) self.setIdleDuration(tau-T) debug(DEBUG, "%s calcControlAction: y: %s/s x: %s/s T: %.2f", self, format_bytes(y), format_bytes(x), T) return y
def fetchNextSegment(self): ''' Schedules the download of the next segment at current level ''' playlist = self.parser.playlists[self.getCurrentLevel()] debug(DEBUG + 1, '%s fetchNextSegment level: %d cur_index: %d', self, self.getCurrentLevel(), self.getCurrentSegmentIndex()) # if self.getCurrentSegmentIndex() < playlist['start_index']: self.setCurrentSegmentIndex(playlist['start_index']) if self.getCurrentSegmentIndex() > playlist['end_index']: # else live video (ONLY HLS!!) if playlist['is_live'] and self.parser.getPlaylistType() == 'HLS': debug(DEBUG, '%s fetchNextSegment cur_index %d', self, self.getCurrentSegmentIndex()) self.parser.updateLevelSegmentsList( self.getCurrentLevel()).addCallback( self._updatePlaylistDone) # if video is vod else: debug(DEBUG, '%s fetchNextSegment last index', self) return cur_index = self.getCurrentSegmentIndex() levels = self.parser.getLevels() url_segment = playlist['segments'][cur_index]['url'] byterange = playlist['segments'][cur_index]['byterange'] if byterange != '': debug( DEBUG, '%s fetchNextSegment level: %d (%s/s) %d/%d : %s (byterange=%s)', self, self.getCurrentLevel(), format_bytes(float(levels[self.getCurrentLevel()]['rate'])), self.getCurrentSegmentIndex(), playlist['end_index'], url_segment, byterange) else: debug(DEBUG, '%s fetchNextSegment level: %d (%s/s) %d/%d : %s', self, self.getCurrentLevel(), format_bytes(float(levels[self.getCurrentLevel()]['rate'])), self.getCurrentSegmentIndex(), playlist['end_index'], url_segment) if self.controller.isBuffering(): idle_duration = 0.0 #fetch segment after the last segment download is completed else: idle_duration = self.controller.getIdleDuration() # load the next segment reactor.callLater(idle_duration, self.startDownload, url_segment, byterange)
def pushData(self, data, fragment_duration, level, caps_data): debug(DEBUG, '%s pushData: pushed %s of data for level %s', self, format_bytes(len(data)), level) self.queue['byte'] += len(data) self.queue['sec'] += fragment_duration self.pushed_segments.append( dict(len_segment=len(data), dur_segment=fragment_duration))
def pushData(self, data, fragment_duration, level, caps_data): debug(DEBUG, '%s pushData: pushed %s of data for level %s', self, format_bytes(len(data)), level) self.queue['byte']+=len(data) self.queue['sec']+=fragment_duration self.pushed_segments.append(dict(len_segment=len(data),dur_segment=fragment_duration))
def __harmonic_mean(v): '''Computes the harmonic mean of vector v''' x = numpy.array(v) debug(DEBUG+1, "%s __harmonic_mean: Bwe vect: %s", self, str(x)) m = 1.0/(sum(1.0/x)/len(x)) debug(DEBUG, "%s__harmonic_mean: Harmonic mean: %s/s", self, format_bytes(m)) return m
def _onDataReceived(self, connection, data): ''' Does something when segment download is completed (used with persistent connection) ''' debug(DEBUG+1, '%s _onDataReceived: %s', self, format_bytes(len(data))) self.playNextGotRequest(data, None)
def pushData(self, data, fragment_duration, level, view, caps_data): debug(DEBUG, '%s pushData: pushed %s of data for level %s', self, format_bytes(len(data)), level) self.evicted_segment = 0 self.checkGstbuffer() # check view change for playout buffer eviction if (view != self.current_view): # check if gstreamer has enough data (for limiting rebuffering events) self.checkGstbuffer() # flush playout buffer byte = 0 sec = 0 self.evicted_segment = len(self.pushed_segments) for segment in self.pushed_segments: byte += segment["len_segment"] sec += segment["dur_segment"] self.queue['byte'] -= byte self.queue['sec'] -= sec del self.pushed_segments self.pushed_segments = [] self.playtime -= sec # set new current view self.current_view = view # fill playout buffer self.queue['byte'] +=len(data) self.queue['sec'] +=fragment_duration self.pushed_segments.append(dict(len_segment=len(data), dur_segment=fragment_duration, data=data))
def on_data_received(self, connection, data, level): debug(DEBUG+1, '%s on_data_received: %s', self, format_bytes(len(data))) self.fd[level] = StringIO.StringIO(data) #print "level", level self.playlists[level]["header_data"]=data self.parse_atom(0,len(data)-1, level) self.deferredList[level].callback(1) self.connection_list[level].stop()
def pushData(self, data, fragment_duration, level, caps_data): buf = gst.Buffer(data) buf.duration = long(fragment_duration * 1e9) debug(DEBUG, '%s pushData: pushed %s of data (duration= %.2fs) for level %s', self, format_bytes(len(data)), fragment_duration, level) self.pipeline.get_by_name('src').emit('push-buffer', buf) del buf
def playNextGotRequest(self, data, factory): ''' Updates feedbacks, calculates the control action and sets level of the next segment. :param data: downloaded data :param factory: the twisted factory (used without persistent connection) ''' self.stop_segment_request = time.time() download_time = (self.stop_segment_request - self.start_segment_request) self.last_downloaded_time = download_time self.bwe = len(data) / download_time self.last_fragment_size = len(data) self.downloaded_bytes += len(data) self.downloaded_segments += 1 real_seg_dur = self.parser.getRealSegmentDuration(self.cur_index) debug(DEBUG, '%s __got_request: bwe: %s/s (fragment size: %s)', self, format_bytes(self.bwe), format_bytes(len(data))) playlist = self.parser.playlists[self.getCurrentLevel()] #self.queuedTime = self.media_engine.getQueuedTime() + self.parser.getFragmentDuration() self.queuedTime = self.media_engine.getQueuedTime( ) + self.parser.getRealSegmentDuration(self.cur_index) self.queuedBytes = self.media_engine.getQueuedBytes() + len(data) self.media_engine.pushData( data, self.parser.getRealSegmentDuration(self.cur_index), self.getCurrentLevel(), self.parser._getCapsDemuxer()) del data if self.getCurrentSegmentIndex() <= playlist['end_index']: self.setCurrentSegmentDuration( playlist['segments'][self.getCurrentSegmentIndex()]['dur']) #Do something before calculating new control action self._onNewSegment() #Passing player parameters at the controller to calculate the control action self.updateFeedback(flag_check_buffering=False) self.cur_index += 1 #calc control action self.controller.setControlAction(self.controller.calcControlAction()) # set new level if self.getDownloadedSegments() > self.getInactiveCycles(): if self.enable_stress_test: self.stressTest() else: self.setLevel(self.controller.getControlAction()) self.fetchNextSegment()
def pushData(self, data, fragment_duration, level, caps_data): buf = gst.Buffer(data) buf.duration = long(fragment_duration*1e9) debug(DEBUG, '%s pushData: pushed %s of data (duration= %ds) for level %s', self, format_bytes(len(data)), fragment_duration, level) self.pipeline.get_by_name('src').emit('push-buffer', buf) del buf
def quantizeRate(self,rate): video_rates = self.feedback['rates'] cur = self.feedback['cur_rate'] level = self.feedback['level'] D_up = self.eps*rate D_down = 0 r_up = self.__levelLessThanRate(rate - D_up) r_down = self.__levelLessThanRate(rate - D_down) new_level = 0 if level < r_up: new_level = r_up elif r_up <= level and level <= r_down: new_level = level else: new_level = r_down debug(DEBUG, "%s quantizeRate: rate: %s/s cur: %s/s D_up: %s/s D_down: %s/s r_up: %d r_down: %d new_level: %d", self, format_bytes(rate), format_bytes(cur), format_bytes(D_up), format_bytes(D_down), r_up, r_down, new_level) debug(DEBUG, "%s quantizeRate: rates: %s", self, video_rates) return new_level
def fetchNextSegment(self): ''' Schedules the download of the next segment at current level ''' playlist = self.parser.playlists[self.getCurrentLevel()] debug(DEBUG+1, '%s fetchNextSegment level: %d cur_index: %d', self, self.getCurrentLevel(), self.getCurrentSegmentIndex()) # if self.getCurrentSegmentIndex() < playlist['start_index']: self.setCurrentSegmentIndex(playlist['start_index']) if self.getCurrentSegmentIndex() > playlist['end_index']: # else live video (ONLY HLS!!) if playlist['is_live'] and self.parser.getPlaylistType() == 'HLS': debug(DEBUG, '%s fetchNextSegment cur_index %d', self, self.getCurrentSegmentIndex()) self.parser.updateLevelSegmentsList(self.getCurrentLevel()).addCallback(self._updatePlaylistDone) # if video is vod else: debug(DEBUG, '%s fetchNextSegment last index', self) return cur_index=self.getCurrentSegmentIndex() levels = self.parser.getLevels() url_segment = playlist['segments'][cur_index]['url'] byterange = playlist['segments'][cur_index]['byterange'] if byterange != '': debug(DEBUG, '%s fetchNextSegment level: %d (%s/s) %d/%d : %s (byterange=%s)', self, self.getCurrentLevel(), format_bytes(float(levels[self.getCurrentLevel()]['rate'])), self.getCurrentSegmentIndex(), playlist['end_index'], url_segment, byterange) else: debug(DEBUG, '%s fetchNextSegment level: %d (%s/s) %d/%d : %s', self, self.getCurrentLevel(), format_bytes(float(levels[self.getCurrentLevel()]['rate'])), self.getCurrentSegmentIndex(), playlist['end_index'], url_segment) if self.controller.isBuffering(): idle_duration = 0.0 #fetch segment after the last segment download is completed else: idle_duration = self.controller.getIdleDuration() # load the next segment reactor.callLater(idle_duration, self.startDownload, url_segment, byterange)
def playNextGotRequest(self, data, factory): ''' Updates feedbacks, calculates the control action and sets level of the next segment. :param data: downloaded data :param factory: the twisted factory (used without persistent connection) ''' self.stop_segment_request = time.time() download_time = (self.stop_segment_request - self.start_segment_request) print("DOWNLOAD COMPLETED AFTER "+str(download_time)+" SECONDS") self.last_downloaded_time = download_time self.bwe = len(data)/download_time self.last_fragment_size = len(data) self.downloaded_bytes += len(data) self.downloaded_segments += 1 debug(DEBUG, '%s __got_request: bwe: %s/s (fragment size: %s)', self, format_bytes(self.bwe), format_bytes(len(data))) self.queuedTime = self.media_engine.getQueuedTime() + self.parser.getFragmentDuration() self.queuedBytes = self.media_engine.getQueuedBytes() + len(data) self.media_engine.pushData(data, self.parser.getFragmentDuration(), self.getCurrentLevel(), self.parser._getCapsDemuxer()) del data self.cur_index += 1 #Do something before calculating new control action self._onNewSegment() #Passing player parameters at the controller to calculate the control action self.updateFeedback(flag_check_buffering=False) #calc control action self.controller.setControlAction(self.controller.calcControlAction()) # set new level if self.getDownloadedSegments() > self.getInactiveCycles(): if self.enable_stress_test: self.stressTest() else: self.setLevel(self.controller.getControlAction()) print colored("[playNextGotRequest()] I called makeRequest() [1]",'red'); self.makeRequest()
def __evalRefLevel(self, bwe_filt): cur = self.feedback['cur_rate'] cur_idx = self.feedback['level'] new_idx = cur_idx video_rates = self.feedback['rates'] debug(DEBUG, "%s __evalRefLevel: vrates: %s", self, str(video_rates)) if cur > 0.85*bwe_filt: new_idx = cur_idx - 1 else: for i in range(0,len(video_rates) - 1): if bwe_filt >= video_rates[i]: debug(DEBUG, "%s __evalRefLevel: Bwe: %s/s vrate: %s/s", self, format_bytes(bwe_filt), format_bytes(video_rates[i])) new_idx = i new_idx = min(max(new_idx,0), len(video_rates) -1) debug(DEBUG, "%s __evalRefLevel: Cur_idx: %d new_idx: %d", self, cur_idx, new_idx) return video_rates[new_idx]
def pushData(self, data, fragment_duration, level, caps_data): try: #f= open("{}.mp4".format(self.ind),"w+") #f.write(data) #self.ind += 1 #f.close() buf = gst.Buffer(data) buf.duration = long(fragment_duration * 1e9) #print(">>>>>>>>>>>>> Push data", level, fragment_duration, len(data), len(buf)) debug( DEBUG, '%s pushData: pushed %s of data (duration= %ds) for level %s', self, format_bytes(len(data)), fragment_duration, level) self.pipeline.get_by_name('src').emit('push-buffer', buf) del buf except Exception, e: print(">>>>>>>>>>>> EXCEPTION: " + str(e)) sys.exit(1) #HARD, better to do return
def quantizeSpecialRate(self, rate): video_rates = self.feedback['rates'] cur = self.feedback['cur_rate'] #D_up = self.w + self.eps*rate #D_down = self.w D_up = self.eps * rate D_down = 0 r_up = video_rates[self.__levelLessThanRate(rate - D_up)] r_down = video_rates[self.__levelLessThanRate(rate - D_down)] new_level = 0 if cur < r_up: new_level = r_up elif r_up <= cur and cur <= r_down: new_level = cur else: new_level = r_down debug( DEBUG, "%s quantizeRate: rate: %s/s D_up: %s/s D_down: %s/s r_up: %s/s r_down: %s/s new_level_rate: %s/s", self, format_bytes(rate), format_bytes(D_up), format_bytes(D_down), format_bytes(r_up), format_bytes(r_down), format_bytes(new_level)) debug(DEBUG, "%s quantizeRate: rates: %s", self, video_rates) return new_level
def playNextGotRequest(self, data, factory): ''' Updates feedbacks, calculates the control action and sets level of the next segment. :param data: downloaded data :param factory: the twisted factory (used without persistent connection) ''' self.stop_segment_request = time.time() download_time = (self.stop_segment_request - self.start_segment_request) self.last_downloaded_time = download_time self.bwe = len(data) / download_time self.last_fragment_size = len(data) self.downloaded_bytes += len(data) self.downloaded_segments += 1 debug(DEBUG, '%s __got_request: bwe: %s/s (fragment size: %s)', self, format_bytes(self.bwe), format_bytes(len(data))) self.queuedTime = self.media_engine.getQueuedTime( ) + self.parser.getFragmentDuration() #print ">>>>>>>>>>>>>>>>>>>>>>>>>> TAPAS QUEUE TIME: " + str(self.queuedTime) self.queuedBytes = self.media_engine.getQueuedBytes() + len(data) playlist = self.parser.getSinglePlaylist(self.getCurrentLevel(), self.getCurrentView()) if ("initURL" in playlist): #download init segment (DASH BASED CODING) if ("initSegment" in playlist): #push next segment print "THERE'S INIT" print "LEVEL " + str(self.cur_level) + "\nURL: " + str( playlist["initURL"]) + "\nINDEX: " + str(self.cur_index) self.media_engine.pushData(playlist["initSegment"] + data, self.parser.getFragmentDuration(), self.getCurrentLevel(), self.parser._getCapsDemuxer()) #if (self.cur_index == 0): #print "INIT + DATA" #self.media_engine.pushData(playlist["initSegment"] + data, self.parser.getFragmentDuration(), self.getCurrentLevel(), self.parser._getCapsDemuxer()) #else: #print "ONLY DATA" #self.media_engine.pushData(data, self.parser.getFragmentDuration(), self.getCurrentLevel(), self.parser._getCapsDemuxer()) else: # ERROR print ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ERROR <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" else: #push next segment print "THERE ISN'T INIT" self.media_engine.pushData(data, self.parser.getFragmentDuration(), self.getCurrentLevel(), self.parser._getCapsDemuxer()) #self.media_engine.pushData(data, self.parser.getFragmentDuration(), self.getCurrentLevel(), self.parser._getCapsDemuxer()) del data self.cur_index += 1 #Do something before calculating new control action self._onNewSegment() #Passing player parameters at the controller to calculate the control action self.updateFeedback(flag_check_buffering=False) #calc control action self.controller.setControlAction(self.controller.calcControlAction()) # set new level if self.getDownloadedSegments() > self.getInactiveCycles(): if (self.getNumViews() > 1): self.setView() if self.enable_stress_test: self.stressTest() else: self.setLevel(self.controller.getControlAction()) self.fetchNextSegment()
def calcControlAction(self): if self.maliciousFlag == 1: self.setIdleDuration(0.0) y = float(self.feedback['max_rate']*1.5) debug(DEBUG, "MALICIOUS USER!!!") debug(DEBUG, "%s calcControlAction: y: %s/s", self, format_bytes(y)) return y else: self.bwe_vec.add(self.feedback['bwe']) def __harmonic_mean(v): '''Computes the harmonic mean of vector v''' #x = numarray.array(v) x = numpy.array(v) debug(DEBUG+2, "%s __harmonic_mean: Bwe vect: %s", self, str(x)) m = 1.0/(sum(1.0/x)/len(x)) debug(DEBUG, "%s__harmonic_mean: Harmonic mean: %s/s", self, format_bytes(m)) return m self.bwe_filt = __harmonic_mean(self.bwe_vec.getBuffer()) e = self.__getError() if e == 0: #this "if" sets the flag zero_int_err to nullify the integral error when entering q > q_H or q < q_L self.prec_state = 1 zero_int_error = 1 elif e > 0 and self.prec_state == 0: self.prec_state = 2 zero_int_error = 1 elif e < 0 and self.prec_state == 2: self.prec_state = 0 zero_int_error = 1 else: zero_int_error = 0 max_rate = self.feedback['max_rate'] min_rate = self.feedback['min_rate'] d = self.feedback['player_status'] if self.t_last < 0: delta_t = 0 self.int_error = e else: delta_t = time.time() - self.t_last if zero_int_error == 1: #flag to nullify the integral error when entering q > q_H or q < q_L #DEBUG && log('Force integral error == 0'); self.int_error = 0 self.int_error += delta_t * e self.t_last = time.time() q = self.feedback['queued_time'] if q < self.__getQueueLowWM() or q > self.__getQueueHighWM() or self.feedback['is_check_buffering']: '''The control law is b / ( 1 - k1 e - k2 ei)''' den = 1 - self.k1*e - self.k2*self.int_error if self.feedback['is_check_buffering']: bwe = self.feedback['bwe'] else: bwe = self.bwe_filt u = bwe/den if den <= 0 or u >= max_rate: u = max_rate + 2000 # Make sure that the maximum rate can be selected debug(DEBUG, '%s calcControlAction: Max rate reached. Anti windup active',self) self.__resetIntegralError(delta_t * e) elif u <= min_rate: u = min_rate self.__resetIntegralError(delta_t * e) debug(DEBUG, '%s calcControlAction: Min rate reached. Anti windup active',self) else: u = self.feedback['cur_rate'] level_u = self.quantizeRate(u) if q > self.__getQueueHighWM() and level_u < self.last_level: u = self.feedback['cur_rate'] level_u = self.last_level debug(DEBUG, '%s Prevented switch down when q > q_H',self) self.last_level = level_u debug(DEBUG, '%s calcControlAction: u: %s/s q: %.2f e: %.2f int_err: %.2f delta_t: %.2f level_u: %d', self, format_bytes(u), q, e, self.int_error, delta_t, level_u) return u
def calcControlAction(self): self.iteration += 1 self.bwe_vec.add(self.feedback['bwe']) level = self.feedback['cur_rate'] def __harmonic_mean(v): '''Computes the harmonic mean of vector v''' x = numpy.array(v) debug(DEBUG+1, "%s __harmonic_mean: Bwe vect: %s", self, str(x)) m = 1.0/(sum(1.0/x)/len(x)) debug(DEBUG, "%s__harmonic_mean: Harmonic mean: %s/s", self, format_bytes(m)) return m w = __harmonic_mean(self.bwe_vec.getBuffer()) debug(DEBUG, "%s calcControlAction: iteration %d", self, self.iteration) if self.iteration >= self.horizon: q = self.feedback['queued_time'] delta = self.feedback['fragment_duration'] randbuf = random.uniform(self.target_buf - delta, self.target_buf + delta) b_ref = self.__evalRefLevel(w) b_cur = self.feedback['cur_rate'] n = sum(self.switches.getBuffer()) debug(DEBUG, "%s q: %.2f randbuf: %.2f b_ref: %.2f b_cur: %s/s n: %d", self, q, randbuf, b_ref, format_bytes(b_cur), n) if self.__evalScore(b_ref, b_ref, w, n) < self.__evalScore(b_cur, b_ref, w,n): self.switches.add(1) level = b_ref + 1000.0 else: self.switches.add(0) level = b_cur + 1000.0 #Randomized scheduling if q < randbuf: self.setIdleDuration(0.0) else: self.setIdleDuration(q - randbuf) return level
def fetchNextSegment(self): ''' Schedules the download of the next segment at current level and view ''' playlist = self.parser.getSinglePlaylist(self.getCurrentLevel(), self.getCurrentView()) debug( DEBUG + 1, '%s fetchNextSegment level: %d angles: %d view: %d cur_index: %d', self.getCurrentLevel(), self.getCurrentAngles()[0], self.getCurrentView(), self.getCurrentSegmentIndex()) # #print str("self.getCurrentSegmentIndex = {}, playlist = {}".format(self.getCurrentSegmentIndex(), playlist['start_index'])) if self.getCurrentSegmentIndex() < playlist['start_index']: self.setCurrentSegmentIndex(playlist['start_index']) if self.getCurrentSegmentIndex() > playlist['end_index']: # else live video (ONLY HLS!!) if playlist['is_live'] and self.parser.getPlaylistType() == 'HLS': print("playlist['is_live'] :" + str(playlist['is_live'])) debug(DEBUG, '%s fetchNextSegment cur_index %d', self, self.getCurrentSegmentIndex()) self.parser.updateLevelSegmentsList( self.getCurrentLevel(), self.getCurrentView()).addCallback( self._updatePlaylistDone) # if video is vod else: debug(DEBUG, '%s fetchNextSegment last index', self) # set max video duration for stopping media_engine self.media_engine.setVideoDuration(playlist['duration']) #print("STOP PLAYING.") return cur_index = self.getCurrentSegmentIndex() levels = self.parser.getLevels() url_segment = playlist['segments'][cur_index]['url'] byterange = playlist['segments'][cur_index]['byterange'] if byterange != '': debug( DEBUG, '%s fetchNextSegment level: %d (%s/s) %d/%d : %s (byterange=%s) angles:%d view: %d', self, self.getCurrentLevel(), format_bytes(float(levels[self.getCurrentLevel()]['rate'])), self.getCurrentSegmentIndex(), playlist['end_index'], url_segment, byterange, self.getCurrentAngles(), self.getCurrentView()) else: debug( DEBUG, '%s fetchNextSegment level: %d (%s/s) %d/%d angles:%d view %d: %s', self, self.getCurrentLevel(), format_bytes(float(levels[self.getCurrentLevel()]['rate'])), self.getCurrentSegmentIndex(), playlist['end_index'], self.getCurrentAngles()[0], self.getCurrentView(), url_segment) if self.controller.isBuffering(): idle_duration = 0.0 #fetch segment after the last segment download is completed else: idle_duration = self.controller.getIdleDuration() # load the next segment if self.last_redirect_host: host, port, path = parse_url(url_segment) url_segment = 'http://' + self.last_redirect_host + path reactor.callLater(idle_duration, self.startDownload, url_segment, byterange)
def fetchNextSegment(self): ''' Schedules the download of the next segment at current level ''' print("FETCH THE NEXT SEGMENT IN LEVEL: " + str(self.getCurrentLevel())) #print ("MY PID IS "+str(os.getpid())) playlist = self.parser.playlists[self.getCurrentLevel()] debug(DEBUG + 1, '%s fetchNextSegment level: %d cur_index: %d', self, self.getCurrentLevel(), self.getCurrentSegmentIndex()) # if self.getCurrentSegmentIndex() < playlist['start_index']: self.setCurrentSegmentIndex(playlist['start_index']) if self.getCurrentSegmentIndex() > playlist['end_index']: # else live video (ONLY HLS!!) if playlist['is_live'] and self.parser.getPlaylistType() == 'HLS': debug(DEBUG, '%s fetchNextSegment cur_index %d', self, self.getCurrentSegmentIndex()) self.parser.updateLevelSegmentsList( self.getCurrentLevel()).addCallback( self._updatePlaylistDone) # if video is vod else: debug(DEBUG, '%s fetchNextSegment last index', self) return cur_index = self.getCurrentSegmentIndex() levels = self.parser.getLevels() url_segment = playlist['segments'][cur_index]['url'] byterange = playlist['segments'][cur_index]['byterange'] if byterange != '': debug( DEBUG, '%s fetchNextSegment level: %d (%s/s) %d/%d : %s (byterange=%s)', self, self.getCurrentLevel(), format_bytes(float(levels[self.getCurrentLevel()]['rate'])), self.getCurrentSegmentIndex(), playlist['end_index'], url_segment, byterange) else: debug(DEBUG, '%s fetchNextSegment level: %d (%s/s) %d/%d : %s', self, self.getCurrentLevel(), format_bytes(float(levels[self.getCurrentLevel()]['rate'])), self.getCurrentSegmentIndex(), playlist['end_index'], url_segment) if self.controller.isBuffering(): idle_duration = 0.0 #fetch segment after the last segment download is completed else: idle_duration = self.controller.getIdleDuration() # load the next segment clientInfo = socket.gethostbyname(socket.gethostname()) segSize = float(float(self.getLastFragmentBytes() * 8)) / 1000 print["MESSAGE SEND TO ORCHESTRATOR"] print colored("[delegated level is ]" + str(self.delegatedLevel), 'green') if self.first == 1: socket_send_app_param.send_string( "%s %s %s %s %s %s" % ("2", "start", str(clientInfo), str( getLocalport()), str(os.getpid()), "1080p")) self.first = 0 else: socket_send_app_param.send_string( "%s %s %s %s %s %s" % ("2", "client_update", str(clientInfo), str( getLocalport()), str(os.getpid()), "1080p")) print("sent info to zmq-entity") reactor.callLater(idle_duration, self.startDownload, url_segment, byterange)
def playNextGotRequest(self, data, factory): ''' Updates feedbacks, calculates the control action and sets level of the next segment. :param data: downloaded data :param factory: the twisted factory (used without persistent connection) ''' self.stop_segment_request = time.time() download_time = (self.stop_segment_request - self.start_segment_request) self.last_downloaded_time = download_time self.bwe = len(data) / download_time self.last_fragment_size = len(data) self.downloaded_bytes += len(data) self.downloaded_segments += 1 debug(DEBUG, '%s __got_request: bwe: %s/s (fragment size: %s)', self, format_bytes(self.bwe), format_bytes(len(data))) self.queuedTime = self.media_engine.getQueuedTime( ) + self.parser.getFragmentDuration() self.queuedBytes = self.media_engine.getQueuedBytes() + len(data) playlist = self.parser.getSinglePlaylist(self.getCurrentLevel(), self.getCurrentView()) if ("initURL" in playlist): #print("HLS on mp4") # download init segment (DASH BASED CODING) if ("initSegment" in playlist): #push next segment #print("LEVEL: " + str(self.cur_level) + "\nVIEW: " + str(self.cur_view) + "\nInit URL: " + str(playlist["initURL"]) + "\nFragment Id: " + str(self.cur_index)) self.media_engine.setOriginalSideWidth( playlist['central_width']) self.media_engine.setScaledSideWidth(playlist['side_width']) self.media_engine.pushData(playlist["initSegment"] + data, self.parser.getFragmentDuration(), self.getCurrentLevel(), self.getCurrentView(), self.parser._getCapsDemuxer()) else: # ERROR print("ERROR: missing init file") else: # push next segment #print("HLS on mpegts") self.media_engine.pushData(data, self.parser.getFragmentDuration(), self.getCurrentLevel(), self.getCurrentView(), self.parser._getCapsDemuxer()) # flush segment on file if (self.save_chunks): filename = "{0!s}_{1!s}_{2!s}.mp4".format(self.downloaded_segments, self.getCurrentLevel(), self.getCurrentView()) self.logger.logData(filename, data) del data self.cur_index = self.media_engine.getNextVideoSegmentToBeFetched( self.cur_index) #Do something before calculating new control action self._onNewSegment() #Passing player parameters at the controller to calculate the control action self.updateFeedback(flag_check_buffering=False) #calc control action self.controller.setControlAction(self.controller.calcControlAction()) # set new level if self.getDownloadedSegments() > self.getInactiveCycles(): if (self.getNumViews() > 1): self.setView() if self.enable_stress_test: self.stressTest() else: self.setLevel(self.controller.getControlAction()) self.fetchNextSegment()