pyrtmp


Namepyrtmp JSON
Version 0.3.0 PyPI version JSON
download
home_pagehttps://github.com/Eittipat/pyrtmp.git
SummaryPyRTMP: Pure Python RTMP server
upload_time2023-12-10 11:03:30
maintainer
docs_urlNone
authorEittipat.K
requires_python>=3.10
licenseMIT
keywords rtmp rtmpt asyncio
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # PyRTMP: Pure Python RTMP server
![coverage](https://github.com/Eittipat/pyrtmp/blob/master/coverage.svg)
## Features

- ✅ Pure Python
- ✅ Easy to customize
- ✅ Production Ready
- ✅ UV loop
- ✅ PyPy
- ✅ Support RTMP(s)
- ✅ Support RTMPT(s)

## Announcement

After using this package for years in production server. It runs flawlessly without any problem.
So I decided to switch the development status from **Beta** to **Production** since version 0.2.0. Also,
I share my configuration at Deployment section below.

If you have any problems. Feel free to create issue on [GitHub](https://github.com/Eittipat/pyrtmp/issues).

## What's new

### 0.3.0

- Clean up, refactoring, bug fixes
- Add more testcases.
- Add support to Python 3.11
- Add GitHub action workflows
- Add [RTMP to FFMPEG](https://github.com/Eittipat/pyrtmp/blob/master/example/demo_ffmpeg.py) example
- Add [RTMP to FLV](https://github.com/Eittipat/pyrtmp/blob/master/example/demo_flvdump.py) example
- Add [RTMPT](https://github.com/Eittipat/pyrtmp/blob/master/example/demo_rtmpt.py) example

## Installation

Install from PyPI:
```
pip install pyrtmp
```
Install from source:
```
pip install pyrtmp@git+https://github.com/Eittipat/pyrtmp.git
```

## Quickstart

Let say we want to create a simple RTMP server that can save all incoming stream to FLV file.
We can do it by creating a subclass of [*Simple RTMP
controller*](https://github.com/Eittipat/pyrtmp/blob/master/pyrtmp/rtmp.py)
and override some methods.
Here is the example:

```python
import asyncio
import os
import logging

from pyrtmp import StreamClosedException
from pyrtmp.flv import FLVFileWriter, FLVMediaType
from pyrtmp.session_manager import SessionManager
from pyrtmp.rtmp import SimpleRTMPController, RTMPProtocol, SimpleRTMPServer

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)


class RTMP2FLVController(SimpleRTMPController):

    def __init__(self, output_directory: str):
        self.output_directory = output_directory
        super().__init__()

    async def on_ns_publish(self, session, message) -> None:
        publishing_name = message.publishing_name
        file_path = os.path.join(self.output_directory, f"{publishing_name}.flv")
        session.state = FLVFileWriter(output=file_path)
        await super().on_ns_publish(session, message)

    async def on_metadata(self, session, message) -> None:
        session.state.write(0, message.to_raw_meta(), FLVMediaType.OBJECT)
        await super().on_metadata(session, message)

    async def on_video_message(self, session, message) -> None:
        session.state.write(message.timestamp, message.payload, FLVMediaType.VIDEO)
        await super().on_video_message(session, message)

    async def on_audio_message(self, session, message) -> None:
        session.state.write(message.timestamp, message.payload, FLVMediaType.AUDIO)
        await super().on_audio_message(session, message)

    async def on_stream_closed(self, session: SessionManager, exception: StreamClosedException) -> None:
        session.state.close()
        await super().on_stream_closed(session, exception)


class SimpleServer(SimpleRTMPServer):

    def __init__(self, output_directory: str):
        self.output_directory = output_directory
        super().__init__()

    async def create(self, host: str, port: int):
        loop = asyncio.get_event_loop()
        self.server = await loop.create_server(
            lambda: RTMPProtocol(controller=RTMP2FLVController(self.output_directory)),
            host=host,
            port=port,
        )


async def main():
    current_dir = os.path.dirname(os.path.abspath(__file__))
    server = SimpleServer(output_directory=current_dir)
    await server.create(host='0.0.0.0', port=1935)
    await server.start()
    await server.wait_closed()


if __name__ == "__main__":
    asyncio.run(main())
```

Next, we can test our server by executing the following command:

```
ffmpeg -i my_test_file.flv -c:v copy -c:a copy -f flv rtmp://127.0.0.1:1935/test/sample
```

Your flv file will be saved in the same directory as your python script.

## Deployment

In production environment, You should run multiple instances of RTMP server and use load balancer to distribute incoming
stream.
I recommend to use `Nginx` as a load balancer and `Supervisord` to manage your RTMP server instances.
Also, `uvloop` or `pypy` can be used to boost your performance.

Here is nginx configuration example:

```
stream {

    upstream stream_backend {
        127.0.0.1:1936;
        127.0.0.1:1937;
    }

    server {
        listen     1935;
        proxy_pass stream_backend;
    }
}
```

## Benchmark

Coming soon.

## Roadmap

- Support AMF3
- ReStream / Client Mode
- Documentation


            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/Eittipat/pyrtmp.git",
    "name": "pyrtmp",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": "",
    "keywords": "RTMP,RTMPT,asyncio",
    "author": "Eittipat.K",
    "author_email": "iammop@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/f9/9f/2e20c12c99253e562fd40141c15afa939bcc3e29e9a3147bb1483defe771/pyrtmp-0.3.0.tar.gz",
    "platform": null,
    "description": "# PyRTMP: Pure Python RTMP server\n![coverage](https://github.com/Eittipat/pyrtmp/blob/master/coverage.svg)\n## Features\n\n- \u2705 Pure Python\n- \u2705 Easy to customize\n- \u2705 Production Ready\n- \u2705 UV loop\n- \u2705 PyPy\n- \u2705 Support RTMP(s)\n- \u2705 Support RTMPT(s)\n\n## Announcement\n\nAfter using this package for years in production server. It runs flawlessly without any problem.\nSo I decided to switch the development status from **Beta** to **Production** since version 0.2.0. Also,\nI share my configuration at Deployment section below.\n\nIf you have any problems. Feel free to create issue on [GitHub](https://github.com/Eittipat/pyrtmp/issues).\n\n## What's new\n\n### 0.3.0\n\n- Clean up, refactoring, bug fixes\n- Add more testcases.\n- Add support to Python 3.11\n- Add GitHub action workflows\n- Add [RTMP to FFMPEG](https://github.com/Eittipat/pyrtmp/blob/master/example/demo_ffmpeg.py) example\n- Add [RTMP to FLV](https://github.com/Eittipat/pyrtmp/blob/master/example/demo_flvdump.py) example\n- Add [RTMPT](https://github.com/Eittipat/pyrtmp/blob/master/example/demo_rtmpt.py) example\n\n## Installation\n\nInstall from PyPI:\n```\npip install pyrtmp\n```\nInstall from source:\n```\npip install pyrtmp@git+https://github.com/Eittipat/pyrtmp.git\n```\n\n## Quickstart\n\nLet say we want to create a simple RTMP server that can save all incoming stream to FLV file.\nWe can do it by creating a subclass of [*Simple RTMP\ncontroller*](https://github.com/Eittipat/pyrtmp/blob/master/pyrtmp/rtmp.py)\nand override some methods.\nHere is the example:\n\n```python\nimport asyncio\nimport os\nimport logging\n\nfrom pyrtmp import StreamClosedException\nfrom pyrtmp.flv import FLVFileWriter, FLVMediaType\nfrom pyrtmp.session_manager import SessionManager\nfrom pyrtmp.rtmp import SimpleRTMPController, RTMPProtocol, SimpleRTMPServer\n\nlogging.basicConfig(level=logging.DEBUG)\nlogger = logging.getLogger(__name__)\nlogger.setLevel(logging.DEBUG)\n\n\nclass RTMP2FLVController(SimpleRTMPController):\n\n    def __init__(self, output_directory: str):\n        self.output_directory = output_directory\n        super().__init__()\n\n    async def on_ns_publish(self, session, message) -> None:\n        publishing_name = message.publishing_name\n        file_path = os.path.join(self.output_directory, f\"{publishing_name}.flv\")\n        session.state = FLVFileWriter(output=file_path)\n        await super().on_ns_publish(session, message)\n\n    async def on_metadata(self, session, message) -> None:\n        session.state.write(0, message.to_raw_meta(), FLVMediaType.OBJECT)\n        await super().on_metadata(session, message)\n\n    async def on_video_message(self, session, message) -> None:\n        session.state.write(message.timestamp, message.payload, FLVMediaType.VIDEO)\n        await super().on_video_message(session, message)\n\n    async def on_audio_message(self, session, message) -> None:\n        session.state.write(message.timestamp, message.payload, FLVMediaType.AUDIO)\n        await super().on_audio_message(session, message)\n\n    async def on_stream_closed(self, session: SessionManager, exception: StreamClosedException) -> None:\n        session.state.close()\n        await super().on_stream_closed(session, exception)\n\n\nclass SimpleServer(SimpleRTMPServer):\n\n    def __init__(self, output_directory: str):\n        self.output_directory = output_directory\n        super().__init__()\n\n    async def create(self, host: str, port: int):\n        loop = asyncio.get_event_loop()\n        self.server = await loop.create_server(\n            lambda: RTMPProtocol(controller=RTMP2FLVController(self.output_directory)),\n            host=host,\n            port=port,\n        )\n\n\nasync def main():\n    current_dir = os.path.dirname(os.path.abspath(__file__))\n    server = SimpleServer(output_directory=current_dir)\n    await server.create(host='0.0.0.0', port=1935)\n    await server.start()\n    await server.wait_closed()\n\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\nNext, we can test our server by executing the following command:\n\n```\nffmpeg -i my_test_file.flv -c:v copy -c:a copy -f flv rtmp://127.0.0.1:1935/test/sample\n```\n\nYour flv file will be saved in the same directory as your python script.\n\n## Deployment\n\nIn production environment, You should run multiple instances of RTMP server and use load balancer to distribute incoming\nstream.\nI recommend to use `Nginx` as a load balancer and `Supervisord` to manage your RTMP server instances.\nAlso, `uvloop` or `pypy` can be used to boost your performance.\n\nHere is nginx configuration example:\n\n```\nstream {\n\n    upstream stream_backend {\n        127.0.0.1:1936;\n        127.0.0.1:1937;\n    }\n\n    server {\n        listen     1935;\n        proxy_pass stream_backend;\n    }\n}\n```\n\n## Benchmark\n\nComing soon.\n\n## Roadmap\n\n- Support AMF3\n- ReStream / Client Mode\n- Documentation\n\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "PyRTMP: Pure Python RTMP server",
    "version": "0.3.0",
    "project_urls": {
        "Download": "https://github.com/Eittipat/pyrtmp/releases/tag/v0.3.0",
        "Homepage": "https://github.com/Eittipat/pyrtmp.git"
    },
    "split_keywords": [
        "rtmp",
        "rtmpt",
        "asyncio"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "f99f2e20c12c99253e562fd40141c15afa939bcc3e29e9a3147bb1483defe771",
                "md5": "f526cbc65da78d757f13060ab8d61f3e",
                "sha256": "2af92ed10382da490b8971b601bb8a16c45cd643deea72d4d5ca21d3d46d89fb"
            },
            "downloads": -1,
            "filename": "pyrtmp-0.3.0.tar.gz",
            "has_sig": false,
            "md5_digest": "f526cbc65da78d757f13060ab8d61f3e",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 17302,
            "upload_time": "2023-12-10T11:03:30",
            "upload_time_iso_8601": "2023-12-10T11:03:30.342575Z",
            "url": "https://files.pythonhosted.org/packages/f9/9f/2e20c12c99253e562fd40141c15afa939bcc3e29e9a3147bb1483defe771/pyrtmp-0.3.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-12-10 11:03:30",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "Eittipat",
    "github_project": "pyrtmp",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [],
    "lcname": "pyrtmp"
}
        
Elapsed time: 0.15298s