예제 #1
0
 def initializeCommands(self):
     self.commands = RtmpCommands()
     self.commands.register(
         "connect", self.commandConnect,
         (ARG_TYPE_OBJECT, ARG_TYPE_OPTIONAL | ARG_TYPE_BOOL))
     self.commands.register("createStream", self.commandCreateStream,
                            (ARG_TYPE_NULL, ))
     self.commands.register(
         "play", self.commandPlay,
         (ARG_TYPE_NULL, ARG_TYPE_STRING, ARG_TYPE_OPTIONAL
          | ARG_TYPE_NUMBER, ARG_TYPE_OPTIONAL | ARG_TYPE_NUMBER))
     self.commands.register("pause", self.commandPause,
                            (ARG_TYPE_NULL, ARG_TYPE_OPTIONAL
                             | ARG_TYPE_BOOL, ARG_TYPE_NUMBER))
     self.commands.register("pauseRaw", self.commandPause,
                            (ARG_TYPE_NULL, ARG_TYPE_OPTIONAL
                             | ARG_TYPE_BOOL, ARG_TYPE_NUMBER))
     self.commands.register("seek", self.commandSeek,
                            (ARG_TYPE_NULL, ARG_TYPE_NUMBER))
     self.commands.register("closeStream", self.commandCloseStream,
                            (ARG_TYPE_NULL, ARG_TYPE_NUMBER))
     self.commands.register("deleteStream", self.commandCloseStream,
                            (ARG_TYPE_NULL, ARG_TYPE_NUMBER))
예제 #2
0
 def initializeCommands(self):
     self.commands = RtmpCommands()
     self.commands.register("connect", self.commandConnect, (ARG_TYPE_OBJECT, ARG_TYPE_OPTIONAL | ARG_TYPE_BOOL))
     self.commands.register("createStream", self.commandCreateStream, (ARG_TYPE_NULL,))
     self.commands.register(
         "play",
         self.commandPlay,
         (ARG_TYPE_NULL, ARG_TYPE_STRING, ARG_TYPE_OPTIONAL | ARG_TYPE_NUMBER, ARG_TYPE_OPTIONAL | ARG_TYPE_NUMBER),
     )
     self.commands.register(
         "pause", self.commandPause, (ARG_TYPE_NULL, ARG_TYPE_OPTIONAL | ARG_TYPE_BOOL, ARG_TYPE_NUMBER)
     )
     self.commands.register(
         "pauseRaw", self.commandPause, (ARG_TYPE_NULL, ARG_TYPE_OPTIONAL | ARG_TYPE_BOOL, ARG_TYPE_NUMBER)
     )
     self.commands.register("seek", self.commandSeek, (ARG_TYPE_NULL, ARG_TYPE_NUMBER))
     self.commands.register("closeStream", self.commandCloseStream, (ARG_TYPE_NULL, ARG_TYPE_NUMBER))
     self.commands.register("deleteStream", self.commandCloseStream, (ARG_TYPE_NULL, ARG_TYPE_NUMBER))
예제 #3
0
class RtmpClient(Client):
    BUFFER_TIME_S = 3.0

    def init(self):
        self.id = "BHAIsmrI"
        self.rtmp = Rtmp.Rtmp()
        self.streams = [None]
        self.state = (STATE_WAITING_FOR_HANDSHAKE,)
        self.initializeCommands()

    def exit(self):
        for stream in self.streams:
            self.closeStream(stream)
        self.rtmp = None
        self.streams = None
        self.commands = None

    def readMessage(self, buffer):
        input = BytesInput(bytes=buffer, bigendian=True)
        length = len(buffer)
        state = self.state[0]

        # Waiting for handshake
        if state == STATE_WAITING_FOR_HANDSHAKE:
            if length < (Handshake.HANDSHAKE_SIZE + 1):
                return None
            else:
                Handshake.readWelcome(input)
                handshake = Handshake.readHandshake(input)
                self.writeMessage(Handshake.makeHandshakeResponse(handshake))
                self.state = (STATE_WAITING_FOR_HANDSHAKE_RESPONSE,)
                return (Handshake.HANDSHAKE_SIZE + 1, None)

                # Waiting for handshake response
        elif state == STATE_WAITING_FOR_HANDSHAKE_RESPONSE:
            if length < Handshake.HANDSHAKE_SIZE:
                return None
            else:
                handshake = Handshake.readHandshake(input)
                self.state = (STATE_READY,)
                return (Handshake.HANDSHAKE_SIZE, None)

                # Ready to receive message - read header
        elif state == STATE_READY:
            header_size = Rtmp.headerSizeFromByte(input.getByte())
            if length < header_size:
                return None
            else:
                header = self.rtmp.readHeader(input)
                body_length = self.rtmp.bodyLength(header, True)
                self.state = (STATE_WAITING_FOR_BODY, (header, body_length))
                return (header_size, None)

                # Waiting for message body
        elif state == STATE_WAITING_FOR_BODY:
            header, body_length = self.state[1]
            if length < body_length:
                return None
            else:
                packet = self.rtmp.readPacket(header, input)
                message = (None, (header, packet))[packet is not None]
                self.state = (STATE_READY,)
                return (body_length, message)
        else:
            return None

    def processMessage(self, message):
        header, packet = message
        type = packet[0]

        if type == Rtmp.RTMP_TYPE_COMMAND:
            name, id, args = packet[1]
            # print("COMMAND %d: %s (%s)" % (id, name, str(args))) #DEBUG
            info = (id, header)

            if not self.commands.has(name):
                self.logError("Unknown Command %d: %s (%s)" % (id, name, str(args)))
            elif not self.commands.execute(name, info, args):
                self.logError("Mismatch arguments for Command %d: %s (%s)" % (id, name, str(args)))

        elif type == Rtmp.RTMP_TYPE_CONTROL:
            stream_id, control = packet[1]
            control_type = control[0]
            if control_type == Rtmp.RTMP_CONTROL_CLIENT_BUFFER:
                stream = self.getStream(stream_id)
                if stream is not None:
                    stream.buffer_time = control[1]

    def refresh(self):
        time = getTime()
        for stream in self.streams:
            if (not self.blocking()) and (stream is not None) and (stream.stream is not None) and (stream.sleep < time):
                if stream.caching is not None:
                    self.cacheFrames(stream)
                if (stream.caching is None) and (stream.paused is None) and (not stream.stopped):
                    self.play(stream, time)

                # ** COMMANDS ** #

    def commandConnect(self, info, object, val=False):
        self.log(str(object))  # debug
        # self.log(object.get('swfUrl')[1])
        # if not self.handler.server.checkUrl(object.get('swfUrl')[1]):
        # 	raise Exception, "Invalid URL"

        self.sendServerBW()
        self.sendClientBW()

        self.sendClear()

        self.sendNetConnectionConnectSuccess(info)

        self.sendOnBWDone(info)

    def commandCreateStream(self, info, _):
        self.sendCreateStreamResponse(info, self.createStream())

    def commandCloseStream(self, info, _, stream_id):
        self.closeStream(self.getStream(stream_id))

    def commandPlay(self, info, _, file, start, length):
        stream = self.getStream(info[1][4])
        if stream is None:
            return

        self.log("%s - Playing: %s" % (self.address, file))  # debug

        stream.close()
        stream.channel_id = 5
        stream.time = getTime()

        if start is not None:
            stream.start = start
        if length is not None:
            stream.length = int(stream.start + length)

        try:
            stream.stream = Stream.createStream(stream.id, stream.channel_id, file, self.rtmp)
        except:
            self.sendNetStreamPlayStreamNotFound(stream, file)
            raise Exception, "Stream Not Found (%s)" % file

        if start > 0:
            self.seek(stream, start, BROADCAST_START)
        else:
            self.startBroadcast(stream, BROADCAST_START)

    def commandPause(self, info, _, pause=False, time=None):
        stream = self.getStream(info[1][4])
        if stream is None:
            return

        if pause == True:
            stream.paused = getTime()
        elif stream.paused is not None:
            if stream.seek_pause:
                stream.seek_pause = False
                stream.paused = None
                self.seek(stream, (stream.seek_time, time)[time is not None], BROADCAST_UNPAUSE)
            else:
                stream.start_time += getTime() - stream.paused
                stream.paused = None

    def commandSeek(self, info, _, time):
        stream = self.getStream(info[1][4])
        if stream is None:
            return

        self.seek(stream, time, BROADCAST_SEEK)

        # ** INTERNAL COMMANDS ** #

    def getStream(self, stream_id):
        try:
            return self.streams[stream_id]
        except:
            return None

    def createStream(self):
        id = 1
        for i in xrange(1, len(self.streams)):
            if self.streams[i] is None:
                break
            else:
                id += 1

        if id == len(self.streams):
            self.streams.append(None)

        self.initStream(id)
        return id

    def initStream(self, id):
        self.streams[id] = StreamInfo(id)

    def closeStream(self, stream):
        if stream is not None:
            stream.close()
            self.streams[stream.id] = None

    def printTime(self, time):
        time = int(time / 1000)
        min, sec = divmod(time, 60)
        return "%02d:%02d" % (min, sec)

    def play(self, stream, time):
        if stream.blocked is not None:
            stream.start_time += time - stream.blocked
            stream.blocked = None

        rel_time = int((time - stream.start_time) * 1000.0) + (stream.buffer_time * 2)

        while rel_time > stream.cur_time:
            frame = stream.stream.getFrame()

            if (frame is None) or ((stream.length > 0) and (frame[0] > stream.length)):
                self.stop(stream)
                return
            else:

                stream.cur_time = frame[0]
                self.writeMessage(frame[1])

                if self.blocking():
                    stream.blocked = getTime()
                    return

                    # self.sleep = getTime() + self.BUFFER_TIME_S / 2

    def seek(self, stream, time, type):
        stream.seek_time = time
        stream.seek_index = stream.stream.seek(time)

        self.startBroadcast(stream, type)

        self.cacheFrames(stream, (type == BROADCAST_START))

    def startBroadcast(self, stream, type):
        stream.stopped = False
        stream.start_time = getTime() - self.BUFFER_TIME_S - (float(stream.seek_time) / 1000.0)
        seek_time = stream.seek_time - (0, stream.cur_time)[stream.cur_time < stream.seek_time]
        stream.cur_time = stream.stream.base_time
        stream.sleep = 0.0

        start = type == BROADCAST_START
        stream_id = (None, stream.id)[start]

        self.rtmp.setChunkSize(4096)
        self.sendChunkSize(self.rtmp.getChunkSize())

        if type == BROADCAST_SEEK:
            self.sendClearPlay(stream.id)

        self.sendReset(stream.id)
        self.sendClear(stream.id)

        if type == BROADCAST_START:
            self.sendNetStreamPlayReset(stream, 0, stream_id)
        elif type == BROADCAST_SEEK:
            self.sendNetStreamSeekNotify(stream, seek_time, (stream_id, stream.id)[seek_time == stream.seek_time])
        elif type == BROADCAST_UNPAUSE:
            self.sendNetStreamUnpauseNotify(stream, 0, stream_id)

        self.sendNetStreamPlayStart(stream, 0, stream_id)

        self.sendRtmpSampleAccess(stream, stream_id)
        self.sendBlankAudioFrame(stream, stream_id)

        self.sendNetStreamDataStart(stream, stream_id)
        if type != BROADCAST_START:
            self.sendBlankVideoFrame(stream, stream_id)
        self.sendMetaData(stream, stream_id)

    def cacheFrames(self, stream, start=False):
        if stream.caching is None:
            stream.caching = [stream.seek_index, start, getTime()]
        while stream.caching is not None:
            if stream.caching[0] < stream.stream.index:
                if stream.caching[0] == stream.seek_index:
                    self.writeMessage(stream.stream.getDescription(Stream.FRAME_TYPE_VIDEO, stream.caching[1]))
                self.writeMessage(stream.stream.getVideoFrame(stream.caching[0], stream.seek_time))
                stream.caching[0] += 1
            else:
                stream.start_time += getTime() - stream.caching[2]
                stream.caching = None
                self.writeMessage(stream.stream.getSeekFrame())

                if stream.paused is not None:
                    stream.seek_pause = True
                    self.writeMessage(stream.stream.getPauseFrame())
                    self.writeMessage(stream.stream.getDescription(Stream.FRAME_TYPE_AUDIO))
                    self.sendBlankAudioFrame(stream)
                    self.sendClearPlay(stream.id)
                else:
                    self.writeMessage(stream.stream.getDescription(Stream.FRAME_TYPE_AUDIO))
                    # self.paused = getTime()
            if self.blocking():
                break

    def stop(self, stream):
        stream.stopped = True
        self.writeMessage(stream.stream.getPauseFrame())
        self.writeMessage(stream.stream.getPauseFrame())
        self.sendClearPlay(stream.id)
        self.sendNetStreamPlayStop(stream)

    def initializeCommands(self):
        self.commands = RtmpCommands()
        self.commands.register("connect", self.commandConnect, (ARG_TYPE_OBJECT, ARG_TYPE_OPTIONAL | ARG_TYPE_BOOL))
        self.commands.register("createStream", self.commandCreateStream, (ARG_TYPE_NULL,))
        self.commands.register(
            "play",
            self.commandPlay,
            (ARG_TYPE_NULL, ARG_TYPE_STRING, ARG_TYPE_OPTIONAL | ARG_TYPE_NUMBER, ARG_TYPE_OPTIONAL | ARG_TYPE_NUMBER),
        )
        self.commands.register(
            "pause", self.commandPause, (ARG_TYPE_NULL, ARG_TYPE_OPTIONAL | ARG_TYPE_BOOL, ARG_TYPE_NUMBER)
        )
        self.commands.register(
            "pauseRaw", self.commandPause, (ARG_TYPE_NULL, ARG_TYPE_OPTIONAL | ARG_TYPE_BOOL, ARG_TYPE_NUMBER)
        )
        self.commands.register("seek", self.commandSeek, (ARG_TYPE_NULL, ARG_TYPE_NUMBER))
        self.commands.register("closeStream", self.commandCloseStream, (ARG_TYPE_NULL, ARG_TYPE_NUMBER))
        self.commands.register("deleteStream", self.commandCloseStream, (ARG_TYPE_NULL, ARG_TYPE_NUMBER))

        # ** MESSAGES ** #

    def sendChunkSize(self, size):
        self.writeMessage(self.rtmp.make(2, (Rtmp.RTMP_TYPE_CHUNK_SIZE, size)))

    def sendClear(self, stream_id=0):
        self.writeMessage(self.rtmp.make(2, (Rtmp.RTMP_TYPE_CONTROL, (stream_id, (Rtmp.RTMP_CONTROL_CLEAR,)))))

    def sendClearPlay(self, stream_id=0):
        self.writeMessage(self.rtmp.make(2, (Rtmp.RTMP_TYPE_CONTROL, (stream_id, (Rtmp.RTMP_CONTROL_CLEAR_PLAY,)))))

    def sendReset(self, stream_id=0):
        self.writeMessage(self.rtmp.make(2, (Rtmp.RTMP_TYPE_CONTROL, (stream_id, (Rtmp.RTMP_CONTROL_RESET,)))))

    def sendServerBW(self, value=0):
        self.writeMessage(self.rtmp.make(2, (Rtmp.RTMP_TYPE_SERVER_BW, value)))

    def sendClientBW(self, value=0):
        self.writeMessage(self.rtmp.make(2, (Rtmp.RTMP_TYPE_CLIENT_BW, value)))

    def sendCreateStreamResponse(self, info, stream_id):
        self.writeMessage(
            self.rtmp.make(
                info[1][0], (Rtmp.RTMP_TYPE_COMMAND, ("_result", info[0], [Amf.encode(None), Amf.encode(stream_id)]))
            )
        )

    def sendNetConnectionConnectSuccess(self, info):
        self.writeMessage(
            self.rtmp.make(
                info[1][0],
                (
                    Rtmp.RTMP_TYPE_COMMAND,
                    (
                        "_result",
                        info[0],
                        [
                            Amf.encode({"fmsVer": self.handler.server.getVersion(), "capabilities": 31.0, "mode": 1}),
                            Amf.encode(
                                {
                                    "level": "status",
                                    "code": "NetConnection.Connect.Success",
                                    "description": "Connection succeeded.",
                                    "objectEncoding": 0.0,
                                }
                            ),
                        ],
                    ),
                ),
            )
        )

    def sendOnBWDone(self, info):
        self.writeMessage(self.rtmp.make(info[1][0], (Rtmp.RTMP_TYPE_COMMAND, ("onBWDone", 0, [Amf.encode(None)]))))

    def sendMetaData(self, stream, stream_id=None):
        # self.writeMessage(self.rtmp.make(stream.channel_id, (Rtmp.RTMP_TYPE_COMMAND, ("onMetaData", 0, [
        # 	Amf.encode(None),
        # 	Amf.encode(stream.stream.getMetadata())
        # ])), 0, stream_id))
        output = BytesOutput(True)
        Amf.write(output, Amf.encode("onMetaData"))
        Amf.write(output, Amf.encode(stream.stream.getMetadata()))
        self.writeMessage(self.rtmp.make(stream.channel_id, (Rtmp.RTMP_TYPE_NOTIFY, output.getBytes()), 0, stream_id))

    def sendNetStreamPlayStreamNotFound(self, stream, file):
        self.writeMessage(
            self.rtmp.make(
                stream.channel_id,
                (
                    Rtmp.RTMP_TYPE_COMMAND,
                    (
                        "onStatus",
                        0,
                        [
                            Amf.encode(None),
                            Amf.encode(
                                {
                                    "level": "status",
                                    "code": "NetStream.Play.StreamNotFound",
                                    "details": file,
                                    "clientid": self.id,
                                }
                            ),
                        ],
                    ),
                ),
                0,
                stream.id,
            )
        )

    def sendNetStreamPlayReset(self, stream, timestamp=0, stream_id=None):
        self.writeMessage(
            self.rtmp.make(
                4,
                (
                    Rtmp.RTMP_TYPE_COMMAND,
                    (
                        "onStatus",
                        0,
                        [
                            Amf.encode(None),
                            Amf.encode(
                                {
                                    "level": "status",
                                    "code": "NetStream.Play.Reset",
                                    "description": "Playing and resetting %s." % stream.stream.getPath(),
                                    "details": stream.stream.getPath(),
                                    "clientid": self.id,
                                }
                            ),
                        ],
                    ),
                ),
                timestamp,
                stream_id,
            )
        )  # 4 - stream.channel_id

    def sendNetStreamPlayStart(self, stream, timestamp=0, stream_id=None):
        self.writeMessage(
            self.rtmp.make(
                stream.channel_id,
                (
                    Rtmp.RTMP_TYPE_COMMAND,
                    (
                        "onStatus",
                        0,
                        [
                            Amf.encode(None),
                            Amf.encode(
                                {
                                    "level": "status",
                                    "code": "NetStream.Play.Start",
                                    "description": "Started playing %s." % stream.stream.getPath(),
                                    "details": stream.stream.getPath(),
                                    "clientid": self.id,
                                }
                            ),
                        ],
                    ),
                ),
                timestamp,
                stream_id,
            )
        )

    def sendNetStreamPlayStop(self, stream, timestamp=0, stream_id=None):
        self.writeMessage(
            self.rtmp.make(
                stream.channel_id,
                (
                    Rtmp.RTMP_TYPE_COMMAND,
                    (
                        "onStatus",
                        0,
                        [
                            Amf.encode(None),
                            Amf.encode(
                                {
                                    "level": "status",
                                    "code": "NetStream.Play.Stop",
                                    "description": "Stopped playing %s." % stream.stream.getPath(),
                                    "details": stream.stream.getPath(),
                                    "clientid": self.id,
                                }
                            ),
                        ],
                    ),
                ),
                timestamp,
                stream_id,
            )
        )

    def sendNetStreamUnpauseNotify(self, stream, timestamp=0, stream_id=None):
        self.writeMessage(
            self.rtmp.make(
                stream.channel_id,
                (
                    Rtmp.RTMP_TYPE_COMMAND,
                    (
                        "onStatus",
                        0,
                        [
                            Amf.encode(None),
                            Amf.encode(
                                {
                                    "level": "status",
                                    "code": "NetStream.Unpause.Notify",
                                    "description": "Unpausing %s." % stream.stream.getPath(),
                                    "details": stream.stream.getPath(),
                                    "clientid": self.id,
                                }
                            ),
                        ],
                    ),
                ),
                timestamp,
                stream_id,
            )
        )

    def sendNetStreamSeekNotify(self, stream, timestamp=0, stream_id=None):
        self.writeMessage(
            self.rtmp.make(
                stream.channel_id,
                (
                    Rtmp.RTMP_TYPE_COMMAND,
                    (
                        "onStatus",
                        0,
                        [
                            Amf.encode(None),
                            Amf.encode(
                                {
                                    "level": "status",
                                    "code": "NetStream.Seek.Notify",
                                    "description": "Seeking %d (stream ID: %d)." % (stream.seek_time, stream.id),
                                    "details": stream.stream.getPath(),
                                    "clientid": self.id,
                                }
                            ),
                        ],
                    ),
                ),
                timestamp,
                stream_id,
            )
        )

    def sendRtmpSampleAccess(self, stream, stream_id=None):
        output = BytesOutput(True)
        Amf.write(output, Amf.encode("|RtmpSampleAccess"))
        Amf.write(output, Amf.encode(False))
        Amf.write(output, Amf.encode(False))
        self.writeMessage(self.rtmp.make(5, (Rtmp.RTMP_TYPE_NOTIFY, output.getBytes()), 0, stream_id))

    def sendBlankAudioFrame(self, stream, stream_id=None):
        self.writeMessage(self.rtmp.make(stream.channel_id, (Rtmp.RTMP_TYPE_AUDIO, ""), 0, stream_id))

    def sendBlankVideoFrame(self, stream, stream_id=None):
        self.writeMessage(
            self.rtmp.make(stream.channel_id, (Rtmp.RTMP_TYPE_VIDEO, pack(">2B", 0x57, 0x00)), 0, stream_id)
        )

    def sendNetStreamDataStart(self, stream, stream_id=None):
        output = BytesOutput(True)
        Amf.write(output, Amf.encode("onStatus"))
        Amf.write(output, Amf.encode({"code": "NetStream.Data.Start"}))
        self.writeMessage(self.rtmp.make(5, (Rtmp.RTMP_TYPE_NOTIFY, output.getBytes()), 0, stream_id))

    def logError(self, msg):
        self.handler.server.logError(msg)
예제 #4
0
class RtmpClient(Client):
    BUFFER_TIME_S = 3.0

    def init(self):
        self.id = "BHAIsmrI"
        self.rtmp = Rtmp.Rtmp()
        self.streams = [None]
        self.state = (STATE_WAITING_FOR_HANDSHAKE, )
        self.initializeCommands()

    def exit(self):
        for stream in self.streams:
            self.closeStream(stream)
        self.rtmp = None
        self.streams = None
        self.commands = None

    def readMessage(self, buffer):
        input = BytesInput(bytes=buffer, bigendian=True)
        length = len(buffer)
        state = self.state[0]

        # Waiting for handshake
        if state == STATE_WAITING_FOR_HANDSHAKE:
            if length < (Handshake.HANDSHAKE_SIZE + 1):
                return None
            else:
                Handshake.readWelcome(input)
                handshake = Handshake.readHandshake(input)
                self.writeMessage(Handshake.makeHandshakeResponse(handshake))
                self.state = (STATE_WAITING_FOR_HANDSHAKE_RESPONSE, )
                return (Handshake.HANDSHAKE_SIZE + 1, None)

        # Waiting for handshake response
        elif state == STATE_WAITING_FOR_HANDSHAKE_RESPONSE:
            if length < Handshake.HANDSHAKE_SIZE:
                return None
            else:
                handshake = Handshake.readHandshake(input)
                self.state = (STATE_READY, )
                return (Handshake.HANDSHAKE_SIZE, None)

        # Ready to receive message - read header
        elif state == STATE_READY:
            header_size = Rtmp.headerSizeFromByte(input.getByte())
            if length < header_size:
                return None
            else:
                header = self.rtmp.readHeader(input)
                body_length = self.rtmp.bodyLength(header, True)
                self.state = (STATE_WAITING_FOR_BODY, (header, body_length))
                return (header_size, None)

        # Waiting for message body
        elif state == STATE_WAITING_FOR_BODY:
            header, body_length = self.state[1]
            if length < body_length:
                return None
            else:
                packet = self.rtmp.readPacket(header, input)
                message = (None, (header, packet))[packet is not None]
                self.state = (STATE_READY, )
                return (body_length, message)
        else:
            return None

    def processMessage(self, message):
        header, packet = message
        type = packet[0]

        if type == Rtmp.RTMP_TYPE_COMMAND:
            name, id, args = packet[1]
            #print("COMMAND %d: %s (%s)" % (id, name, str(args))) #DEBUG
            info = (id, header)

            if not self.commands.has(name):
                self.logError("Unknown Command %d: %s (%s)" %
                              (id, name, str(args)))
            elif not self.commands.execute(name, info, args):
                self.logError("Mismatch arguments for Command %d: %s (%s)" %
                              (id, name, str(args)))

        elif type == Rtmp.RTMP_TYPE_CONTROL:
            stream_id, control = packet[1]
            control_type = control[0]
            if control_type == Rtmp.RTMP_CONTROL_CLIENT_BUFFER:
                stream = self.getStream(stream_id)
                if stream is not None:
                    stream.buffer_time = control[1]

    def refresh(self):
        time = getTime()
        for stream in self.streams:
            if (not self.blocking()) and (stream is not None) and (
                    stream.stream is not None) and (stream.sleep < time):
                if stream.caching is not None:
                    self.cacheFrames(stream)
                if (stream.caching is None) and (stream.paused is None) and (
                        not stream.stopped):
                    self.play(stream, time)

    # ** COMMANDS ** #
    def commandConnect(self, info, object, val=False):
        self.log(str(object))  #debug
        #self.log(object.get('swfUrl')[1])
        #if not self.handler.server.checkUrl(object.get('swfUrl')[1]):
        #	raise Exception, "Invalid URL"

        self.sendServerBW()
        self.sendClientBW()

        self.sendClear()

        self.sendNetConnectionConnectSuccess(info)

        self.sendOnBWDone(info)

    def commandCreateStream(self, info, _):
        self.sendCreateStreamResponse(info, self.createStream())

    def commandCloseStream(self, info, _, stream_id):
        self.closeStream(self.getStream(stream_id))

    def commandPlay(self, info, _, file, start, length):
        stream = self.getStream(info[1][4])
        if stream is None: return

        self.log("%s - Playing: %s" % (self.address, file))  #debug

        stream.close()
        stream.channel_id = 5
        stream.time = getTime()

        if start is not None:
            stream.start = start
        if length is not None:
            stream.length = int(stream.start + length)

        try:
            stream.stream = Stream.createStream(stream.id, stream.channel_id,
                                                file, self.rtmp)
        except:
            self.sendNetStreamPlayStreamNotFound(stream, file)
            raise Exception, "Stream Not Found (%s)" % file

        if start > 0:
            self.seek(stream, start, BROADCAST_START)
        else:
            self.startBroadcast(stream, BROADCAST_START)

    def commandPause(self, info, _, pause=False, time=None):
        stream = self.getStream(info[1][4])
        if stream is None: return

        if pause == True:
            stream.paused = getTime()
        elif stream.paused is not None:
            if stream.seek_pause:
                stream.seek_pause = False
                stream.paused = None
                self.seek(stream, (stream.seek_time, time)[time is not None],
                          BROADCAST_UNPAUSE)
            else:
                stream.start_time += getTime() - stream.paused
                stream.paused = None

    def commandSeek(self, info, _, time):
        stream = self.getStream(info[1][4])
        if stream is None: return

        self.seek(stream, time, BROADCAST_SEEK)

    # ** INTERNAL COMMANDS ** #
    def getStream(self, stream_id):
        try:
            return self.streams[stream_id]
        except:
            return None

    def createStream(self):
        id = 1
        for i in xrange(1, len(self.streams)):
            if self.streams[i] is None:
                break
            else:
                id += 1

        if id == len(self.streams):
            self.streams.append(None)

        self.initStream(id)
        return id

    def initStream(self, id):
        self.streams[id] = StreamInfo(id)

    def closeStream(self, stream):
        if stream is not None:
            stream.close()
            self.streams[stream.id] = None

    def printTime(self, time):
        time = int(time / 1000)
        min, sec = divmod(time, 60)
        return "%02d:%02d" % (min, sec)

    def play(self, stream, time):
        if stream.blocked is not None:
            stream.start_time += time - stream.blocked
            stream.blocked = None

        rel_time = int(
            (time - stream.start_time) * 1000.0) + (stream.buffer_time * 2)

        while rel_time > stream.cur_time:
            frame = stream.stream.getFrame()

            if (frame is None) or ((stream.length > 0) and
                                   (frame[0] > stream.length)):
                self.stop(stream)
                return
            else:

                stream.cur_time = frame[0]
                self.writeMessage(frame[1])

                if self.blocking():
                    stream.blocked = getTime()
                    return

        #self.sleep = getTime() + self.BUFFER_TIME_S / 2

    def seek(self, stream, time, type):
        stream.seek_time = time
        stream.seek_index = stream.stream.seek(time)

        self.startBroadcast(stream, type)

        self.cacheFrames(stream, (type == BROADCAST_START))

    def startBroadcast(self, stream, type):
        stream.stopped = False
        stream.start_time = getTime() - self.BUFFER_TIME_S - (
            float(stream.seek_time) / 1000.0)
        seek_time = stream.seek_time - (
            0, stream.cur_time)[stream.cur_time < stream.seek_time]
        stream.cur_time = stream.stream.base_time
        stream.sleep = 0.0

        start = (type == BROADCAST_START)
        stream_id = (None, stream.id)[start]

        self.rtmp.setChunkSize(4096)
        self.sendChunkSize(self.rtmp.getChunkSize())

        if type == BROADCAST_SEEK:
            self.sendClearPlay(stream.id)

        self.sendReset(stream.id)
        self.sendClear(stream.id)

        if type == BROADCAST_START:
            self.sendNetStreamPlayReset(stream, 0, stream_id)
        elif type == BROADCAST_SEEK:
            self.sendNetStreamSeekNotify(
                stream, seek_time,
                (stream_id, stream.id)[seek_time == stream.seek_time])
        elif type == BROADCAST_UNPAUSE:
            self.sendNetStreamUnpauseNotify(stream, 0, stream_id)

        self.sendNetStreamPlayStart(stream, 0, stream_id)

        self.sendRtmpSampleAccess(stream, stream_id)
        self.sendBlankAudioFrame(stream, stream_id)

        self.sendNetStreamDataStart(stream, stream_id)
        if type != BROADCAST_START:
            self.sendBlankVideoFrame(stream, stream_id)
        self.sendMetaData(stream, stream_id)

    def cacheFrames(self, stream, start=False):
        if stream.caching is None:
            stream.caching = [stream.seek_index, start, getTime()]
        while stream.caching is not None:
            if stream.caching[0] < stream.stream.index:
                if stream.caching[0] == stream.seek_index:
                    self.writeMessage(
                        stream.stream.getDescription(Stream.FRAME_TYPE_VIDEO,
                                                     stream.caching[1]))
                self.writeMessage(
                    stream.stream.getVideoFrame(stream.caching[0],
                                                stream.seek_time))
                stream.caching[0] += 1
            else:
                stream.start_time += getTime() - stream.caching[2]
                stream.caching = None
                self.writeMessage(stream.stream.getSeekFrame())

                if stream.paused is not None:
                    stream.seek_pause = True
                    self.writeMessage(stream.stream.getPauseFrame())
                    self.writeMessage(
                        stream.stream.getDescription(Stream.FRAME_TYPE_AUDIO))
                    self.sendBlankAudioFrame(stream)
                    self.sendClearPlay(stream.id)
                else:
                    self.writeMessage(
                        stream.stream.getDescription(Stream.FRAME_TYPE_AUDIO))
                    #self.paused = getTime()
            if self.blocking():
                break

    def stop(self, stream):
        stream.stopped = True
        self.writeMessage(stream.stream.getPauseFrame())
        self.writeMessage(stream.stream.getPauseFrame())
        self.sendClearPlay(stream.id)
        self.sendNetStreamPlayStop(stream)

    def initializeCommands(self):
        self.commands = RtmpCommands()
        self.commands.register(
            "connect", self.commandConnect,
            (ARG_TYPE_OBJECT, ARG_TYPE_OPTIONAL | ARG_TYPE_BOOL))
        self.commands.register("createStream", self.commandCreateStream,
                               (ARG_TYPE_NULL, ))
        self.commands.register(
            "play", self.commandPlay,
            (ARG_TYPE_NULL, ARG_TYPE_STRING, ARG_TYPE_OPTIONAL
             | ARG_TYPE_NUMBER, ARG_TYPE_OPTIONAL | ARG_TYPE_NUMBER))
        self.commands.register("pause", self.commandPause,
                               (ARG_TYPE_NULL, ARG_TYPE_OPTIONAL
                                | ARG_TYPE_BOOL, ARG_TYPE_NUMBER))
        self.commands.register("pauseRaw", self.commandPause,
                               (ARG_TYPE_NULL, ARG_TYPE_OPTIONAL
                                | ARG_TYPE_BOOL, ARG_TYPE_NUMBER))
        self.commands.register("seek", self.commandSeek,
                               (ARG_TYPE_NULL, ARG_TYPE_NUMBER))
        self.commands.register("closeStream", self.commandCloseStream,
                               (ARG_TYPE_NULL, ARG_TYPE_NUMBER))
        self.commands.register("deleteStream", self.commandCloseStream,
                               (ARG_TYPE_NULL, ARG_TYPE_NUMBER))

    # ** MESSAGES ** #
    def sendChunkSize(self, size):
        self.writeMessage(self.rtmp.make(2, (Rtmp.RTMP_TYPE_CHUNK_SIZE, size)))

    def sendClear(self, stream_id=0):
        self.writeMessage(
            self.rtmp.make(2, (Rtmp.RTMP_TYPE_CONTROL,
                               (stream_id, (Rtmp.RTMP_CONTROL_CLEAR, )))))

    def sendClearPlay(self, stream_id=0):
        self.writeMessage(
            self.rtmp.make(2, (Rtmp.RTMP_TYPE_CONTROL,
                               (stream_id, (Rtmp.RTMP_CONTROL_CLEAR_PLAY, )))))

    def sendReset(self, stream_id=0):
        self.writeMessage(
            self.rtmp.make(2, (Rtmp.RTMP_TYPE_CONTROL,
                               (stream_id, (Rtmp.RTMP_CONTROL_RESET, )))))

    def sendServerBW(self, value=0):
        self.writeMessage(self.rtmp.make(2, (Rtmp.RTMP_TYPE_SERVER_BW, value)))

    def sendClientBW(self, value=0):
        self.writeMessage(self.rtmp.make(2, (Rtmp.RTMP_TYPE_CLIENT_BW, value)))

    def sendCreateStreamResponse(self, info, stream_id):
        self.writeMessage(
            self.rtmp.make(
                info[1][0],
                (Rtmp.RTMP_TYPE_COMMAND,
                 ("_result", info[0],
                  [Amf.encode(None), Amf.encode(stream_id)]))))

    def sendNetConnectionConnectSuccess(self, info):
        self.writeMessage(
            self.rtmp.make(
                info[1][0], (Rtmp.RTMP_TYPE_COMMAND, ("_result", info[0], [
                    Amf.encode({
                        'fmsVer': self.handler.server.getVersion(),
                        'capabilities': 31.0,
                        'mode': 1
                    }),
                    Amf.encode({
                        'level': "status",
                        'code': "NetConnection.Connect.Success",
                        'description': "Connection succeeded.",
                        'objectEncoding': 0.0
                    })
                ]))))

    def sendOnBWDone(self, info):
        self.writeMessage(
            self.rtmp.make(info[1][0], (Rtmp.RTMP_TYPE_COMMAND,
                                        ("onBWDone", 0, [Amf.encode(None)]))))

    def sendMetaData(self, stream, stream_id=None):
        #self.writeMessage(self.rtmp.make(stream.channel_id, (Rtmp.RTMP_TYPE_COMMAND, ("onMetaData", 0, [
        #	Amf.encode(None),
        #	Amf.encode(stream.stream.getMetadata())
        #])), 0, stream_id))
        output = BytesOutput(True)
        Amf.write(output, Amf.encode("onMetaData"))
        Amf.write(output, Amf.encode(stream.stream.getMetadata()))
        self.writeMessage(
            self.rtmp.make(stream.channel_id,
                           (Rtmp.RTMP_TYPE_NOTIFY, output.getBytes()), 0,
                           stream_id))

    def sendNetStreamPlayStreamNotFound(self, stream, file):
        self.writeMessage(
            self.rtmp.make(stream.channel_id,
                           (Rtmp.RTMP_TYPE_COMMAND, ("onStatus", 0, [
                               Amf.encode(None),
                               Amf.encode({
                                   'level': "status",
                                   'code': "NetStream.Play.StreamNotFound",
                                   'details': file,
                                   'clientid': self.id
                               })
                           ])), 0, stream.id))

    def sendNetStreamPlayReset(self, stream, timestamp=0, stream_id=None):
        self.writeMessage(
            self.rtmp.make(4, (Rtmp.RTMP_TYPE_COMMAND, ("onStatus", 0, [
                Amf.encode(None),
                Amf.encode({
                    'level':
                    "status",
                    'code':
                    "NetStream.Play.Reset",
                    'description':
                    "Playing and resetting %s." % stream.stream.getPath(),
                    'details':
                    stream.stream.getPath(),
                    'clientid':
                    self.id
                })
            ])), timestamp, stream_id))  #4 - stream.channel_id

    def sendNetStreamPlayStart(self, stream, timestamp=0, stream_id=None):
        self.writeMessage(
            self.rtmp.make(
                stream.channel_id, (Rtmp.RTMP_TYPE_COMMAND, ("onStatus", 0, [
                    Amf.encode(None),
                    Amf.encode({
                        'level':
                        "status",
                        'code':
                        "NetStream.Play.Start",
                        'description':
                        "Started playing %s." % stream.stream.getPath(),
                        'details':
                        stream.stream.getPath(),
                        'clientid':
                        self.id
                    })
                ])), timestamp, stream_id))

    def sendNetStreamPlayStop(self, stream, timestamp=0, stream_id=None):
        self.writeMessage(
            self.rtmp.make(
                stream.channel_id, (Rtmp.RTMP_TYPE_COMMAND, ("onStatus", 0, [
                    Amf.encode(None),
                    Amf.encode({
                        'level':
                        "status",
                        'code':
                        "NetStream.Play.Stop",
                        'description':
                        "Stopped playing %s." % stream.stream.getPath(),
                        'details':
                        stream.stream.getPath(),
                        'clientid':
                        self.id
                    })
                ])), timestamp, stream_id))

    def sendNetStreamUnpauseNotify(self, stream, timestamp=0, stream_id=None):
        self.writeMessage(
            self.rtmp.make(stream.channel_id,
                           (Rtmp.RTMP_TYPE_COMMAND, ("onStatus", 0, [
                               Amf.encode(None),
                               Amf.encode({
                                   'level':
                                   "status",
                                   'code':
                                   "NetStream.Unpause.Notify",
                                   'description':
                                   "Unpausing %s." % stream.stream.getPath(),
                                   'details':
                                   stream.stream.getPath(),
                                   'clientid':
                                   self.id
                               }),
                           ])), timestamp, stream_id))

    def sendNetStreamSeekNotify(self, stream, timestamp=0, stream_id=None):
        self.writeMessage(
            self.rtmp.make(stream.channel_id,
                           (Rtmp.RTMP_TYPE_COMMAND, ("onStatus", 0, [
                               Amf.encode(None),
                               Amf.encode({
                                   'level':
                                   "status",
                                   'code':
                                   "NetStream.Seek.Notify",
                                   'description':
                                   "Seeking %d (stream ID: %d)." %
                                   (stream.seek_time, stream.id),
                                   'details':
                                   stream.stream.getPath(),
                                   'clientid':
                                   self.id
                               })
                           ])), timestamp, stream_id))

    def sendRtmpSampleAccess(self, stream, stream_id=None):
        output = BytesOutput(True)
        Amf.write(output, Amf.encode("|RtmpSampleAccess"))
        Amf.write(output, Amf.encode(False))
        Amf.write(output, Amf.encode(False))
        self.writeMessage(
            self.rtmp.make(5, (Rtmp.RTMP_TYPE_NOTIFY, output.getBytes()), 0,
                           stream_id))

    def sendBlankAudioFrame(self, stream, stream_id=None):
        self.writeMessage(
            self.rtmp.make(stream.channel_id, (Rtmp.RTMP_TYPE_AUDIO, ''), 0,
                           stream_id))

    def sendBlankVideoFrame(self, stream, stream_id=None):
        self.writeMessage(
            self.rtmp.make(stream.channel_id,
                           (Rtmp.RTMP_TYPE_VIDEO, pack(">2B", 0x57, 0x00)), 0,
                           stream_id))

    def sendNetStreamDataStart(self, stream, stream_id=None):
        output = BytesOutput(True)
        Amf.write(output, Amf.encode("onStatus"))
        Amf.write(output, Amf.encode({"code": "NetStream.Data.Start"}))
        self.writeMessage(
            self.rtmp.make(5, (Rtmp.RTMP_TYPE_NOTIFY, output.getBytes()), 0,
                           stream_id))

    def logError(self, msg):
        self.handler.server.logError(msg)