From: Andre L. V. Loureiro Date: Wed, 27 May 2009 23:15:29 +0000 (-0400) Subject: Added the services for zukebox render X-Git-Url: http://vcs.maemo.org/git/?p=zukebox;a=commitdiff_plain;h=7358a38f5c6bf47dd14cad755fbc34a2e3ddf983 Added the services for zukebox render --- diff --git a/zukebox_render/src/services/__init__.py b/zukebox_render/src/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/zukebox_render/src/services/avtransport/__init__.py b/zukebox_render/src/services/avtransport/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/zukebox_render/src/services/avtransport/av_transport.py b/zukebox_render/src/services/avtransport/av_transport.py new file mode 100644 index 0000000..95144bb --- /dev/null +++ b/zukebox_render/src/services/avtransport/av_transport.py @@ -0,0 +1,261 @@ + +__all__ = ('AVTransport', ) + +import os.path +import platform + +from brisa.core import log +from brisa.upnp.device import Service, ServiceController +from zukebox_renderer.services.gst_renderer import GSTRenderer + +service_name = 'AVTransport' +service_type = 'urn:schemas-upnp-org:service:AVTransport:1' + + +class AVTransport(Service): + + def __init__(self, xml_path): + Service.__init__(self, service_name, service_type, '', + os.path.join(xml_path, 'render-transport-scpd.xml'), + AVTransportControl()) + + def get_player(self): + return self.control_controller.get_player() + + +class AVTransportControl(ServiceController): + + def __init__(self): + ServiceController.__init__(self, service_type) + self.av_transport_uri = '' + self.number_of_tracks = 0 + + self.gst_player = GSTRenderer() + + self.urilist = {} + self.transport_state = 'NO_MEDIA_PRESENT' + self.transport_status= 'OK' + self.transport_speed = 1 + + def soap_SetAVTransportURI(self, *args, **kwargs): + """Specifies the URI of resource. + + This action specifies the URI of the resource to be controlled + by the specified AVTransport instance. It is RECOMMENDED that the + AVTransport service checks the MIME-type of the specified resource + when executing this action""" + + instance_id = kwargs['InstanceID'] + current_uri = kwargs['CurrentURI'] + current_uri_metadata = kwargs['CurrentURIMetaData'] + + self.av_transport_uri = current_uri + self.number_of_tracks = 1 + self.gst_player.av_uri = current_uri + self.transport_state = 'STOPPED' + + log.info('SetAVTransportURI()') + + return {'SetAVTransportURI': {}} + + def soap_GetMediaInfo(self, *args, **kwargs): + """Return information of current Media. + + This action returns information associated with the current + media of the specified instance; it has no effect on state.""" + + log.info('GetMediaInfo()') + + return {'GetMediaInfoResponse': + {'NrTracks': str(self.number_of_tracks), + 'MediaDuration': str(self.gst_player.query_duration()[0]), + 'CurrentURI': self.av_transport_uri, + 'CurrentURIMetaData': '', + 'NextURI': '', 'NextURIMetaData': '', + 'PlayMedium': 'NETWORK', + 'RecordMedium': '', + 'WriteStatus': ''}} + + def soap_GetMediaInfo_Ext(self, *args, **kwargs): + """Return information of current Media and CurrentType argment. + + This action returns information associated with the current + media of the specified instance; it has no effect on state.The + information returned is identical to the information returned + by the GetMediaInfo() action, except for the additionally + returned CurrentType argument """ + + log.info('GetMediaInfo_Ext()') + + return {'GetMediaInfo_ExtResponse': {'CurrentType': 'TRACK_UNAWARE', + 'NrTracks': str(self.number_of_tracks), + 'MediaDuration': str(self.gst_player.query_duration()[0]), + 'CurrentURI': self.av_transport_uri, + 'CurrentURIMetaData': '', + 'NextURI': '', 'NextURIMetaData': '', + 'PlayMedium': 'NETWORK', + 'RecordMedium': '', + 'WriteStatus': ''}} + + def soap_GetTransportInfo(self, *args, **kwargs): + """Return informations os current Transport state. + + This action returns information associated with the current + transport state of the specified instance; it has no effect on + state.""" + + self.transport_state = self.gst_player.get_state() + + return {'GetTransportInfoResponse': {'CurrentTransportState': + self.transport_state, + 'CurrentTransportStatus': + self.transport_status, + 'CurrentSpeed': + str(self.transport_speed)}} + + def soap_GetPositionInfo(self, *args, **kwargs): + """Return information of the Transporte of the specified instante. + + This action returns information associated with the current + position of the transport of the specified instance; it has no + effect on state.""" + + dur = self.gst_player.query_duration() + pos = self.gst_player.query_position() + + duration = dur[0] + + if dur[1] == -1: + duration = pos[0] + + + abs_pos = pos[1] + rel_pos = pos[1] + + if dur[1] > 2147483646: + abs_pos = pos[1]*2147483646/dur[1] + + if pos[1] == -1: + abs_pos = 0 + rel_pos = 0 + + return {'GetPositionInfoResponse': {'Track': '1', + 'TrackDuration': \ + '"'+str(duration)+'"', + 'TrackMetaData': '', + 'TrackURI': \ + self.av_transport_uri, + 'RelTime': '"'+str(pos[0])+'"', + 'AbsTime': '"'+str(pos[0])+'"', + 'RelCount': rel_pos, + 'AbsCount': abs_pos}} + + def soap_GetDeviceCapabilities(self, *args, **kwargs): + """Return information on device capabilities. + + This action returns information on device capabilities of + the specified instance, such as the supported playback and + recording formats, and the supported quality levels for + recording. This action has no effect on state.""" + + log.info('GetDeviceCapabilities()') + + return {'GetDeviceCapabilitiesResponse': {'PlayMedia': 'NONE', + 'RecMedia': 'NOT_IMPLEMENTED', + 'ReqQualityMode': 'NOT_IMPLEMENTED'}} + + def soap_GetTransportSettings(self, *args, **kwargs): + """Return information on various settings of instance. + + This action returns information on various settings of the + specified instance, such as the current play mode and the + current recording quality mode. This action has no effect on + state.""" + + log.info('GetTransportSettings()') + + return {'GetTransportSettingsResponse': {'PlayMode': 'DIRECT_1', + 'ReqQualityMode': 'NOT_IMPLEMENTED'}} + + def soap_Stop(self, *args, **kwargs): + """Stop progression of current resource. + + This action stops the progression of the current resource that + is associated with the specified instance.""" + + log.info('Stop()') + self.transport_state = 'TRANSITIONING' + self.gst_player.stop() + self.transport_state = 'STOPPED' + + return {'StopResponse': {}} + + def soap_Play(self, *args, **kwargs): + """Play the resource of instance. + + This action starts playing the resource of the specified + instance, at the specified speed, starting at the current + position, according to the current play mode.""" + + log.info('Play()') + log.debug('Playing uri: %r', self.gst_player.av_uri) + self.transport_state = 'TRANSITIONING' + + self.gst_player.play() + + self.transport_state = 'PLAYING' + + return {'PlayResponse': {}} + + def soap_Pause(self, *args, **kwargs): + """Pause the resouce of instance. + + This action starts playing the resource of the specified + instance, at the specified speed, starting at the current + position, according to the current play mode.""" + + log.info('Pause()') + + self.transport_state = 'TRANSITIONING' + + self.gst_player.pause() + + self.transport_state = 'PAUSED_PLAYBACK' + + return {'PauseResponse': {}} + + def soap_Seek(self, *args, **kwargs): + """Seek through the resource controlled. + + This action starts seeking through the resource controlled + by the specified instance - as fast as possible - to the position, + specified in the Target argument.""" + + (instance_id, unit, target) = args + + self.gst_player.seek(unit, target) + + log.info('Seek()') + + return {'SeekResponse': {}} + + def soap_Next(self, *args, **kwargs): + """Advance to the next track. + + This is a convenient action to advance to the next track. This + action is functionally equivalent to Seek(TRACK_NR, CurrentTrackNr+1). + This action does not cycle back to the first track.""" + + log.info('Next()') + + def soap_Previous(self, *args, **kwargs): + """Advance to the previous track. + + This is a convenient action to advance to the previous track. This + action is functionally equivalent to Seek(TRACK_NR, CurrentTrackNr-1). + This action does not cycle back to the last track.""" + + log.info('Previous()') + + def get_player(self): + return self.gst_player diff --git a/zukebox_render/src/services/gst_renderer/__init__.py b/zukebox_render/src/services/gst_renderer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/zukebox_render/src/services/gst_renderer/gst_renderer.py b/zukebox_render/src/services/gst_renderer/gst_renderer.py new file mode 100644 index 0000000..7b53c3d --- /dev/null +++ b/zukebox_render/src/services/gst_renderer/gst_renderer.py @@ -0,0 +1,137 @@ + +import pygst +import time +pygst.require('0.10') +import gst + +from brisa.core import log +from brisa.utils.looping_call import LoopingCall + + +class GSTRenderer(object): + + def __init__(self): + self.build_pipeline() + self.__av_uri = None + self.time_format = gst.Format(gst.FORMAT_TIME) + self.player_state = 0 + loop = LoopingCall(self.poll_bus) + loop.start(0.2, True) + + def poll_bus(self): + if self.bus: + message = self.bus.poll(gst.MESSAGE_ERROR|gst.MESSAGE_EOS, + timeout=1) + if message: + self.on_message(self.bus, message) + + def get_state(self): + if self.player_state == 0: + return 'STOPPED' + if self.player_state == 1: + return 'PLAYING' + if self.player_state == 2: + return 'PAUSED_PLAYBACK' + + def __set_uri(self, uri): + self.player.set_property('uri', uri) + self.__av_uri = uri + + def __get_uri(self): + return self.__av_uri + + av_uri = property(__get_uri, __set_uri) + + def build_pipeline(self): + self.player = gst.element_factory_make("playbin", "player") + self.bus = self.player.get_bus() + self.player.set_state(gst.STATE_READY) + + def on_message(self, bus, message): + t = message.type + if t == gst.MESSAGE_EOS: + self.player.set_state(gst.STATE_NULL) + self.player_state = 0 + elif t == gst.MESSAGE_ERROR: + self.player.set_state(gst.STATE_NULL) + self.player_state = 0 + + def play(self): + if self.av_uri is not None: + if (self.player.set_state(gst.STATE_PLAYING) == + gst.STATE_CHANGE_FAILURE): + log.error("error trying to play %s.", self.av_uri) + self.player_state = 1 + else: + log.info("av_uri is None, unable to play.") + + def stop(self): + if self.player.set_state(gst.STATE_READY) == gst.STATE_CHANGE_FAILURE: + log.error("error while stopping the player") + self.player_state = 0 + + def pause(self): + if self.player.set_state(gst.STATE_PAUSED) == gst.STATE_CHANGE_FAILURE: + log.error("error while pausing the player") + self.player_state = 2 + + def seek(self, unit, target): + if unit == "ABS_TIME": + target_time = self.convert_int(target) + self.player.seek_simple(self.time_format, gst.SEEK_FLAG_FLUSH, + target_time) + + if unit == "REL_TIME": + target_time = self.convert_int(target) + cur_pos = self.query_position()[1] + self.player.seek_simple(self.time_format, gst.SEEK_FLAG_FLUSH, + target_time+cur_pos) + + if unit == "ABS_COUNT": + self.player.seek_simple(self.time_format, gst.SEEK_FLAG_FLUSH, + target) + + if unit == "REL_COUNT": + cur_pos = self.query_position()[1] + self.player.seek_simple(self.time_format, gst.SEEK_FLAG_FLUSH, + target + cur_pos) + + def set_volume(self, volume): + self.player.set_property("volume", volume/10) + + def get_volume(self): + return int(self.player.get_property("volume")*10) + + def query_duration(self): + time.sleep(0.3) + try: + dur_int = self.player.query_duration(self.time_format, None)[0] + dur_str = self.convert_ns(dur_int) + except gst.QueryError: + dur_int = -1 + dur_str = '' + + return dur_str, dur_int + + def query_position(self): + try: + pos_int = self.player.query_position(self.time_format, None)[0] + pos_str = self.convert_ns(pos_int) + except gst.QueryError: + pos_int = -1 + pos_str = '' + + return pos_str, pos_int + + def convert_ns(self, time): + hours, left_time = divmod(time/1000000000, 3600) + minutes, left_time = divmod(left_time, 60) + return '%02d:%02d:%02d' % (hours, minutes, left_time) + + def convert_int(self, time_str): + time_str = time_str.strip('")( ') + (hours, min, sec) = time_str.split(":") + time_int = int(hours) * 3600 + int(min) * 60 + int(sec) + time_int = time_int * 1000000000 + return time_int + diff --git a/zukebox_render/src/services/render_control/__init__.py b/zukebox_render/src/services/render_control/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/zukebox_render/src/services/render_control/render_ctl.py b/zukebox_render/src/services/render_control/render_ctl.py new file mode 100644 index 0000000..c091fc2 --- /dev/null +++ b/zukebox_render/src/services/render_control/render_ctl.py @@ -0,0 +1,58 @@ + +import os.path + +from brisa.core import log +from brisa.upnp.device import Service, ServiceController + +class RenderingControl(Service): + + def __init__(self, xml_path, gst_player): + Service.__init__(self, service_name, service_type, '', + os.path.join(xml_path, 'render-control-scpd.xml'), + RenderingControlController(gst_player)) + + + +class RenderingControlController(ServiceController): + + service_name = 'RenderingControl' + service_type = 'urn:schemas-upnp-org:service:RenderingControl:1' + + def __init__(self, gst_player): + ServiceController.__init__(self, self.service_type) + self.gst_player = gst_player + + def soap_ListPresets(self, *args, **kwargs): + """ Return List of currently defined. This action returns a list of + the currently defined presets. + """ + log.debug('Action on RenderingControlController: ListPresets()') + return {'ListPresetsResponse': {'CurrentPresetNameList': ''}} + + def soap_SelectPreset(self, *args, **kwargs): + """ Select Present state variables. This action restores (a subset) of + the state variables to the values associated with the specified + preset. + """ + log.debug('Action on RenderingControlController: SelectPreset()') + return {'SelectPresetResponse': {}} + + def soap_GetVolume(self, *args, **kwargs): + """ Return the current volume state. This action retrieves the current + value of the Volume state variable of the specified channel for the + specified instance of this service + """ + log.debug('Action on RenderingControlController: GetVolume()') + (instance_id, channel) = args + volume = int(self.gst_player.get_volume()) + return {'GetVolumeResponse': {'CurrentVolume': volume}} + + def soap_SetVolume(self, *args, **kwargs): + """Set volume of instance and chanel. + + This action sets the Volume state variable of the specified + instance and channel to the specified value.""" + log.debug('Action on RenderingControlController: SetVolume%s', args) + (instance_id, channel, desired_volume) = args + self.gst_player.set_volume(int(desired_volume)) + return {'SetVolumeResponse': {}}