Beispiel #1
0
    def preroll_done(self):
        if self.rewrite:
            gps = [b[-1][0] for b in self.bufs.values() if b]
            if gps:
                self.base_gp = max(gps)

        log.debug('preroll_done: buf lens: %r, rewr: %r (%r), mark: %r',
                  [(t, len(b)) for (t, b) in self.bufs.items()], self.rewrite,
                  self.base_gp, self.mark)

        for type, mark in ((chunks.MSG_VIDEO, self.mark),
                           (chunks.MSG_AUDIO, False)):
            frames = self.bufs.get(type)
            if mark:
                # TODO: use correct codec id for the info markers,
                #       not always "7"
                self.nstream.send(0, t, vb('\x57\x00'))
                if frames:
                    if self.rewrite:
                        self._send_many_zero_gp(type, frames)
                    else:
                        self._send_many(type, frames)
                    frames.clear()
                self.nstream.send(0, t, vb('\x57\x01'))
            elif frames:
                if self.rewrite:
                    self._send_many_zero_gp(type, frames)
                else:
                    self._send_many(type, frames)
                frames.clear()

        self.prerolling = False

        log.debug('preroll_done: done.')
Beispiel #2
0
    def preroll_done(self):
        if self.rewrite:
            gps = [b[-1][0] for b in self.bufs.values() if b]
            if gps:
                self.base_gp = max(gps)

        log.debug('preroll_done: buf lens: %r, rewr: %r (%r), mark: %r',
                  [(t, len(b)) for (t, b) in self.bufs.items()], self.rewrite,
                  self.base_gp, self.mark)

        for type, mark in ((chunks.MSG_VIDEO, self.mark),
                           (chunks.MSG_AUDIO, False)):
            frames = self.bufs.get(type)
            if mark:
                # TODO: use correct codec id for the info markers,
                #       not always "7"
                self.nstream.send(0, t, vb('\x57\x00'))
                if frames:
                    if self.rewrite:
                        self._send_many_zero_gp(type, frames)
                    else:
                        self._send_many(type, frames)
                    frames.clear()
                self.nstream.send(0, t, vb('\x57\x01'))
            elif frames:
                if self.rewrite:
                    self._send_many_zero_gp(type, frames)
                else:
                    self._send_many(type, frames)
                frames.clear()

        self.prerolling = False

        log.debug('preroll_done: done.')
Beispiel #3
0
    def _connect_succeeded(self, result):
        self._connected = True

        sm = self.muxer.sendMessage
        sm(0, chunks.PROTO_WINDOW_SIZE, 0, vb('002625a0'.decode('hex')))
        sm(0, chunks.PROTO_SET_BANDWIDTH, 0, vb('002625a002'.decode('hex')))
        sm(0, chunks.PROTO_USER_CONTROL, 0, vb('000000000000'.decode('hex')))

        return result
Beispiel #4
0
    def _init_connect(self):
        # why change chunk size? :/
        sm = self.muxer.sendMessage
        sm(0, chunks.PROTO_SET_CHUNK_SIZE, 0, vb('00000400'.decode('hex')))
        self.muxer.set_chunk_size(0x0400)

        app_path = self.factory.get_connect_app_path()
        app_url = self.factory.get_connect_url()

        self._app = self.factory.make_app(self)

        def _connected(info):
            log.debug('_connected: %r', info)
            self._app.makeConnection(info)

        def _translate_failure(failure):
            failure.trap(CommandResultError)
            # for the moment we know we tried to connect, hence ConnectError
            # TODO: add a more fine-grained translation(?)
            return Failure(ClientConnectError(*failure.value.args))

        params = self.make_connect_params(app=app_path,
                                          # flashVer='LNX 10,0,22,87',
                                          flashVer=CLIENT_VERSION,
                                          tcUrl=app_url,
                                          objectEncoding=0)
        extra_params = self._app.get_connect_extra()
        log.debug('invoking connect(%r, extra=%r)' % (params, extra_params))
        d = self.callRemote(0, 'connect', params, *extra_params)
        d.addErrback(_translate_failure)
        d.addCallbacks(_connected, self._connect_call_failed)
        d.addErrback(self._failed_disconnect)
Beispiel #5
0
def muxer_messages(mux):
    return [
        (m[0], m[1], m[2],
         (decode_amf(vb(m[3])) if m[1] in (chunks.MSG_COMMAND,
                                           chunks.MSG_DATA) else m[3]), m[4])
        for m in mux.messages
    ]
Beispiel #6
0
def muxer_messages(mux):
    return [(m[0], m[1], m[2],
             (decode_amf(vb(m[3]))
              if m[1] in (chunks.MSG_COMMAND, chunks.MSG_DATA)
              else m[3]),
             m[4])
            for m in mux.messages]
Beispiel #7
0
    def _init_connect(self):
        # why change chunk size? :/
        sm = self.muxer.sendMessage
        sm(0, chunks.PROTO_SET_CHUNK_SIZE, 0, vb('00000400'.decode('hex')))
        self.muxer.set_chunk_size(0x0400)

        app_path = self.factory.get_connect_app_path()
        app_url = self.factory.get_connect_url()

        self._app = self.factory.make_app(self)

        def _connected(info):
            log.debug('_connected: %r', info)
            self._app.makeConnection(info)

        def _translate_failure(failure):
            failure.trap(CommandResultError)
            # for the moment we know we tried to connect, hence ConnectError
            # TODO: add a more fine-grained translation(?)
            return Failure(ClientConnectError(*failure.value.args))

        params = self.make_connect_params(app=app_path,
                                          # flashVer='LNX 10,0,22,87',
                                          flashVer=CLIENT_VERSION,
                                          tcUrl=app_url,
                                          objectEncoding=0)
        log.debug('invoking connect(%r)', params)
        d = self.callRemote(0, 'connect', params, {})
        d.addErrback(_translate_failure)
        d.addCallbacks(_connected, self._connect_call_failed)
        d.addErrback(self._failed_disconnect)
Beispiel #8
0
    def test_callRemote_disconnect(self):
        p, t, dmx, mux = self.build_proto()

        d = p.callRemote(1, 'echo', 'sing it back')

        self.assertEquals(len(mux.messages), 1)
        msg = mux.messages[0]
        self.assertEquals(msg[0:3] + msg[4:],
                          (0, chunks.MSG_COMMAND, 1, False))

        # can't rely on transaction number, so we skip it
        decoded = decode_amf(vb(msg[3]))
        self.assertEquals((decoded[0], decoded[2]), ('echo', 'sing it back'))

        p.connectionLost()

        self.assertFailure(d, error.ConnectionDone)
        return d
Beispiel #9
0
    def test_callRemote_disconnect(self):
        p, t, dmx, mux = self.build_proto()

        d = p.callRemote(1, 'echo', 'sing it back')

        self.assertEquals(len(mux.messages), 1)
        msg = mux.messages[0]
        self.assertEquals(msg[0:3] + msg[4:],
                          (0, chunks.MSG_COMMAND, 1, False))

        # can't rely on transaction number, so we skip it
        decoded = decode_amf(vb(msg[3]))
        self.assertEquals((decoded[0], decoded[2]), ('echo', 'sing it back'))

        p.connectionLost()

        self.assertFailure(d, error.ConnectionDone)
        return d
Beispiel #10
0
    def test_callRemote_result(self):
        p, t, dmx, mux = self.build_proto()

        d = p.callRemote(1, 'echo', 'sing it back')

        self.assertEquals(len(mux.messages), 1)
        msg = mux.messages[0]
        self.assertEquals(msg[0:3] + msg[4:],
                          (0, chunks.MSG_COMMAND, 1, False))

        # can't rely on transaction number, so we skip it
        decoded = decode_amf(vb(msg[3]))
        self.assertEquals((decoded[0], decoded[2]), ('echo', 'sing it back'))

        dmx.inject(3, 0, const.RTMP_COMMAND, 1,
                   encode_amf('_result', decoded[1], None, decoded[2]))

        d.addCallback(self.assertEquals, (None, 'sing it back'))

        return d
Beispiel #11
0
    def test_callRemote_result(self):
        p, t, dmx, mux = self.build_proto()

        d = p.callRemote(1, 'echo', 'sing it back')

        self.assertEquals(len(mux.messages), 1)
        msg = mux.messages[0]
        self.assertEquals(msg[0:3] + msg[4:],
                          (0, chunks.MSG_COMMAND, 1, False))

        # can't rely on transaction number, so we skip it
        decoded = decode_amf(vb(msg[3]))
        self.assertEquals((decoded[0], decoded[2]), ('echo', 'sing it back'))

        dmx.inject(3, 0, const.RTMP_COMMAND, 1,
                   encode_amf('_result', decoded[1], None, decoded[2]))

        d.addCallback(self.assertEquals, (None, 'sing it back'))

        return d
Beispiel #12
0
    def test_callRemote_error(self):
        p, t, dmx, mux = self.build_proto()

        d = p.callRemote(1, 'echo', 'sing it back')

        self.assertEquals(len(mux.messages), 1)
        msg = mux.messages[0]
        self.assertEquals(msg[0:3] + msg[4:],
                          (0, chunks.MSG_COMMAND, 1, False))

        # can't rely on transaction number, so we skip it
        decoded = decode_amf(vb(msg[3]))
        self.assertEquals((decoded[0], decoded[2]), ('echo', 'sing it back'))

        err_info = Object(code='Failed', level='error', desc='no echo method')
        dmx.inject(3, 0, const.RTMP_COMMAND, 1,
                   encode_amf('_error', decoded[1], None, err_info))

        self.assertFailure(d, CommandResultError)
        d.addCallback(lambda r: self.assertEquals(r.args, (None, err_info)))

        return d
Beispiel #13
0
    def test_callRemote_error(self):
        p, t, dmx, mux = self.build_proto()

        d = p.callRemote(1, 'echo', 'sing it back')

        self.assertEquals(len(mux.messages), 1)
        msg = mux.messages[0]
        self.assertEquals(msg[0:3] + msg[4:],
                          (0, chunks.MSG_COMMAND, 1, False))

        # can't rely on transaction number, so we skip it
        decoded = decode_amf(vb(msg[3]))
        self.assertEquals((decoded[0], decoded[2]), ('echo', 'sing it back'))

        err_info = Object(code='Failed', level='error', desc='no echo method')
        dmx.inject(3, 0, const.RTMP_COMMAND, 1,
                   encode_amf('_error', decoded[1], None, err_info))

        self.assertFailure(d, CommandResultError)
        d.addCallback(lambda r: self.assertEquals(r.args, (None, err_info)))

        return d
Beispiel #14
0
 def doUserControlPing(self, header, peer_time):
     sm = self.protocol.muxer.sendMessage
     sm(0, chunks.PROTO_USER_CONTROL, 0,
        vb(_s_uctrl_single.pack(const.UCTRL_PONG, peer_time)))
Beispiel #15
0
    def on_data(self, ts, type_, data):
        stream = self._tracks.get(type_, None)

        if not stream:
            d = self._make_stream(ts, type_)
            if not d:
                return
            # FIXME: there should really be a queue between nstream and sg
            # instead, for the moment, we'll just re-try the on_data()
            d.addCallback(lambda _: self.on_data(ts, type_, data))
            return

        flags = 0
        if type_ == chunks.MSG_VIDEO:
            bytes = len(data)
            frame_type, codec_id, h264_type = None, None, None

            if bytes > 1:
                ft_codec, h264_type = _s_double_uchar.unpack(data.peek(2))
                frame_type, codec_id = ft_codec >> 4, ft_codec & 0x0f
            elif bytes > 0:
                ft_codec, = _s_uchar.unpack(data.peek(1))
                frame_type, codec_id = ft_codec >> 4, ft_codec & 0x0f

            if frame_type == 1 and codec_id == 7 and h264_type == 0:
                d = stream.write_headers(data)
                return
            elif frame_type is not None:
                if frame_type == 1:
                    flags = FF_KEYFRAME
                else:           # FIXME: this is not necessarily correct
                    flags = FF_INTERFRAME
        elif type_ == chunks.MSG_AUDIO:
            bytes = len(data)
            codec_id, aac_type = None, None

            if bytes > 1:
                ft_codec, aac_type = _s_double_uchar.unpack(data.peek(2))
                codec_id = ft_codec >> 4
            elif bytes > 0:
                ft_codec, = _s_uchar.unpack(data.peek(1))
                codec_id = ft_codec >> 4

            if codec_id == 10 and aac_type == 0:
                self._audio_headers += 1
                d = stream.write_headers(data)
                return
            elif codec_id != 10 and self._audio_headers == 0:
                # Flash, doesn't use real headers for those formats,
                # but let's mark that there is an audio track with an
                # empty packet (Flash doesn't seem to mind those)
                # early on
                self._audio_headers += 1
                d = stream.write_headers(vb(data.peek(1)))

            if codec_id is not None:
                flags = FF_KEYFRAME # audio usually is all keyframes
        else:
            log.error('Unsupported data type: %r', type_)
            return

        d = stream.write(ts, flags, data)
Beispiel #16
0
 def sinject(self, cs_id, time, type_, ms_id, data):
     body = vb(data)
     self.inject(cs_id, time, type_, ms_id, body)
Beispiel #17
0
 def doUserControlPing(self, header, peer_time):
     sm = self.protocol.muxer.sendMessage
     sm(0, chunks.PROTO_USER_CONTROL, 0,
        vb(_s_uctrl_single.pack(const.UCTRL_PONG, peer_time)))
Beispiel #18
0
 def check_send_ack(self):
     if self._next_ack < self.bytes_read:
         self.set_next_ack()
         self.muxer.sendMessage(0, chunks.PROTO_ACK, 0,
                                vb(_s_ulong.pack(self.bytes_read)))
Beispiel #19
0
    def on_data(self, ts, type_, data):
        stream = self._tracks.get(type_, None)

        if not stream:
            d = self._make_stream(ts, type_)
            if not d:
                return
            # FIXME: there should really be a queue between nstream and sg
            # instead, for the moment, we'll just re-try the on_data()
            d.addCallback(lambda _: self.on_data(ts, type_, data))
            return

        flags = 0
        if type_ == chunks.MSG_VIDEO:
            bytes = len(data)
            frame_type, codec_id, h264_type = None, None, None

            if bytes > 1:
                ft_codec, h264_type = _s_double_uchar.unpack(data.peek(2))
                frame_type, codec_id = ft_codec >> 4, ft_codec & 0x0f
            elif bytes > 0:
                ft_codec, = _s_uchar.unpack(data.peek(1))
                frame_type, codec_id = ft_codec >> 4, ft_codec & 0x0f

            if frame_type == 1 and codec_id == 7 and h264_type == 0:
                d = stream.write_headers(data)
                return
            elif frame_type is not None:
                if frame_type == 1:
                    flags = FF_KEYFRAME
                else:           # FIXME: this is not necessarily correct
                    flags = FF_INTERFRAME
        elif type_ == chunks.MSG_AUDIO:
            bytes = len(data)
            codec_id, aac_type = None, None

            if bytes > 1:
                ft_codec, aac_type = _s_double_uchar.unpack(data.peek(2))
                codec_id = ft_codec >> 4
            elif bytes > 0:
                ft_codec, = _s_uchar.unpack(data.peek(1))
                codec_id = ft_codec >> 4

            if codec_id == 10 and aac_type == 0:
                self._audio_headers += 1
                d = stream.write_headers(data)
                return
            elif codec_id != 10 and self._audio_headers == 0:
                # Flash, doesn't use real headers for those formats,
                # but let's mark that there is an audio track with an
                # empty packet (Flash doesn't seem to mind those)
                # early on
                self._audio_headers += 1
                d = stream.write_headers(vb(data.peek(1)))

            if codec_id is not None:
                flags = FF_KEYFRAME # audio usually is all keyframes
        else:
            log.error('Unsupported data type: %r', type_)
            return

        d = stream.write(ts, flags, data)
Beispiel #20
0
 def set_chunk_size(self, new_size):
     sm = self.protocol.muxer.sendMessage
     sm(0, chunks.PROTO_SET_CHUNK_SIZE, 0, vb(_s_ulong.pack(new_size)))
     self.protocol.muxer.set_chunk_size(new_size)
Beispiel #21
0
 def ctrlStreamBegin(self):
     sm = self.protocol.muxer.sendMessage
     return sm(0, chunks.PROTO_USER_CONTROL, 0,
               vb(_s_uc_single.pack(const.UCTRL_STREAM_BEGIN, self.id)))
Beispiel #22
0
 def check_send_ack(self):
     if self._next_ack < self.bytes_read:
         self.set_next_ack()
         self.muxer.sendMessage(0, chunks.PROTO_ACK, 0,
                                vb(_s_ulong.pack(self.bytes_read)))
Beispiel #23
0
 def ctrlStreamRecorded(self):
     sm = self.protocol.muxer.sendMessage
     return sm(0, chunks.PROTO_USER_CONTROL, 0,
               vb(_s_uc_single.pack(const.UCTRL_STREAM_RECORDED, self.id)))
Beispiel #24
0
 def sinject(self, cs_id, time, type_, ms_id, data):
     body = vb(data)
     self.inject(cs_id, time, type_, ms_id, body)
Beispiel #25
0
 def set_chunk_size(self, new_size):
     sm = self.protocol.muxer.sendMessage
     sm(0, chunks.PROTO_SET_CHUNK_SIZE, 0, vb(_s_ulong.pack(new_size)))
     self.protocol.muxer.set_chunk_size(new_size)