Metadata-Version: 2.1
Name: pyrtmp
Version: 0.1.5
Summary: Pure python RTMP server
Home-page: https://github.com/Eittipat/pyrtmp.git
Author: Eittipat.K
Author-email: iammop@gmail.com
License: MIT
Download-URL: https://github.com/Eittipat/pyrtmp/releases/tag/v0.1.5
Keywords: RTMP,RTMPT,asyncio
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Build Tools
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3
Description-Content-Type: text/markdown
License-File: LICENSE.txt

# pyrtmp: Pure Python RTMP server    
  
- Pure python  
- AsyncIO with uvloop support  
- Easy to customize  
- Support RTMP(s)  
- Support RTMPT(s)  
  
## Quickstart  
  
You have to create your own rtmp controller to decide what to do when user connected or stream received.  The below example shows process to receive stream and write them to flv file using RTMP.

If you are looking for RTMPT, please look inside [pyrtmp/misc/rtmpt.py](https://github.com/Eittipat/pyrtmp/blob/master/pyrtmp/misc/rtmpt.py)

P.S. Pull requests are welcome!

[*Simple RTMP controller*](https://github.com/Eittipat/pyrtmp/blob/master/pyrtmp/rtmp.py)


    import asyncio  
    import logging  
    import os  
    import tempfile  
      
    from pyrtmp import StreamClosedException, RTMPProtocol  
    from pyrtmp.messages import SessionManager  
    from pyrtmp.messages.audio import AudioMessage  
    from pyrtmp.messages.command import NCConnect, NCCreateStream, NSPublish, NSCloseStream, NSDeleteStream  
    from pyrtmp.messages.data import MetaDataMessage  
    from pyrtmp.messages.protocolcontrol import WindowAcknowledgementSize, SetChunkSize, SetPeerBandwidth  
    from pyrtmp.messages.usercontrol import StreamBegin  
    from pyrtmp.messages.video import VideoMessage  
    from pyrtmp.misc.flvdump import FLVStream, FLVMediaType  
      
    logging.basicConfig(level=logging.DEBUG)  
    logger = logging.getLogger(__name__)  
    logger.setLevel(logging.DEBUG)  
      
      
    async def simple_controller(reader, writer):  
        session = SessionManager(reader=reader, writer=writer)  
        flv = None  
        try:  
            logger.debug(f'Client connected {session.peername}')  
      
            # do handshake  
            await session.handshake()  
      
            # read chunks  
            async for chunk in session.read_chunks_from_stream():
                message = chunk.as_message()  
                logger.debug(f"Receiving {str(message)} {message.chunk_id}")  
                if isinstance(message, NCConnect):  
                    session.write_chunk_to_stream(WindowAcknowledgementSize(ack_window_size=5000000))  
                    session.write_chunk_to_stream(SetPeerBandwidth(ack_window_size=5000000, limit_type=2))  
                    session.write_chunk_to_stream(StreamBegin(stream_id=0))  
                    session.write_chunk_to_stream(SetChunkSize(chunk_size=8192))  
                    session.writer_chunk_size = 8192  
                    session.write_chunk_to_stream(message.create_response())  
                    await session.drain()  
                    logger.debug("Response to NCConnect")  
                elif isinstance(message, WindowAcknowledgementSize):  
                    pass  
                elif isinstance(message, NCCreateStream):  
                    session.write_chunk_to_stream(message.create_response())  
                    await session.drain()  
                    logger.debug("Response to NCCreateStream")  
                elif isinstance(message, NSPublish):  
                    # create flv file at temp  
                    flv = FLVStream(os.path.join(tempfile.gettempdir(), message.publishing_name))  
                    session.write_chunk_to_stream(StreamBegin(stream_id=1))  
                    session.write_chunk_to_stream(message.create_response())  
                    await session.drain()  
                    logger.debug("Response to NSPublish")  
                elif isinstance(message, MetaDataMessage):  
                    # Write meta data to file  
                    flv.write(0, message.to_raw_meta(), FLVMediaType.OBJECT)  
                elif isinstance(message, SetChunkSize):  
                    session.reader_chunk_size = message.chunk_size  
                elif isinstance(message, VideoMessage):  
                    # Write video data to file  
                    flv.write(message.timestamp, message.payload, FLVMediaType.VIDEO)  
                elif isinstance(message, AudioMessage):  
                    # Write data data to file  
                    flv.write(message.timestamp, message.payload, FLVMediaType.AUDIO)  
                elif isinstance(message, NSCloseStream):  
                    pass  
                    elif isinstance(message, NSDeleteStream):  
                    pass  
                else:  
                    logger.debug(f"Unknown message {str(message)}")  
      
        except StreamClosedException as ex:  
            logger.debug(f"Client {session.peername} disconnected!")  
        finally:  
            if flv:  
                flv.close()  
      
      
    async def serve_rtmp(use_protocol=True):  
        loop = asyncio.get_running_loop()  
        if use_protocol is True:  
            server = await loop.create_server(lambda: RTMPProtocol(controller=simple_controller, loop=loop), '0.0.0.0', 1935)  
        else:  
            server = await asyncio.start_server(simple_controller, '0.0.0.0', 1935)  
        addr = server.sockets[0].getsockname()  
        logger.info(f'Serving on {addr}')  
        async with server:  
            await server.serve_forever()  
      
      
    if __name__ == "__main__":  
        asyncio.run(serve_rtmp())


 

## Deployment  

  
## Roadmap  
- Supported HTTP2/3  
- RTMP over websocket  
- Support AMF3  
- ReStream  
- HLS Playback  
- Documentation



