def connectionLost(self, reason): log.info('app disconnected: %s', reason.getErrorMessage()) if self._stream: self.closeStream(self._stream, force=True) self._stream = None self.publish_source = None
def play(self, net_stream, stream_name, start=-2, duration=-1, reset=True): # rtmpdump (commandline client) sends -1000 for live streams # flash based clients (flowplayer) send -2 instead assert start in (-2, -1000), 'only live streams' # ignoring duration and reset for now... log.info('opening stream %r', stream_name) def got_streamgroup(streamgroup): self.sg = streamgroup log.debug('starting playing %r', streamgroup) self.ctrl = c = RTMPPlayer(streamgroup) c.connect(net_stream) log.debug('calling c.start()...') d = c.start() def _dbg(r, msg): log.debug(msg) return r d.addCallback(_dbg, 'c.start()... done.') return d d = self.server.open(stream_name, namespace=self.ns) d.addCallback(got_streamgroup) return d
def play(self, net_stream, stream_name, start=-2, duration=-1, reset=True): assert start == -2, 'only live streams' # ignoring duration and reset for now... log.info('opening stream %r', stream_name) def got_streamgroup(streamgroup): self.sg = streamgroup log.debug('starting playing %r', streamgroup) self.ctrl = c = RTMPPlayer(streamgroup) c.connect(net_stream) log.debug('calling c.start()...') d = c.start() def _dbg(r, msg): log.debug(msg) return r d.addCallback(_dbg, 'c.start()... done.') return d d = self.server.open(stream_name, namespace=self.ns) d.addCallback(got_streamgroup) return d
def on_mute_message(self, ts, type_, do_send): log.info('on_mute_message: %s, %s, %s', ts, type_, do_send) if type_ == chunks.MSG_AUDIO: self._send_audio = do_send else: self._send_video = do_send
def publish(self, net_stream, stream_name, publish_type='live'): publish_type = 'live' if publish_type != 'live': # this app is only for live streams raise CallResultError('only live streams for now') def got_streamgroup(streamgroup): self.sg = streamgroup self.ctrl = r = RTMPRecorder(streamgroup) r.connect(net_stream) d = r.start() return d # normalize stream_name better? :/ # this just throws away url-type args: args = '' name_args = stream_name.split('?', 1) stream_name = name_args[0] if len(name_args) > 1: args = name_args[1] log.info('requested publishing of: %r with args %r', stream_name, args) d = self.server.open(stream_name, mode='l', namespace=self.ns) # AppDispatchServerProtocol should handle reporting failures # in server.open() for us... (strange idea? :/ ) d.addCallback(got_streamgroup) return d
def prepare_video(self): log.info('prepare_video, caps: "%s"', self.video_caps) if self.video_codec.codecId == 7: codec_data = self.video_caps[0]['codec_data'] self.video_avc_profile = ord(codec_data[1]) self.video_avc_level = ord(codec_data[3]) self._make_rtmp_video = self._make_rtmp_video_complex else: self._make_rtmp_video = self._make_rtmp_video_simple
def connectionLost(self, reason): log.info('app connection lost: %r (%r)', reason, self.sg) if self.ctrl: self.ctrl.stop() self.ctrl.disconnect() self.ctrl = None if self.sg: sg, self.sg = self.sg, None self.server.close(sg)
def command_onBWDone(self, ts, ms_id, trans_id, _none, bw, _ignore1=None, _ignore2=None, latency=None): log.info('Bandwidth check done (estimated bandwidth: %r, latency: %r)', bw, latency) if self._app: handler_m = None if self._app: handler_m = getattr(self._app, 'onBWDone', None) if handler_m is None: return return handler_m(ts, ms_id, trans_id, _none, bw, latency)
def _build_pipeline(self): self.make_pipeline() log.info('pipeline: %r', self.pipeline) self._caps_pending = {} if self.audio_codec: pad = self.asink.get_static_pad('sink') cid = pad.connect('notify::caps', self._got_caps_cb) self._caps_pending[pad] = (cid, 0) if self.video_codec: pad = self.vsink.get_static_pad('sink') cid = pad.connect('notify::caps', self._got_caps_cb) self._caps_pending[pad] = (cid, 1)
def connect(self, request, req_opts): log.info('connect(%r, %r)', request, req_opts) # note: order of setting attributes is important server_info = (Object(fmsVer=SERVER_VERSION_STRING) .s(capabilities=31) .s(mode=1)) status_info = (Object(level='status') .s(code='NetConnection.Connect.Success') .s(description='Connection succeeded.') .s(objectEncoding=0) .s(data={'version': '%d,%d,%d,%d' % SERVER_VERSION})) return server_info, status_info
def prepare_audio(self): log.info('prepare_audio, caps: "%s"', self.audio_caps) if self.audio_codec.codecId in (10, 11): # aac and speex are always # marked 44kHz, stereo rate = 44100 channels = 2 else: rate = self.audio_rate channels = self.channels h1 = self.audio_codec.codecId << 4 h1 |= {44100: 3, 22050: 2, 11025: 1}.get(rate, 3) << 2 h1 |= 1 << 1 # always 16-bit size h1 |= int(channels == 2) self._ah1 = h1 if self.complex_audio_header: self._header_audio = _s_double_uchar.pack(h1, 1) # h1 | "keyframe" else: self._header_audio = _s_uchar.pack(h1)
def discover_client_scheme(self, context): ts, v0, v1, v2, v3 = _s_ts_ver.unpack_from(context, 0) client_ver = (v0, v1, v2, v3) if client_ver == NO_VERSION: log.info("no client version - won't use crypto handshake") return None log.debug('client version: %s', client_ver) # non-zero version - let's find out the scheme scheme = find_client_offset_scheme(client_ver) log.debug('scheme from ver: %s', scheme) if scheme is not None: if self._check_client_scheme(scheme, context): log.debug('verified client scheme: %s', scheme) self._digest_offset_extractor = schemes[scheme][0] return scheme log.debug('trying all known schemes...') # the exact version<->scheme match didn't help - try all the # other known schemes for i in xrange(len(schemes)): if i == scheme: # we've checked that one already continue if self._check_client_scheme(i, context): log.debug('verified client scheme: %s', i) self._digest_offset_extractor = schemes[i][0] return i log.info("couldn't figure the client scheme out") if not self.strict: # Why so strict? Let's just use the lowest scheme we know log.debug('selecting scheme anyway: %s', OFFSET_SCHEME_1) return OFFSET_SCHEME_1 return None
def dataReceived(self, data): self._buf.write(data) while 1: # need to be able to retry immediately when handlers change try: while len(self._buf) >= self._toread: self._toread = self._handler.send(self._buf) break # no data, no retry except StopIteration: if self._handler_changed: self._handler_changed = False else: self._toread = None self._handler = None break except: # undocumented, but real; this way we can signal # protocol errors directly, and by not passing/raising # directly we avoid a stack trace log, with the same # effect - connectionLost f = failure.Failure() log.info(f.value, exc_info=log.isEnabledFor(logging.DEBUG)) return f
def _remote_handler_eb(self, failure, ms_id, trans_id): if log.isEnabledFor(logging.DEBUG): log.info('remote call failure: %s', failure.value, exc_info=(failure.type, failure.value, failure.getTracebackObject())) else: log.info('remote call failure: %s', failure.value) fatal = False if failure.check(CallResultError): body = self.encode_amf('_error', trans_id, *failure.value.get_error_args()) fatal = failure.value.is_fatal else: err = amf0.Object(code='NetStream.Failed', level='error', description=repr(failure.value)) body = self.encode_amf('_error', trans_id, None, err) ts = ms_time_wrapped(self.session_time()) self.muxer.sendMessage(ts, chunks.MSG_COMMAND, ms_id, body) if fatal: self.transport.loseConnection()
def log_failure(failure): # log the failure and consume it log.info(failure.value, exc_info=log.isEnabledFor(logging.DEBUG)) return None
def _fcpublish_failed(self, failure): self._state = None log.info('FCPublish failed: %r', failure.value, exc_info=log.isEnabledFor(logging.DEBUG)) return failure
def _failed_unpublishing(self, failure): log.info('failed unpublishing - Huh?!') return failure
def command_onBWDone(self, ts, ms_id, trans_id, _none, bw, _ignore1=None, _ignore2=None, latency=None): log.info('Bandwidth check done (estimated bandwidth: %r, latency: %r)', bw, latency)
def _publish_failed(self, failure): self._state = None log.info('publish failed: %r', failure) return failure
def clientConnectionLost(self, _connector, reason): log.info('lost connection: %s', reason.getErrorMessage()) if reactor.running: reactor.stop()
def command_close(self, ts, ms_id, trans_id, *args): log.info('Closing connection on server request.') self.transport.loseConnection()
def _failed_disconnect(self, failure): log.info("Failed 'connecting': %r", failure.value) self.transport.loseConnection()
def connectionMade(self, info): log.info('Yay, connected! (%r)', info) # assert server (info) supports necessary codecs? d = self.createStream() d.addCallbacks(self._start_streaming, self._disconnect)
def _bus_message_eos_cb(self, bus, message): log.info("eos, quitting") reactor.stop()
def _fail(self, msg=None): if msg is not None: log.info('ADSP._fail: %s', msg) self.transport.loseConnection()
def remote_createStream(self, ts, ms_id, _none): s = self._nsmgr.make_stream(self) log.info('created message stream: %r', s.id) return None, s.id