def __remove_point(self, point): assert type(point) is PointController LOG.debug("MC: removing point [%s]", point.point_id) try: self.__manager.remove_point(point.point_id) except ManagerError as e: LOG.warning(str(e))
def __send_dtmf(self, message): point_id = str(message.point_id) point = self.__get_point_by_id(point_id) if point is None: LOG.warning("Attempt to unjoin with unexisting point") return point.event("SEND_DTMF", dtmf=str(message.get("dtmf", "")))
def __on_message_join(self, message): try: point_id = str(message.point_id) room_id = str(message.room_id) self.__manager.join_room(point_id, room_id) except ManagerError as e: LOG.warning(str(e))
def on_message(self, message, transport): """ received message from bus :param message: message object """ assert type(message) is messaging.Message message_type = message.type if message_type == MessageType.CREATE_POINT: self.__on_message_create_point(message) elif message_type == MessageType.REMOVE_POINT: self.__on_message_remove_point(message) elif message_type == MessageType.JOIN_ROOM: self.__on_message_join(message) elif message_type == MessageType.UNJOIN_ROOM: self.__on_message_unjoin(message) elif message_type == MessageType.SDP_ANSWER: self.set_remote_sdp(message) elif message_type == MessageType.SEND_DTMF: self.__send_dtmf(message) else: LOG.warning("Unknown message type: %s", repr(message_type))
def set_remote_sdp(self, message): point_id = str(message.point_id) point = self.__get_point_by_id(point_id) if point is not None: point.event(PointEvent.SDP_ANSWER, sdp=str(message.get("sdp", ""))) else: LOG.warning("Unknown point " + str(point_id))
def unjoin(self, media_point): assert isinstance(media_point, IMediaPoint) if media_point in self.__points: if len(self.__points) == 2: LOG.debug("MediaRoom.stopping pipeline") self.__context.stop() self.__points.remove(media_point) self.__remove_point_from_pipeline(media_point)
def __check_conn_ready(self): if self.__remote_conn and self.__local_rtp_conn and self.__local_rtcp_conn: LOG.info("SrtpConn.check_conn_ready: %s -> %d, %d", self.__remote_conn, self.__local_rtp_conn.port, self.__local_rtcp_conn.port) self.__local_media_description.add_candidate(self.__remote_conn.port, self.__profile.ip) if self._listener is not None: self._listener.media_point_frontend_ready(self)
def set_remote_media_description(self, remote_media_description): assert type(remote_media_description) is MediaDescription self._remote_media = remote_media_description remote_media_description.set_addr(self.__local_rtp_conn.port, "127.0.0.1") remote_media_description.rtcp.port = self.__local_rtcp_conn.port self._media_point.set_remote_media_description(remote_media_description) add_point_message = dict( type="ADD_POINT", id=self.point_id, localRtp=self.__local_rtp_conn.id, localRtcp=self.__local_rtcp_conn.id, remoteRtp=self.__remote_conn.id, remoteRtcp=self.__remote_conn.id, localIce=self.__local_media_description.ice.ufrag + ":" + self.__local_media_description.ice.pwd, remoteIce=self._remote_media.ice.ufrag + ":" + self._remote_media.ice.pwd) self.__agent.send_message(add_point_message) local_crypto_key = binascii.b2a_hex(binascii.a2b_base64(self.__local_media_description.crypto.key)) self.__agent.send_message({ "type": "ADD_LOCAL_STREAM", "pointId": self.point_id, "key": local_crypto_key, "ssrc": self.__local_media_description.streams[0].ssrc_id}) remote_crypto_key = binascii.b2a_hex(binascii.a2b_base64(self._remote_media.crypto.key)) if len(self._remote_media.streams): self.__agent.send_message({ "type": "ADD_REMOTE_STREAM", "pointId": self.point_id, "key": remote_crypto_key, "ssrc": self._remote_media.streams[0].ssrc_id, "rtp": "udp://127.0.0.1:" + str(self._media_point.rtp_port), "rtcp": "udp://127.0.0.1:" + str(self._media_point.rtcp_port)}) ####### draw debug scheme LOG.debug(""" +-- ice --+ +-- gst -- + ! ! ! ! ! %05d <----rtp----> %05d ! Chrome ? <----------->%05d ! ! ! ! %05d <----rtcp---> %05d ! ! ! ! ! +---------+ +----------+ """, self.__local_rtp_conn.port, self._media_point.rtp_port, self.__remote_conn.port, self.__local_rtcp_conn.port, self._media_point.rtcp_port)
def join(self, media_point): assert isinstance(media_point, IMediaPoint) if media_point in self.__points: return assert len( self.__points) <= 1 # currently no more than 2 points in room self.__points.append(media_point) self.__add_point_to_pipeline(media_point) if len(self.__points) == 2: self.__link_points(self.__points[0], self.__points[1]) self.__link_points(self.__points[1], self.__points[0]) LOG.debug("MediaRoom.starting pipeline") self.__context.play() for p in self.__points: p.force_key_unit()
def join(self, point): # we have PointController here # TODO: create RoomController point = point.point assert isinstance(point, Point) if point in self.__points: LOG.warning("Attempt to join room %s more than once", self.__room_id) return self.__points.append(point) if point.audio_point: self.__get_audio_room().join(point.audio_point) if point.video_point: self.__get_video_room().join(point.video_point)
def media_point_frontend_ready(self, media_point): LOG.debug("SrtpMediaPoint.media_point_frontend_ready") self.__local_media_description.direction = SdpDirection.SEND_RECV media_type = self.__local_media_description.media_type #ssrc_id = 2222L if media_type is MediaType.VIDEO else 1111L ssrc_id = self._media_point.transcoder.ssrc_id self.__local_media_description.streams.generate(ssrc_id=ssrc_id, media_type=media_type) #self._media_point.transcoder.ssrc_id = self.__local_media_description.streams[0].ssrc_id self._media_point.transcoder.cname = self.__local_media_description.streams[0].cname remote_profile = self.__profile local_profile = self.__config.profile("local") self.__agent.request_conn(remote_profile, lambda conn: self.__remote_conn_created(conn)) self.__agent.request_conn(local_profile, lambda conn: self.__local_rtp_conn_created(conn)) self.__agent.request_conn(local_profile, lambda conn: self.__local_rtcp_conn_created(conn))
def __init__(self, raw_media): """ :param raw_media: raw media object :param pt_reserver: payload type reserver """ assert type(raw_media) is raw.media.Media self.__raw_media = raw_media self.__rtp_codecs = [] for payload_type in self.payload_types: try: rtp_codec = create_codec_from_attributes(raw_media.media_type, payload_type, self.__raw_media.attributes) self.__rtp_codecs.append(rtp_codec) except UnknownCodecError as e: LOG.warn("Unknown codec with payload type %d", e.value)
def __on_message_create_point(self, message): try: point = self.__manager.create_point( point_id=str(message.point_id), initiator_message=message, config=self.__config, balancer=self.__balancer, transcoding_factory=self.__transcoding_factory) if message.has("cc") and message.has("vv"): profile_name = message.get("profile", "") profile = self.__config.profile(profile_name) assert profile is not None try: point.event(PointEvent.CREATE_OFFER, cc=message.get("cc"), vv=message.get("vv"), profile=profile) except PointControllerError: self.__remove_point(point) else: LOG.warning("No cc or vv in initiator message") LOG.warning("Answer model not implemented") except ManagerError as e: LOG.warning(str(e))
def event(self, event_type, **kwargs): if event_type != Event.TIMER: LOG.debug("PointController[%s]: Got event %s in state %s", self.__point_id, repr(event_type), repr(self.__state)) if self.state == State.START: if event_type == Event.CREATE_OFFER: self.__create_offer(**kwargs) else: self.__unhandled_event(event_type) elif self.state == State.CREATING_OFFER: if event_type == Event.CONN_READY: LOG.debug("PointController.LOCAL-SDP=%s", self.__point.local_sdp) self.state = State.WAITING_REMOTE_SDP self.reply(MessageType.SDP_OFFER, {"sdp": str(self.__point.local_sdp)}) elif event_type == Event.REMOVE: self.__remove() self.reply(MessageType.REMOVE_POINT_OK) elif event_type == Event.TIMER: pass else: self.__unhandled_event(event_type) elif self.state == State.WAITING_REMOTE_SDP: if event_type == Event.SDP_ANSWER: try: self.state = State.CONNECTED self.__on_remote_sdp(**kwargs) self.reply(MessageType.CREATE_POINT_OK) except (ParseError, AssertionError) as err: LOG.exception("Exception while parsing SDP-ANSWER: %s", str(err)) self.state = State.ERROR self.reply(MessageType.CREATE_POINT_FAILED) elif event_type == Event.REMOVE: self.__remove() self.reply(MessageType.REMOVE_POINT_OK) elif event_type == Event.TIMER: pass else: self.__unhandled_event(event_type) elif self.state == State.CONNECTED: if event_type == Event.REMOVE: self.__remove() self.reply(MessageType.REMOVE_POINT_OK) elif event_type == Event.TIMER: self.__point.on_timer() elif event_type == "SEND_DTMF": self.__point.send_dtmf(kwargs["dtmf"]) else: self.__unhandled_event(event_type) else: self.__unhandled_event(event_type)
def start(self): LOG.debug("RtmpMediaPoint %s: starting", self.__point_id) assert self.__profile # # Currently we accept points with local media description set before start # assert self.__local_media_description media_type = self.__local_media_description.media_type self.__rtmp_frontend = self.__transcoding_factory.create_rtmp_frontend( media_type, self.__profile) assert isinstance(self.__rtmp_frontend, IRtmpFrontend) # fill local sdp with opened ports LOG.debug("RtmpMediaPoint: pub: %s, sub: %s", self.__rtmp_frontend.pub, self.__rtmp_frontend.sub) self.__local_media_description.raw_media.attributes.append( Attribute("rtmp-sub", StrAttributeValue(self.__rtmp_frontend.sub))) self.__local_media_description.raw_media.attributes.append( Attribute("rtmp-pub", StrAttributeValue(self.__rtmp_frontend.pub))) if self.__listener is not None: self.__listener.media_point_frontend_ready(self)
def __add_point_to_pipeline(self, media_point): LOG.debug("MediaRoom.add_point %s", media_point.point_id) assert isinstance(media_point, IMediaPoint) media_point.set_context(self.__context)
def __unhandled_event(self, event_type): LOG.warning("PointController: Unhandled event " + repr(event_type) + " in state " + repr(self.state))
def __error(self, message): LOG.warning("PointController: Error: " + repr(message)) self.state = State.ERROR raise PointControllerError(message)
def __on_remote_sdp(self, sdp): assert type(sdp) is str LOG.debug("PointController: remote SDP: %s", sdp) self.__point.remote_sdp = SdpFactory.create_from_string(sdp)
def point_conn_ready(self): LOG.debug("PointController.point_conn_ready") self.event(Event.CONN_READY)
def dispose(self): LOG.debug("RtmpMediaPoint.dispose") self.__rtmp_frontend.dispose() self.__rtmp_frontend = None
def state(self, new_state): LOG.debug("PointController[%s]: transition %s -> %s", self.__point_id, self.__state, new_state) self.__state = new_state
def __remote_conn_created(self, conn): LOG.debug("SrtpMediaPoint.remote_conn_created %s", str(conn)) self.__remote_conn = conn self.__check_conn_ready()
def __on_message_remove_point(self, message): try: self.__manager.remove_point(str(message.point_id)) except ManagerError as e: LOG.warning(str(e))
def __local_rtcp_conn_created(self, conn): LOG.debug("SrtpMediaPoint.local_rtcp_conn_created %s", str(conn)) self.__local_rtcp_conn = conn self.__check_conn_ready()
self.__manager.unjoin(point_id) except ManagerError as e: LOG.warning(str(e)) def __send_dtmf(self, message): point_id = str(message.point_id) point = self.__get_point_by_id(point_id) if point is None: LOG.warning("Attempt to unjoin with unexisting point") return point.event("SEND_DTMF", dtmf=str(message.get("dtmf", ""))) if __name__ == "__main__": from a3.config import CommandLineConfig, IniConfig, DefaultConfig from a3.transcoding.gst1.factory import Gst1TranscodingFactory config = CommandLineConfig(IniConfig(DefaultConfig())) LOG.info("Config:\n%s", config) # TODO: implement configuration-based tanscoding transcoding_factory = Gst1TranscodingFactory() media_controller = MediaController(config, transcoding_factory) messaging.create(config.mq, media_controller).listen() while True: time.sleep(1) media_controller.on_timer()
def __remove_point_from_pipeline(self, media_point): LOG.debug("MediaRoom.remove_point %s", media_point.point_id) assert isinstance(media_point, IMediaPoint) media_point.set_context(None)
def create_offer(cls, cc, vv, supported_codecs): assert type(cc) is capabilities.Cc assert type(vv) is capabilities.Vv assert isinstance(supported_codecs, collections.Iterable) sdp = SessionDescription() if cc.bundle: sdp.group_semantics = GroupSemantics.BUNDLE for media_type in vv.media_types: assert media_type in [MediaType.AUDIO, MediaType.VIDEO] codecs = [ codec for codec in cc.get_codecs(media_type) if codec in supported_codecs ] LOG.debug( "Cc: Creating media description (%s) based on codecs: %s", str(media_type), ",".join([str(codec) for codec in codecs])) raw_media = raw.media.Media(media_type=media_type) media = sdp.add_raw_media(raw_media) if cc.rtcp_mux: media.rtcp_mux = True for codec in codecs: media.add_codec(codec) media.raw_media.media_description.proto = cc.profile if cc.profile == MediaDescriptionProto.RTP_SAVPF: media.crypto.generate_AES_CM_128_HMAC_SHA1_80() if cc.ssrc_required: media.direction = SdpDirection.RECV_ONLY if cc.ice: media.raw_media.connection_data = ConnectionData( NetType.IN, AddrType.IP4, "0.0.0.0") media.rtcp.port = 1 media.rtcp.connection_address = "0.0.0.0" media.ice.generate_google_ice() if media_type is MediaType.AUDIO: # # some hacks # #media.packet_time = 20 #media.silence_supp_enable = False #media.codecs.add(Codec("telephone-event", 8000)) pass # fmtp:101 0-15 #if self._ice: # #sdp.host = None # ??? ERROR??? # ice_ufrag, ice_pwd = gen_ice_pair() # sdp.set_ice(ice_ufrag, ice_pwd, "google-ice") #else: # sdp.host = host # http://code.google.com/p/webrtc/issues/detail?id=757 # As per the latest draft when you use BUNDLE in offer/answer than rtcp-mux attribute must be specified. # In your case you are using BUNDLE in answer without rtcp-mux. Please refer to section 6.1 in the below draft. #http://datatracker.ietf.org/doc/draft-ietf-mmusic-sdp-bundle-negotiation/?include_text=1 # #if cc.rtcp_mux: # sdp.group_bundle() return sdp
def __on_message_unjoin(self, message): try: point_id = str(message.point_id) self.__manager.unjoin(point_id) except ManagerError as e: LOG.warning(str(e))