def queue_next(self,step=1): """ Advances 1 position in queue. case player state: - playing: the new track will start playing - paused : the current track will be preserved. new track will be played only if user inputs 'next' or ['stop','play']. - stopped: the new track will not be played until client sends 'play' Return values: - If successful [200,positions_moved] - else: [error_code] """ ans = [400] try: gst.debug('Will next_prev(%d)' % step) ok,moved = self.next_prev(step) gst.debug('next_prev(%d) returned (%f,%d)' % (step,ok,moved)) if ok: ans = [200] ans.append(moved) except Exception as e: print e gst.error('Problem near queue_next()') return ans
def status(self): """ Get player playing/!playing If metadata is available, then the answer will include it. Return value: - If successful: [200,True/False,metadata] - else: [err_code] """ ans = [200] try: gst.debug(self.queue.__str__()) playing = self.is_playing() current = self.player.get_current() tags = self.queue_item_by_uri(current) if not tags: # Failed to find tags or search is still # in progress tags = {} ans.append([playing,current,tags]) except Exception as e: print e gst.error('Problem near status()') ans = [400] return ans
def bus_message_eos (self, bus, message): """ Callback for gst.MESSAGE_EOS, will give up on tag search """ gst.error('Got eos, will abort') self.quit()
def start_recording(self): structure = gst.Structure("shifter-start-recording") event = gst.event_new_custom(gst.EVENT_CUSTOM_UPSTREAM, structure) res = self.pipeline.send_event(event) if res: print "start recording" else: gst.error("start recording failed")
def get_current(self): ans = [] try: ans = self.player.get_property('uri') except Exception as e: err_msg = 'Failed to get current. Exception:%s' % e.__str__() gst.error(err_msg) return ans
def start_recording(self): structure = gst.Structure ("shifter-start-recording") event = gst.event_new_custom (gst.EVENT_CUSTOM_UPSTREAM, structure) res = self.pipeline.send_event(event) if res: print "start recording" else: gst.error("start recording failed")
def bus_message_err (self, bus, message): """ Callback for gst.MESSAGE_ERROR, will give up on tag search """ err, debug = message.parse_error() err_msg = 'Got error message, will abort:%s ' % err, debug gst.error(err_msg) self.quit()
def queue_del(self,uri=None,pos=-1): """Remove an item from the queue. Item can be selected either by URI or by pos (position) in the queue. Removing the currently playing element will not stop playback. """ ans = [200] ind = -1 if uri: try: elem = self.queue_item_by_uri(uri) self.queue.index(elem) self.queue.remove(uri) ans.append('Removed element %s' % uri) except (ValueError, StopIteration) as e: err_msg = 'Exception:%s' % e.__str__() gst.warning(err_msg) ans = [400] ans.append(err_msg) elif pos >= 0: try: ind = pos del self.queue[pos] ans.append('Removed element at queue[%d]' % pos) except IndexError as e: err_msg = 'Failed to remove at %d. Exception:%s' % (pos,e.__str__()) gst.warning(err_msg) ans = [400] ans.append(err_msg) try: if ans[0] == 200: if ind < self.queue_pos: # update queue_pos to new queue size self.queue_pos -= 1 elif ind == self.queue_pos: # check if currently selected item was deleted if not ind and self.queue: # special case, first element was removed and # more are left, set queue_pos to head self.queue_pos = 0 if self.queue_pos >= 0: uri_new = self.queue[self.queue_pos]['uri'] get.debug('setting uri to %s' % uri_new) self.player.set_location(uri_new) else: # queue is empty now self.player.set_location(None) except Exception as e: print e err_msg = 'Problem near update queue_pos. Exception:%s' % e.__str__() gst.error(err_msg) ans = [400] ans.append(err_msg) if ans[0] == 200 and len(ans) > 1: # call to get debug output self.queue_get() return ans
def seek_end(self): event = gst.event_new_seek( 1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE, gst.SEEK_TYPE_END, -1, gst.SEEK_TYPE_NONE, 0) res = self.pipeline.send_event(event) if res: gst.info("setting new stream time to 0") else: gst.error("seek to end failed")
def playEpisode (self, view, episode): gst.info('Playing episode ' + episode.title) mrl = episode.getUri(self.getConnectionSpeed()) if mrl: gst.log('Playing uri ' + mrl) self.totem.action_set_mrl_and_play(mrl, None) #self.totem.action_remote(totem.REMOTE_COMMAND_ENQUEUE, mrl) #self.totem.action_remote(totem.REMOTE_COMMAND_PLAY, mrl) else: gst.error('No uri for episode ' + episode.title)
def next_prev(self, step): """ Move queue step positions (next,back) Returns a tuple (ok,queue_moved), where the value queue_moved indicates number of postions shifted by queue_move(), and is valid only if ok==True """ ans = False queue_moved = 0 if(step == 0): # come on... return ans,queue_moved elif not self.queue: # queue is empty gst.debug('queue is empty') ans = True return ans,queue_moved try: queue_pos_old = self.queue_pos queue_pos_new = self.queue_pos + step queue_finished = False # Verify index is within limits, stop if out of boundries # This is the typical behaviour of mp3 playing software # such as Rhythmbox. if queue_pos_new < 0 or queue_pos_new >= len(self.queue): gst.debug('queue playback completed, will stop and return to first element') # queue completed, stop player and return queue_pos_new = 0 queue_finished = True queue_moved = abs(queue_pos_new - queue_pos_old) uri_new = self.queue[queue_pos_new]['uri'] gst.debug('Will set new location to %s' % uri_new) self.player.set_location(uri_new) # update queue position self.queue_pos = queue_pos_new gst.debug('New queue_pos:%d/%d' % (self.queue_pos, len(self.queue))) try: if(self.is_playing()): gst.debug('Will stop+play to advance in queue') self.stop() if queue_finished: gst.debug('Omiting play(), queue finished') else: self.play() # Mark success ans = True except Exception as e: print e gst.error('Failed to advance in queue') except Exception as e: print e gst.error('Problem near next()') return ans,queue_moved
def help_msg(self): """ Returns help message string. """ menu = "\n-- Help menu --\n\nValid commands:\n\t" try: funcs = '\n\t'.join(self.handlers.keys()) except: gst.error('Failed to build handler list') funcs = [] return "%s%s" % (menu,funcs)
def seek_end(self): event = gst.event_new_seek(1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE, gst.SEEK_TYPE_END, -1, gst.SEEK_TYPE_NONE, 0) res = self.pipeline.send_event(event) if res: gst.info("setting new stream time to 0") else: gst.error("seek to end failed")
def seek(self, location): gst.debug("seeking to %r" % location) event = gst.event_new_seek(1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE, gst.SEEK_TYPE_SET, location, gst.SEEK_TYPE_NONE, 0) res = self.player.send_event(event) if res: gst.info("setting new stream time to 0") self.player.set_new_stream_time(0L) else: gst.error("seek to %r failed" % location)
def is_playing(self): """ Wrapper for player.is_playing() """ playing = False try: playing = self.player.is_playing() if self.src == 'stdin': print "Player state: Playing" if playing else "Player state: NOT Playing" gst.debug('status:%r' % playing) except Exception as e: print e gst.error('Problem near is_playing()') return playing
def seek(self, location): """ @param location: time to seek to, in nanoseconds """ gst.debug("seeking to %r" % location) event = gst.event_new_seek( 1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE, gst.SEEK_TYPE_SET, location, gst.SEEK_TYPE_NONE, 0) res = self.player.send_event(event) if res: gst.info("setting new stream time to 0") self.player.set_new_stream_time(0L) else: gst.error("seek to %r failed" % location)
def seek(self, location): """ @param location: time to seek to, in nanoseconds """ # gst.debug("seeking to %r" % location) event = gst.event_new_seek(1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE, gst.SEEK_TYPE_SET, location, gst.SEEK_TYPE_NONE, 0) res = self._pipeline.send_event(event) if res: # gst.info("setting new stream time to 0") self._pipeline.set_new_stream_time(0L) else: gst.error("seek to %r failed" % location)
def queue_prev(self,step=-1): """ Just like 'queue_next()' but backwards' """ ans = [400] if step >= 0: return ans try: ok,moved = self.next_prev(step) if ok: ans = [200] ans.append(moved) except Exception as e: print e gst.error('Problem near queue_prev()') return ans
def fast_forward(self): """ Here we will fast forward the stream for as many times as this is called """ if self.rate < 8.0: self.rate = self.rate*2.0 event = gst.event_new_seek(self.rate, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, gst.SEEK_TYPE_SET, self.query_position()[0], gst.SEEK_TYPE_NONE, 0) res = self.player.send_event(event) if res: gst.info("fast forwarding at rate: %f" % self.rate) self.player.set_new_stream_time(0L) else: gst.error("change rate to %f failed" % self.rate) return
def slow_motion(self): """ Here we will slow motion the stream for as many times as this is called """ self.rate = self.rate/2.0 event = gst.event_new_seek(self.rate, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, gst.SEEK_TYPE_SET, self.query_position()[0], gst.SEEK_TYPE_NONE, 0) res = self.player.send_event(event) if res: gst.info("slowing playback to rate: %f" % self.rate) self.player.set_new_stream_time(0L) else: gst.error("change rate to %f failed" % self.rate) return
def play(self): """ Change the stream state to playing or simply change its playing rate to normal rate """ if self.rate != 1.0: self.rate = 1.0 event = gst.event_new_seek(self.rate, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, gst.SEEK_TYPE_SET, self.query_position()[0], gst.SEEK_TYPE_NONE, 0) res = self.player.send_event(event) if res: gst.info("slowing playback to rate: %f" % self.rate) self.player.set_new_stream_time(0L) else: gst.error("change rate to %f failed" % self.rate) else: gst.info("playing player") self.player.set_state(gst.STATE_PLAYING) self.playing = True return
def queue_add(self, location): """ Adds location to the play queue. NOTES: - location must be a valid URI - If self.verify_on_load==True, then location can be a directory. All .mp3 files from the directory will be loaded. Return values: - If successful: [200] - else: [err_code,[err_msg]]. """ ans = [200] is_dir = False is_ascii = True # ignore non-ascii stuff try: unicode(location) except UnicodeDecodeError as e: is_ascii = False gst.warning('Ignoring %s, not ascii' % location) ans = [406] ans.append(e.__str__()) if not is_ascii or not gst.uri_is_valid(location): if is_ascii: gst.error("Error: Invalid URI: %s\nExpected uri" "like file:///home/foo/bar.mp3\nIgnoring...\n" % location) ans = [403] else: # if(self.player.status()): # self.queued = True gst.debug('URI is valid: %s' % location) err_msg = [] code = 0 msg = '' if self.verify_on_load: gst.debug('Attempting to verify %s' % location) if location[0:4] == 'file': path = location[7:] try: with open(path) as f: pass except IOError as e: if e.errno == 21: is_dir = True loaded = [] try: # is directory, load content os.chdir(path) for f in os.listdir('.'): if f.endswith('.mp3'): full_path = location+'/'+f res = self.queue_add(full_path) if res[0] == 200: loaded.append(full_path) ans.append(loaded) except IOError as e: err_msg = 'Error parsing directory, will'\ 'stop loading, exception:%s' % e.__str__() gst.error(err_msg) ans = [404] ans.append(err_msg) else: err_msg = 'Will not load, exception when opening'\ '%s: %s' % (path,e.__str__()) gst.error(err_msg) ans = [404] ans.append(err_msg) elif location[0:4] == 'http': try: urlopen_ans = urlopen(location) code = urlopen_ans.code msg = urlopen_ans.msg if code >= 400: err_msg = 'urlopen failed with %d: %s' % (code,msg) except: err_msg = 'urlopen() failed' if err_msg: ans = [400] ans.append(err_msg) if not err_msg: gst.debug('Verification succeeded!') if err_msg: gst.warning(err_msg) elif not is_dir: gst.debug('Setting location to %s' % location) try: if location in self.queue: gst.info('Duplicate entry %s' % location) self.queue.append({'uri':location,'tags':{}}) try: gst.debug('will prepare tag getter: get_tags(%s)' % location) tgt = threading.Thread(target=tagget.get_tags, args=[self.queue[-1]['tags'], location]) gst.debug('will start tag getter') tgt.start() except Exception as e: err_msg = 'Problem near tag get thread. Exception:%s' % e.__str__() gst.warning(err_msg) # call to print current queue self.queue_get() if self.queue_pos == -1: gst.debug('New queue, will next()') ans_n = self.queue_next() if ans_n[0] == 200: gst.debug('New queue play success!') else: ans = [400] ans.append('Failed to set initial queue position') except Exception as e: print e ans = [400] gst.error('Problem near queue.append()') return ans
def _decode(self,callback,width=None,height=None,\ format=None,depth=None,bpp=None): if not width or not height or not format : raise NameError("Failed to init decoder without format,"\ +" or height or width") begin = self.video.begin end = self.video.end #init gstreamer caps="video/%s,width=%i,height=%i"\ % (format,width,height) if depth : caps+=",depth=%i" % depth if bpp : caps+=",bpp=%i" % bpp pip_desc ="uridecodebin uri=%s ! ffmpegcolorspace ! videoscale ! appsink name=sink " % self.video.uri pipeline = gst.parse_launch(pip_desc) self.appsink = pipeline.get_by_name("sink") self.appsink.set_property('emit-signals', True) self.appsink.set_property('sync', False) self.appsink.set_property('caps', gst.caps_from_string(caps)) if pipeline.set_state(gst.STATE_PLAYING) == gst.STATE_CHANGE_FAILURE: raise NameError("Failed to load uri video (example file:///home/... )%s" % self.video.uri) buff = self.appsink.emit('pull-preroll') #seek if necessary if begin > 0 or end is not None : self.log.info("seek in video from %d to %d" % (begin,end)) flags = gst.SEEK_FLAG_FLUSH flags |= gst.SEEK_FLAG_ACCURATE res = pipeline.seek( 1.0, # normal-speed playback gst.FORMAT_TIME, # time-based seek flags, gst.SEEK_TYPE_SET, begin*gst.SECOND, gst.SEEK_TYPE_SET, end*gst.SECOND) res = pipeline.seek_simple(gst.FORMAT_TIME,flags, begin*gst.SECOND) if res: pipeline.set_new_stream_time(0L) else: gst.error("seek to %r failed" % begin) raise NameError("seek failed") #start video decoding framenumber = 0 while True : buff = self.appsink.emit('pull-buffer') if not buff : break f = pimpy.video.Frame(**{ "video" : self.video , "framenumber" : framenumber, "data" : buff.data, "format": format , "width" : width, "height" : height }) f.position = pipeline.query_position(gst.Format(gst.FORMAT_TIME), None)[0] #send frame decoded to the callback function callback(f) framenumber+=1 #Decode step finish self.video.framenumber = framenumber pipeline.set_state(gst.STATE_NULL) pipeline = None
def do_state_change(self, transition): if not self._prerolled: gst.error("Call Jukebox.preroll() before!") return gst.STATE_CHANGE_FAILURE # chaining up return gst.Bin.do_state_change(self, message)
def handle_cmd(self, msg): """ Receives a msg and executes it if it corresponds to a registered cmd Return values: - If successful: [200,data_returned_by_cmd] - else: [err_code,err_msg] """ if self.src == 'zmq': cmd,cmd_code,args = shared.json_server_dec(msg) gst.debug('dec: cmd_name=%s,'\ 'cmd_code=%d,args=%s' % (cmd,cmd_code,args.__str__())) else: words = msg.split() cmd = words[0] cmd_code = words[-1] args = [] cmd_code_dic= 0 ans = [200] # default to OK if not self.is_registered(cmd) or (self.src == 'stdin' and len(words) < 2): gst.error("Error: Invalid command: %s, Ignoring...\n" % cmd) help_msg = self.help_msg() if(self.src == 'stdin'): print help_msg ans = [401] ans.append(help_msg) else: # first arg is command name, last is cmd if if self.src == 'stdin': # if src is zmq, then json_server_dec() took # care of fetching arguments for command args = words[1:-1] try: # convert from string to int cmd_code = int(cmd_code) except ValueError as e: gst.warning('int(cmd_code) failed!Exception:%s' % e.__str__()) try: # get command id from dict, compare with rx id (must match) gst.debug('Matching command id with dict values...') cmd_code_dic = shared.cmd_name_id[cmd] try: # check matching if cmd_code_dic != cmd_code: gst.error('Command code received %d does not match dict %d' % (cmd_code,cmd_code_dic)) else: gst.debug('Command id matched!') try: if(args): gst.debug('Executing cmd=%s with args=%s' % (cmd,args)) args = args[0] if cmd == 'queue_add': ans = list(self.queue_add(args)) elif cmd == 'queue_del': param = -1 try: param = int(args) except ValueError as e: gst.debug('Param is not int. Exception:%s' % e.__str__()) if not param == -1: ans = list(self.queue_del(pos=param)) else: ans = list(self.queue_del(uri=args)) #TODO implement generic multi arg command and support for multi load (queue) # ans = self.fire(cmd,args) else: gst.debug('Executing cmd=%s without args' % cmd) ans = list(self.fire(cmd)) except ValueError as ve: print 'Exception:',ve except Exception, e: print 'Exception:',e gst.error('Problem near cmd execution: %s' % msg) ans = [402] except Exception as e: print 'Exception:',e gst.error('Problem near command id matching: (rx,dict)=(%d,%d)' % (cmd_code,cmd_code_dic)) except Exception as e: print 'Exception:',e gst.error('Problem near: find Id for cmd: %s' % cmd) # Take cmd result and prepare answer to send to client gst.debug('ans(len=%d)=%s' % (len(ans),ans)) assert(len(ans) == 1 or len(ans) == 2) # (code, data) data = [] if len(ans) == 2: data = ans[1] return shared.json_server_enc(cmd_code, ans[0], data)