Package: pyrtmp
PyRTMP: Pure Python RTMP serverFeatures✅ Pure Python✅ Easy to customize✅ Production Ready✅ UV loop✅ PyPy✅ Support RTMP(s)✅ Support RTMPT(s)AnnouncementAfter using this package for years in production server. It runs flawlessly without any problem.
So I decided to switch the development status fromBetatoProductionsince version 0.2.0. Also,
I share my configuration at Deployment section below.If you have any problems. Feel free to create issue onGitHub.What's new0.3.0Clean up, refactoring, bug fixesAdd more testcases.Add support to Python 3.11Add GitHub action workflowsAddRTMP to FFMPEGexampleAddRTMP to FLVexampleAddRTMPTexampleInstallationInstall from PyPI:pip install pyrtmpInstall from source:pip install pyrtmp@git+https://github.com/Eittipat/pyrtmp.gitQuickstartLet 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 ofSimple RTMP
controllerand override some methods.
Here is the example:importasyncioimportosimportloggingfrompyrtmpimportStreamClosedExceptionfrompyrtmp.flvimportFLVFileWriter,FLVMediaTypefrompyrtmp.session_managerimportSessionManagerfrompyrtmp.rtmpimportSimpleRTMPController,RTMPProtocol,SimpleRTMPServerlogging.basicConfig(level=logging.DEBUG)logger=logging.getLogger(__name__)logger.setLevel(logging.DEBUG)classRTMP2FLVController(SimpleRTMPController):def__init__(self,output_directory:str):self.output_directory=output_directorysuper().__init__()asyncdefon_ns_publish(self,session,message)->None:publishing_name=message.publishing_namefile_path=os.path.join(self.output_directory,f"{publishing_name}.flv")session.state=FLVFileWriter(output=file_path)awaitsuper().on_ns_publish(session,message)asyncdefon_metadata(self,session,message)->None:session.state.write(0,message.to_raw_meta(),FLVMediaType.OBJECT)awaitsuper().on_metadata(session,message)asyncdefon_video_message(self,session,message)->None:session.state.write(message.timestamp,message.payload,FLVMediaType.VIDEO)awaitsuper().on_video_message(session,message)asyncdefon_audio_message(self,session,message)->None:session.state.write(message.timestamp,message.payload,FLVMediaType.AUDIO)awaitsuper().on_audio_message(session,message)asyncdefon_stream_closed(self,session:SessionManager,exception:StreamClosedException)->None:session.state.close()awaitsuper().on_stream_closed(session,exception)classSimpleServer(SimpleRTMPServer):def__init__(self,output_directory:str):self.output_directory=output_directorysuper().__init__()asyncdefcreate(self,host:str,port:int):loop=asyncio.get_event_loop()self.server=awaitloop.create_server(lambda:RTMPProtocol(controller=RTMP2FLVController(self.output_directory)),host=host,port=port,)asyncdefmain():current_dir=os.path.dirname(os.path.abspath(__file__))server=SimpleServer(output_directory=current_dir)awaitserver.create(host='0.0.0.0',port=1935)awaitserver.start()awaitserver.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/sampleYour flv file will be saved in the same directory as your python script.DeploymentIn production environment, You should run multiple instances of RTMP server and use load balancer to distribute incoming
stream.
I recommend to useNginxas a load balancer andSupervisordto manage your RTMP server instances.
Also,uvlooporpypycan 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;
    }
}BenchmarkComing soon.RoadmapSupport AMF3ReStream / Client ModeDocumentation