Repository: bluenviron/mediamtx Branch: main Commit: a82e0c7a9d52 Files: 473 Total size: 1.9 MB Directory structure: gitextract__vy29lq8/ ├── .dockerignore ├── .github/ │ ├── DISCUSSION_TEMPLATE/ │ │ └── questions.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug.yml │ │ ├── config.yml │ │ └── feature.yml │ ├── dependabot.yml │ └── workflows/ │ ├── lint.yml │ ├── nightly_binaries.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .golangci.yml ├── LICENSE ├── Makefile ├── README.md ├── SECURITY.md ├── api/ │ ├── .redocly.yaml │ └── openapi.yaml ├── docker/ │ ├── ffmpeg-rpi.Dockerfile │ ├── ffmpeg.Dockerfile │ ├── rpi.Dockerfile │ └── standard.Dockerfile ├── docs/ │ ├── 1-kickoff/ │ │ ├── 1-introduction.md │ │ ├── 2-install.md │ │ ├── 3-upgrade.md │ │ ├── 4-basic-usage.md │ │ └── index.md │ ├── 2-publish/ │ │ ├── 01-overview.md │ │ ├── 02-srt-clients.md │ │ ├── 03-srt-cameras-and-servers.md │ │ ├── 04-webrtc-clients.md │ │ ├── 05-webrtc-servers.md │ │ ├── 06-rtsp-clients.md │ │ ├── 07-rtsp-cameras-and-servers.md │ │ ├── 08-rtmp-clients.md │ │ ├── 09-rtmp-cameras-and-servers.md │ │ ├── 10-hls-cameras-and-servers.md │ │ ├── 11-mpeg-ts.md │ │ ├── 12-rtp.md │ │ ├── 13-raspberry-pi-cameras.md │ │ ├── 14-generic-webcams.md │ │ ├── 15-ffmpeg.md │ │ ├── 16-gstreamer.md │ │ ├── 17-obs-studio.md │ │ ├── 18-python-opencv.md │ │ ├── 19-golang.md │ │ ├── 20-unity.md │ │ ├── 21-web-browsers.md │ │ └── index.md │ ├── 3-read/ │ │ ├── 01-overview.md │ │ ├── 02-srt.md │ │ ├── 03-webrtc.md │ │ ├── 04-rtsp.md │ │ ├── 05-rtmp.md │ │ ├── 06-hls.md │ │ ├── 07-ffmpeg.md │ │ ├── 08-gstreamer.md │ │ ├── 09-vlc.md │ │ ├── 10-obs-studio.md │ │ ├── 11-python-opencv.md │ │ ├── 12-golang.md │ │ ├── 13-unity.md │ │ ├── 14-web-browsers.md │ │ └── index.md │ ├── 4-other/ │ │ ├── 02-configuration.md │ │ ├── 03-authentication.md │ │ ├── 04-remuxing-reencoding-compression.md │ │ ├── 05-always-available.md │ │ ├── 06-record.md │ │ ├── 07-playback.md │ │ ├── 08-forward.md │ │ ├── 09-proxy.md │ │ ├── 10-extract-snapshots.md │ │ ├── 11-on-demand-publishing.md │ │ ├── 12-absolute-timestamps.md │ │ ├── 13-expose-the-server-in-a-subfolder.md │ │ ├── 14-embed-streams-in-a-website.md │ │ ├── 15-start-on-boot.md │ │ ├── 16-logging.md │ │ ├── 17-hooks.md │ │ ├── 18-control-api.md │ │ ├── 19-metrics.md │ │ ├── 20-performance.md │ │ ├── 21-srt-specific-features.md │ │ ├── 22-webrtc-specific-features.md │ │ ├── 23-rtsp-specific-features.md │ │ ├── 24-rtmp-specific-features.md │ │ ├── 25-decrease-packet-loss.md │ │ └── index.md │ ├── 5-references/ │ │ ├── 1-configuration-file.md │ │ ├── 2-control-api.md │ │ └── index.md │ ├── 6-misc/ │ │ ├── 1-compile.md │ │ ├── 2-license.md │ │ ├── 3-security.md │ │ ├── 4-specifications.md │ │ ├── 5-related-projects.md │ │ └── index.md │ └── redirects.yaml ├── go.mod ├── go.sum ├── internal/ │ ├── api/ │ │ ├── api.go │ │ ├── api_config_global.go │ │ ├── api_config_global_test.go │ │ ├── api_config_pathdefaults.go │ │ ├── api_config_pathdefaults_test.go │ │ ├── api_config_paths.go │ │ ├── api_config_paths_test.go │ │ ├── api_hls.go │ │ ├── api_hls_test.go │ │ ├── api_paths.go │ │ ├── api_paths_test.go │ │ ├── api_recordings.go │ │ ├── api_recordings_test.go │ │ ├── api_rtmp.go │ │ ├── api_rtmp_test.go │ │ ├── api_rtsp.go │ │ ├── api_rtsp_test.go │ │ ├── api_srt.go │ │ ├── api_srt_test.go │ │ ├── api_test.go │ │ ├── api_webrtc.go │ │ ├── api_webrtc_test.go │ │ ├── paginate.go │ │ ├── paginate_test.go │ │ └── testdata/ │ │ └── fuzz/ │ │ └── FuzzPaginate/ │ │ ├── 23731da0f18d31d0 │ │ ├── 34523a772174e26e │ │ └── 85649d45641911d0 │ ├── auth/ │ │ ├── credentials.go │ │ ├── error.go │ │ ├── jwt_claims.go │ │ ├── manager.go │ │ ├── manager_test.go │ │ └── request.go │ ├── certloader/ │ │ ├── certloader.go │ │ └── certloader_test.go │ ├── conf/ │ │ ├── always_available_track.go │ │ ├── always_available_track_codec.go │ │ ├── auth_action.go │ │ ├── auth_internal_user.go │ │ ├── auth_internal_user_permission.go │ │ ├── auth_method.go │ │ ├── conf.go │ │ ├── conf_test.go │ │ ├── credential.go │ │ ├── credential_test.go │ │ ├── decrypt/ │ │ │ └── decrypt.go │ │ ├── duration.go │ │ ├── duration_test.go │ │ ├── encryption.go │ │ ├── env/ │ │ │ ├── env.go │ │ │ └── env_test.go │ │ ├── global.go │ │ ├── hls_variant.go │ │ ├── ip_network.go │ │ ├── ip_networks.go │ │ ├── jsonwrapper/ │ │ │ ├── testdata/ │ │ │ │ └── fuzz/ │ │ │ │ └── FuzzUnmarshal/ │ │ │ │ ├── 297c43aee5d30530 │ │ │ │ └── 7f71a2d116d5afde │ │ │ ├── unmarshal.go │ │ │ └── unmarshal_test.go │ │ ├── log_destination.go │ │ ├── log_destinations.go │ │ ├── log_level.go │ │ ├── optional_global.go │ │ ├── optional_path.go │ │ ├── path.go │ │ ├── path_test.go │ │ ├── record_format.go │ │ ├── rtsp_auth_method.go │ │ ├── rtsp_auth_methods.go │ │ ├── rtsp_range_type.go │ │ ├── rtsp_transport.go │ │ ├── rtsp_transports.go │ │ ├── string_size.go │ │ ├── webrtc_ice_server.go │ │ └── yamlwrapper/ │ │ ├── testdata/ │ │ │ └── fuzz/ │ │ │ └── FuzzUnmarshal/ │ │ │ ├── 90b5d1b18dd9f8ac │ │ │ └── dc806ec658c460fc │ │ ├── unmarshal.go │ │ └── unmarshal_test.go │ ├── confwatcher/ │ │ ├── confwatcher.go │ │ └── confwatcher_test.go │ ├── core/ │ │ ├── api_test.go │ │ ├── core.go │ │ ├── core_test.go │ │ ├── metrics_test.go │ │ ├── path.go │ │ ├── path_manager.go │ │ ├── path_manager_test.go │ │ ├── path_test.go │ │ ├── source_redirect.go │ │ ├── test_on_demand/ │ │ │ └── main.go │ │ ├── upgrade.go │ │ ├── upgrade_disabled.go │ │ └── versiongetter/ │ │ └── main.go │ ├── counterdumper/ │ │ ├── dumper.go │ │ └── dumper_test.go │ ├── defs/ │ │ ├── api.go │ │ ├── api_hls.go │ │ ├── api_path.go │ │ ├── api_path_track.go │ │ ├── api_path_track_codec.go │ │ ├── api_path_track_codec_props.go │ │ ├── api_path_track_codec_test.go │ │ ├── api_recording.go │ │ ├── api_rtmp.go │ │ ├── api_rtsp.go │ │ ├── api_srt.go │ │ ├── api_webrtc.go │ │ ├── defs.go │ │ ├── path.go │ │ ├── path_access_request.go │ │ ├── publisher.go │ │ ├── reader.go │ │ ├── source.go │ │ └── static_source.go │ ├── errordumper/ │ │ ├── dumper.go │ │ └── dumper_test.go │ ├── externalcmd/ │ │ ├── cmd.go │ │ ├── cmd_unix.go │ │ ├── cmd_win.go │ │ └── pool.go │ ├── hooks/ │ │ ├── hooks.go │ │ ├── on_connect.go │ │ ├── on_demand.go │ │ ├── on_init.go │ │ ├── on_read.go │ │ └── on_ready.go │ ├── linters/ │ │ ├── conf/ │ │ │ └── conf_test.go │ │ └── go2api/ │ │ └── go2api_test.go │ ├── logger/ │ │ ├── destination.go │ │ ├── destination_file.go │ │ ├── destination_stdout.go │ │ ├── destination_syslog.go │ │ ├── destination_syslog_disabled.go │ │ ├── level.go │ │ ├── logger.go │ │ ├── logger_test.go │ │ └── writer.go │ ├── metrics/ │ │ ├── metrics.go │ │ └── metrics_test.go │ ├── ntpestimator/ │ │ ├── estimator.go │ │ └── estimator_test.go │ ├── packetdumper/ │ │ ├── conn.go │ │ ├── conn_test.go │ │ ├── dial_context.go │ │ ├── listen.go │ │ ├── listen_packet.go │ │ ├── listener.go │ │ ├── packet_conn.go │ │ └── packet_conn_test.go │ ├── playback/ │ │ ├── muxer.go │ │ ├── muxer_fmp4.go │ │ ├── muxer_mp4.go │ │ ├── muxer_mp4_test.go │ │ ├── on_get.go │ │ ├── on_get_test.go │ │ ├── on_list.go │ │ ├── on_list_test.go │ │ ├── segment_fmp4.go │ │ ├── segment_fmp4_test.go │ │ ├── server.go │ │ └── server_test.go │ ├── pprof/ │ │ ├── pprof.go │ │ └── pprof_test.go │ ├── protocols/ │ │ ├── hls/ │ │ │ ├── from_stream.go │ │ │ ├── from_stream_test.go │ │ │ ├── to_stream.go │ │ │ └── to_stream_test.go │ │ ├── httpp/ │ │ │ ├── content_type.go │ │ │ ├── credentials.go │ │ │ ├── credentials_test.go │ │ │ ├── handler_exit_on_panic.go │ │ │ ├── handler_filter_requests.go │ │ │ ├── handler_filter_requests_test.go │ │ │ ├── handler_logger.go │ │ │ ├── handler_origin.go │ │ │ ├── handler_origin_test.go │ │ │ ├── handler_server_header.go │ │ │ ├── handler_tracker.go │ │ │ ├── handler_tracker_test.go │ │ │ ├── handler_write_timeout.go │ │ │ ├── remote_addr.go │ │ │ ├── server.go │ │ │ └── server_test.go │ │ ├── mpegts/ │ │ │ ├── enhanced_reader.go │ │ │ ├── from_stream.go │ │ │ ├── from_stream_test.go │ │ │ ├── to_stream.go │ │ │ └── to_stream_test.go │ │ ├── rtmp/ │ │ │ ├── from_stream.go │ │ │ ├── from_stream_test.go │ │ │ ├── to_stream.go │ │ │ └── to_stream_test.go │ │ ├── rtsp/ │ │ │ ├── credentials.go │ │ │ ├── credentials_test.go │ │ │ └── to_stream.go │ │ ├── tls/ │ │ │ ├── make_config.go │ │ │ └── make_config_test.go │ │ ├── udp/ │ │ │ ├── listener.go │ │ │ ├── listener_test.go │ │ │ └── params.go │ │ ├── unix/ │ │ │ ├── listener.go │ │ │ ├── listener_test.go │ │ │ └── params.go │ │ ├── webrtc/ │ │ │ ├── from_stream.go │ │ │ ├── from_stream_test.go │ │ │ ├── incoming_track.go │ │ │ ├── net.go │ │ │ ├── outgoing_data_channel.go │ │ │ ├── outgoing_track.go │ │ │ ├── peer_connection.go │ │ │ ├── peer_connection_test.go │ │ │ ├── stats.go │ │ │ ├── stats_interceptor.go │ │ │ ├── tcp_mux_wrapper.go │ │ │ ├── to_stream.go │ │ │ └── to_stream_test.go │ │ ├── websocket/ │ │ │ ├── serverconn.go │ │ │ └── serverconn_test.go │ │ └── whip/ │ │ ├── client.go │ │ ├── client_test.go │ │ ├── ice_fragment.go │ │ ├── ice_fragment_test.go │ │ ├── link_header.go │ │ └── link_header_test.go │ ├── recordcleaner/ │ │ ├── cleaner.go │ │ └── cleaner_test.go │ ├── recorder/ │ │ ├── format.go │ │ ├── format_fmp4.go │ │ ├── format_fmp4_part.go │ │ ├── format_fmp4_segment.go │ │ ├── format_fmp4_track.go │ │ ├── format_mpegts.go │ │ ├── format_mpegts_segment.go │ │ ├── format_mpegts_track.go │ │ ├── recorder.go │ │ ├── recorder_instance.go │ │ └── recorder_test.go │ ├── recordstore/ │ │ ├── mp4_boxes.go │ │ ├── path.go │ │ ├── path_test.go │ │ ├── recordstore.go │ │ ├── segment.go │ │ └── segment_test.go │ ├── restrictnetwork/ │ │ └── restrict_network.go │ ├── rlimit/ │ │ ├── rlimit_unix.go │ │ └── rlimit_win.go │ ├── servers/ │ │ ├── hls/ │ │ │ ├── hlsjsdownloader/ │ │ │ │ ├── HASH │ │ │ │ ├── VERSION │ │ │ │ └── main.go │ │ │ ├── http_server.go │ │ │ ├── index.html │ │ │ ├── muxer.go │ │ │ ├── muxer_instance.go │ │ │ ├── server.go │ │ │ └── server_test.go │ │ ├── rtmp/ │ │ │ ├── conn.go │ │ │ ├── listener.go │ │ │ ├── server.go │ │ │ └── server_test.go │ │ ├── rtsp/ │ │ │ ├── conn.go │ │ │ ├── mpegts_demuxer.go │ │ │ ├── server.go │ │ │ ├── server_test.go │ │ │ └── session.go │ │ ├── srt/ │ │ │ ├── conn.go │ │ │ ├── listener.go │ │ │ ├── server.go │ │ │ ├── server_test.go │ │ │ ├── streamid.go │ │ │ └── streamid_test.go │ │ └── webrtc/ │ │ ├── http_server.go │ │ ├── publish_index.html │ │ ├── publisher.js │ │ ├── read_index.html │ │ ├── reader.js │ │ ├── server.go │ │ ├── server_test.go │ │ └── session.go │ ├── staticsources/ │ │ ├── handler.go │ │ ├── hls/ │ │ │ ├── source.go │ │ │ └── source_test.go │ │ ├── mpegts/ │ │ │ ├── source.go │ │ │ └── source_test.go │ │ ├── rpicamera/ │ │ │ ├── camera_arm32_.go │ │ │ ├── camera_arm64_.go │ │ │ ├── camera_arm_.go │ │ │ ├── camera_other.go │ │ │ ├── downloader.go │ │ │ ├── mtxrpicamdownloader/ │ │ │ │ ├── HASH_MTXRPICAM_32_TAR_GZ │ │ │ │ ├── HASH_MTXRPICAM_64_TAR_GZ │ │ │ │ ├── VERSION │ │ │ │ └── main.go │ │ │ ├── params.go │ │ │ ├── params_serialize.go │ │ │ ├── pipe.go │ │ │ └── source.go │ │ ├── rtmp/ │ │ │ ├── source.go │ │ │ └── source_test.go │ │ ├── rtp/ │ │ │ ├── format.go │ │ │ ├── media.go │ │ │ ├── source.go │ │ │ └── source_test.go │ │ ├── rtsp/ │ │ │ ├── source.go │ │ │ └── source_test.go │ │ ├── srt/ │ │ │ ├── source.go │ │ │ └── source_test.go │ │ └── webrtc/ │ │ ├── source.go │ │ └── source_test.go │ ├── stream/ │ │ ├── format_updater.go │ │ ├── offline_sub_stream.go │ │ ├── offline_sub_stream_track.go │ │ ├── reader.go │ │ ├── rtp_decoder.go │ │ ├── rtp_encoder.go │ │ ├── stream.go │ │ ├── stream_alwaysavailable_test.go │ │ ├── stream_format.go │ │ ├── stream_media.go │ │ ├── stream_standard_test.go │ │ ├── sub_stream.go │ │ ├── sub_stream_format.go │ │ ├── sub_stream_media.go │ │ └── unit_remuxer.go │ ├── test/ │ │ ├── auth_manager.go │ │ ├── formats.go │ │ ├── logger.go │ │ ├── medias.go │ │ ├── path_manager.go │ │ ├── static_source_parent.go │ │ ├── temp_file.go │ │ └── tls_cert.go │ ├── teste2e/ │ │ ├── build_images_test.go │ │ ├── hls_manager_test.go │ │ ├── images/ │ │ │ ├── ffmpeg/ │ │ │ │ ├── Dockerfile │ │ │ │ ├── emptyvideo.mkv │ │ │ │ ├── emptyvideoaudio.mkv │ │ │ │ └── start.sh │ │ │ ├── gstreamer/ │ │ │ │ ├── Dockerfile │ │ │ │ ├── emptyvideo.mkv │ │ │ │ ├── exitafterframe.c │ │ │ │ └── start.sh │ │ │ └── vlc/ │ │ │ ├── Dockerfile │ │ │ └── start.sh │ │ ├── rtsp_server_test.go │ │ └── tests_test.go │ └── unit/ │ ├── payload.go │ ├── payload_ac3.go │ ├── payload_av1.go │ ├── payload_g711.go │ ├── payload_h264.go │ ├── payload_h265.go │ ├── payload_klv.go │ ├── payload_lpcm.go │ ├── payload_mjpeg.go │ ├── payload_mpeg1_audio.go │ ├── payload_mpeg1_video.go │ ├── payload_mpeg4_audio.go │ ├── payload_mpeg4_audio_latm.go │ ├── payload_mpeg4_video.go │ ├── payload_opus.go │ ├── payload_vp8.go │ ├── payload_vp9.go │ └── unit.go ├── main.go ├── mediamtx.yml └── scripts/ ├── binaries.mk ├── dockerhub.mk ├── format.mk ├── lint.mk ├── test-e2e.mk ├── test.mk └── winres.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .dockerignore ================================================ # do not add .git, since it is needed to extract the tag # do not add /binaries, since it is needed by Docker images /tmp /coverage*.txt /api/*.html /internal/core/VERSION /internal/servers/hls/hls.min.js /internal/staticsources/rpicamera/mtxrpicam_*/ ================================================ FILE: .github/DISCUSSION_TEMPLATE/questions.yml ================================================ body: - type: markdown attributes: value: | * Please create a discussion FOR EACH question. Do not ask for multiple questions all at once, otherwise they'll probably never get all answered. * If you are asking for help because you're having trouble doing something, provide enough informations to replicate the problem. In particular, include in the question: * MediaMTX version * precise instructions on how to replicate the problem * MediaMTX configuration * MediaMTX logs with setting `logLevel` set to `debug` * If you are asking for help and you think MediaMTX is misbehaving, open a bug report, NOT a discussion. - type: textarea attributes: label: Question validations: required: true ================================================ FILE: .github/ISSUE_TEMPLATE/bug.yml ================================================ name: Bug report description: Report a bug body: - type: markdown attributes: value: | To increase the chance of your bug getting fixed, open an issue FOR EACH bug. Do not report multiple problems in a single issue, otherwise they'll probably never get all fixed. - type: input id: version attributes: label: Which version are you using? description: MediaMTX version or commit validations: required: true - type: dropdown id: os attributes: label: Which operating system are you using? multiple: true options: - Linux amd64 standard - Linux amd64 Docker - Linux arm64 standard - Linux arm64 Docker - Linux arm7 standard - Linux arm7 Docker - Linux arm6 standard - Linux arm6 Docker - Windows amd64 standard - Windows amd64 Docker (WSL backend) - macOS amd64 standard - macOS amd64 Docker - Other (please describe) validations: required: true - type: textarea id: description attributes: label: Describe the issue validations: required: true - type: textarea id: replica attributes: label: Describe how to replicate the issue description: | The maintainers must be able to REPLICATE your issue to solve it - therefore, describe in a very detailed way how to replicate it. value: | 1. start the MediaMTX 2. publish with ... 3. read with ... validations: required: true - type: textarea id: conf attributes: label: MediaMTX configuration description: | MediaMTX configuration is often required to replicate the issue. placeholder: Paste the configuration file here render: yaml - type: textarea id: logs attributes: label: MediaMTX logs description: | MediaMTX logs are often useful to identify the issue. If you think this is the case, set 'logLevel' to 'debug' and attach logs. placeholder: Paste or drag the log file here - type: textarea id: network attributes: label: Packet dump description: | If the bug arises when using MediaMTX with external hardware or software, the most helpful information you can provide is a packet dump, that can be generated in this way: 1. In mediamtx.yml, set 'dumpPackets' to 'true' 2. Start the server and replicate the issue 3. Stop the server, find the generated .pcapng files in the current directory 4. Attach the pcapng files by dragging them here placeholder: Attach the pcapng files by dragging them here ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: Question url: https://github.com/bluenviron/mediamtx/discussions/new?category=questions about: Ask the community for help ================================================ FILE: .github/ISSUE_TEMPLATE/feature.yml ================================================ name: Feature request description: Share ideas for new features body: - type: markdown attributes: value: | Please create a request FOR EACH feature. Do not report multiple features in a single request, otherwise they'll probably never get all implemented. - type: textarea id: description attributes: label: Describe the feature validations: required: true ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: "gomod" directory: "/" schedule: interval: "daily" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" ================================================ FILE: .github/workflows/lint.yml ================================================ name: lint on: push: branches: [ main ] pull_request: branches: [ main ] jobs: go: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - uses: actions/setup-go@v6 with: go-version: "1.25" - run: go generate ./... - uses: golangci/golangci-lint-action@v9 with: version: v2.11.3 go_mod: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v6 - uses: actions/setup-go@v6 with: go-version: "1.25" - run: make lint-go-mod conf: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v6 - uses: actions/setup-go@v6 with: go-version: "1.25" - run: make lint-conf go2api: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v6 - uses: actions/setup-go@v6 with: go-version: "1.25" - run: make lint-go2api docs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v6 - run: make lint-docs api_docs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v6 - run: make lint-api-docs ================================================ FILE: .github/workflows/nightly_binaries.yml ================================================ name: nightly_binaries on: workflow_dispatch: jobs: nightly_binaries: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - run: make binaries - uses: actions/upload-artifact@v7 with: name: binaries path: binaries ================================================ FILE: .github/workflows/release.yml ================================================ name: release on: push: tags: - 'v*' permissions: id-token: write attestations: write artifact-metadata: write contents: write issues: write jobs: binaries: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v6 - run: make binaries - run: cd binaries && sha256sum -b * > checksums.sha256 - uses: actions/attest@v4 with: subject-path: '${{ github.workspace }}/binaries/*' - uses: actions/upload-artifact@v7 with: name: binaries path: binaries github_release: needs: binaries runs-on: ubuntu-22.04 steps: - uses: actions/download-artifact@v8 with: name: binaries path: binaries - uses: actions/github-script@v8 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const fs = require('fs').promises; const { repo: { owner, repo } } = context; const currentRelease = context.ref.split('/')[2]; let body = `## New major features\n` + `\n` + `TODO\n` + `\n` + `## Fixes and improvements\n` + `\n` + `TODO\n` + `\n` + `## Security\n` + `\n` + `Binaries are compiled from source code by the [Release workflow](https://github.com/${owner}/${repo}/actions/workflows/release.yml), which is a fully-visible process that prevents any change or external interference in produced artifacts.\n` + `\n` + 'Checksums of binaries are also published in a public blockchain by using [GitHub Attestations](https://docs.github.com/en/actions/concepts/security/artifact-attestations), and they can be verified by running:\n' + `\n` + '```\n' + `ls mediamtx_* | xargs -L1 gh attestation verify --repo bluenviron/mediamtx\n` + '```\n' + `\n` + 'You can verify checksums of binaries by downloading `checksums.sha256` and running:\n' + `\n` + '```\n' + `cat checksums.sha256 | grep "$(ls mediamtx_*)" | sha256sum --check\n` + '```\n' + `\n`; const res = await github.rest.repos.createRelease({ owner, repo, tag_name: currentRelease, name: currentRelease, body, }); const release_id = res.data.id; for (const name of await fs.readdir('./binaries/')) { await github.rest.repos.uploadReleaseAsset({ owner, repo, release_id, name, data: await fs.readFile(`./binaries/${name}`), }); } github_notify_issues: needs: github_release runs-on: ubuntu-22.04 steps: - uses: actions/github-script@v8 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { repo: { owner, repo } } = context; const tags = await github.rest.repos.listTags({ owner, repo, }); const curTag = tags.data[0]; const prevTag = tags.data[1]; const diff = await github.rest.repos.compareCommitsWithBasehead({ owner, repo, basehead: `${prevTag.commit.sha}...${curTag.commit.sha}`, }); const issues = {}; for (const commit of diff.data.commits) { for (const match of commit.commit.message.matchAll(/(^| |\()#([0-9]+)( |\)|$)/g)) { issues[match[2]] = 1; } } for (const issue in issues) { try { await github.rest.issues.createComment({ owner, repo, issue_number: parseInt(issue), body: `This issue is mentioned in release ${curTag.name} 🚀\n` + `Check out the entire changelog by [clicking here](https://github.com/${owner}/${repo}/releases/tag/${curTag.name})`, }); } catch (exc) { console.error(exc.toString()); } } dockerhub: needs: binaries runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v6 - uses: actions/download-artifact@v8 with: name: binaries path: binaries - run: make dockerhub env: DOCKER_USER: ${{ secrets.DOCKER_USER }} DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} ================================================ FILE: .github/workflows/test.yml ================================================ name: test on: push: branches: [ main ] pull_request: branches: [ main ] jobs: test_64: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - run: make test - uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} test_32: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - run: make test-32 test_e2e: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - uses: actions/setup-go@v6 with: go-version: "1.25" - run: make test-e2e-nodocker ================================================ FILE: .gitignore ================================================ /tmp /binaries /coverage*.txt /api/*.html /internal/core/VERSION /internal/servers/hls/hls.min.js /internal/staticsources/rpicamera/mtxrpicam_*/ ================================================ FILE: .golangci.yml ================================================ version: "2" linters: enable: - asciicheck - bidichk - bodyclose - copyloopvar - dupl - errorlint - gochecknoinits - gocritic - lll - misspell - modernize - nilerr - prealloc - predeclared - reassign - revive - usestdlibvars - unconvert - tparallel - wastedassign - whitespace settings: errcheck: exclude-functions: - io.Copy - (io.Closer).Close - (io.Writer).Write - (hash.Hash).Write - (net.Conn).Close - (net.Conn).SetReadDeadline - (net.Conn).SetWriteDeadline - (*net.TCPConn).SetKeepAlive - (*net.TCPConn).SetKeepAlivePeriod - (*net.TCPConn).SetNoDelay - (net.Listener).Close - (net.PacketConn).Close - (net.PacketConn).SetReadDeadline - (net.PacketConn).SetWriteDeadline - (net/http.ResponseWriter).Write - (*net/http.Server).Serve - (*net/http.Server).ServeTLS - (*net/http.Server).Shutdown - os.Chdir - os.Mkdir - os.MkdirAll - os.Remove - os.RemoveAll - os.Setenv - os.Unsetenv - (*os.File).WriteString - (*os.File).Close - (github.com/datarhei/gosrt.Conn).Close - (github.com/datarhei/gosrt.Conn).SetReadDeadline - (github.com/datarhei/gosrt.Conn).SetWriteDeadline - (*github.com/bluenviron/gortsplib/v5.Client).Close - (*github.com/bluenviron/gortsplib/v5.Server).Close - (*github.com/bluenviron/gortsplib/v5.ServerSession).Close - (*github.com/bluenviron/gortsplib/v5.ServerStream).Close - (*github.com/bluenviron/gortsplib/v5.ServerConn).Close govet: enable-all: true disable: - fieldalignment - reflectvaluecompare settings: shadow: strict: true modernize: disable: - reflecttypefor - stringsbuilder - testingcontext formatters: enable: - gofmt - gofumpt - goimports ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2019 aler9 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Makefile ================================================ BASE_IMAGE = golang:1.25-alpine3.22 GOLANGCI_LINT_IMAGE = golangci/golangci-lint:v2.11.3 NODE_IMAGE = node:20-alpine3.22 .PHONY: $(shell ls) help: @echo "usage: make [action]" @echo "" @echo "available actions:" @echo "" @echo " format format code" @echo " test run tests" @echo " test-32 run tests on a 32-bit system" @echo " test-e2e run end-to-end tests" @echo " lint run linters" @echo " binaries build binaries for all supported platforms" @echo " dockerhub build and push images to Docker Hub" @echo "" blank := define NL $(blank) endef include scripts/*.mk ================================================ FILE: README.md ================================================

MediaMTX

[![Website](https://img.shields.io/badge/website-mediamtx.org-1c94b5)](https://mediamtx.org) [![Test](https://github.com/bluenviron/mediamtx/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/bluenviron/mediamtx/actions/workflows/test.yml?query=branch%3Amain) [![Lint](https://github.com/bluenviron/mediamtx/actions/workflows/lint.yml/badge.svg?branch=main)](https://github.com/bluenviron/mediamtx/actions/workflows/lint.yml?query=branch%3Amain) [![CodeCov](https://codecov.io/gh/bluenviron/mediamtx/branch/main/graph/badge.svg)](https://app.codecov.io/gh/bluenviron/mediamtx/tree/main) [![Release](https://img.shields.io/github/v/release/bluenviron/mediamtx)](https://github.com/bluenviron/mediamtx/releases) [![Docker Hub](https://img.shields.io/badge/docker-bluenviron/mediamtx-blue)](https://hub.docker.com/r/bluenviron/mediamtx)


_MediaMTX_ is a ready-to-use and zero-dependency real-time media server and media proxy that allows to publish, read, proxy, record and playback video and audio streams. It has been conceived as a "media router" that routes media streams from one end to the other, with a focus on efficiency and portability.
|[Install](https://mediamtx.org/docs/kickoff/install)|[Documentation](https://mediamtx.org/docs/kickoff/introduction)| |-|-|

Features

- [Publish](https://mediamtx.org/docs/usage/publish) live streams to the server with SRT, WebRTC, RTSP, RTMP, HLS, MPEG-TS, RTP, using FFmpeg, GStreamer, OBS Studio, Python , Golang, Unity, web browsers, Raspberry Pi Cameras and more. - [Read](https://mediamtx.org/docs/usage/read) live streams from the server with SRT, WebRTC, RTSP, RTMP, HLS, using FFmpeg, GStreamer, VLC, OBS Studio, Python , Golang, Unity, web browsers and more. - Streams are automatically converted from a protocol to another - Serve several streams at once in separate paths - Reload the configuration without disconnecting existing clients (hot reloading) - [Serve always-available streams](https://mediamtx.org/docs/usage/always-available) even when the publisher is offline - [Record](https://mediamtx.org/docs/usage/record) streams to disk in fMP4 or MPEG-TS format - [Playback](https://mediamtx.org/docs/usage/playback) recorded streams - [Authenticate](https://mediamtx.org/docs/usage/authentication) users with internal, HTTP or JWT authentication - [Forward](https://mediamtx.org/docs/usage/forward) streams to other servers - [Proxy](https://mediamtx.org/docs/usage/proxy) requests to other servers - [Control](https://mediamtx.org/docs/usage/control-api) the server through the Control API - [Extract metrics](https://mediamtx.org/docs/usage/metrics) from the server in a Prometheus-compatible format - [Monitor performance](https://mediamtx.org/docs/usage/performance) to investigate CPU and RAM consumption - [Run hooks](https://mediamtx.org/docs/usage/hooks) (external commands) when clients connect, disconnect, read or publish streams - Compatible with Linux, Windows and macOS, does not require any dependency or interpreter, it's a single executable - ...and many [others](https://mediamtx.org/docs/kickoff/introduction). ================================================ FILE: SECURITY.md ================================================ # Security Check the [Security page](https://mediamtx.org/docs/other/security) on the website. ================================================ FILE: api/.redocly.yaml ================================================ extends: - recommended rules: operation-4xx-response: off ================================================ FILE: api/openapi.yaml ================================================ openapi: 3.0.0 info: version: 1.0.0 title: MediaMTX API description: API of MediaMTX, a server and proxy that supports various protocols. license: name: MIT url: https://opensource.org/licenses/MIT servers: - url: http://localhost:9997 security: [] components: schemas: OKStatus: type: string enum: [ok] ErrorStatus: type: string enum: [error] PathSourceType: type: string enum: - hlsSource - redirect - rpiCameraSource - rtmpConn - rtmpsConn - rtmpSource - rtspSession - rtspSource - rtspsSession - srtConn - srtSource - mpegtsSource - rtpSource - webRTCSession - webRTCSource PathReaderType: type: string enum: - hlsMuxer - rpiCameraSecondary - rtmpConn - rtmpsConn - rtspConn - rtspSession - rtspsConn - rtspsSession - srtConn - webRTCSession PathTrackCodec: type: string enum: - AV1 - VP9 - VP8 - H265 - H264 - MPEG-4 Video - MPEG-1 Video - MJPEG - Opus - Vorbis - MPEG-4 Audio - MPEG-4 Audio LATM - MPEG-1 Audio - AC3 - Speex - G726 - G722 - G711 - LPCM - MPEG-TS - KLV - Generic AlwaysAvailableTrackCodec: type: string enum: - AV1 - VP9 - H265 - H264 - MPEG4Audio - Opus - G711 - LPCM AuthAction: type: string enum: - publish - read - playback - api - metrics - pprof AuthMethod: type: string enum: [internal, http, jwt] Encryption: type: string enum: [no, optional, strict] HLSVariant: type: string enum: [mpegts, fmp4, lowLatency] LogDestination: type: string enum: [stdout, file, syslog] LogLevel: type: string enum: [error, warn, info, debug] RecordFormat: type: string enum: [fmp4, mpegts] RTSPAuthMethod: type: string enum: [basic, digest] RTSPRangeType: type: string enum: ['', clock, npt, smpte] RTSPTransport: type: string enum: [udp, multicast, tcp, automatic] RTMPConnState: type: string enum: [idle, read, publish] RTSPSessionState: type: string enum: [idle, read, publish] SRTConnState: type: string enum: [idle, read, publish] WebRTCSessionState: type: string enum: [read, publish] OK: type: object properties: status: $ref: '#/components/schemas/OKStatus' Error: type: object properties: status: $ref: '#/components/schemas/ErrorStatus' error: type: string Info: type: object properties: version: type: string started: type: string AuthInternalUser: type: object properties: user: type: string pass: type: string ips: type: array items: type: string permissions: type: array items: $ref: '#/components/schemas/AuthInternalUserPermission' AuthInternalUserPermission: type: object properties: action: $ref: '#/components/schemas/AuthAction' path: type: string GlobalConf: type: object properties: # General logLevel: $ref: '#/components/schemas/LogLevel' logDestinations: type: array items: $ref: '#/components/schemas/LogDestination' logStructured: type: boolean logFile: type: string sysLogPrefix: type: string dumpPackets: type: boolean readTimeout: type: string writeTimeout: type: string readBufferCount: type: integer format: int64 nullable: true deprecated: true writeQueueSize: type: integer format: int64 udpMaxPayloadSize: type: integer format: int64 udpReadBufferSize: type: integer format: uint64 runOnConnect: type: string runOnConnectRestart: type: boolean runOnDisconnect: type: string # Authentication authMethod: $ref: '#/components/schemas/AuthMethod' authInternalUsers: type: array items: $ref: '#/components/schemas/AuthInternalUser' authHTTPAddress: type: string externalAuthenticationURL: type: string nullable: true deprecated: true authHTTPFingerprint: type: string authHTTPExclude: type: array items: $ref: '#/components/schemas/AuthInternalUserPermission' authJWTJWKS: type: string authJWTJWKSFingerprint: type: string authJWTClaimKey: type: string authJWTIssuer: type: string authJWTAudience: type: string authJWTExclude: type: array items: $ref: '#/components/schemas/AuthInternalUserPermission' authJWTInHTTPQuery: type: boolean # Control API api: type: boolean apiAddress: type: string apiEncryption: type: boolean apiServerKey: type: string apiServerCert: type: string apiAllowOrigin: type: string nullable: true deprecated: true apiAllowOrigins: type: array items: type: string apiTrustedProxies: type: array items: type: string # Metrics metrics: type: boolean metricsAddress: type: string metricsEncryption: type: boolean metricsServerKey: type: string metricsServerCert: type: string metricsAllowOrigin: type: string nullable: true deprecated: true metricsAllowOrigins: type: array items: type: string metricsTrustedProxies: type: array items: type: string # PPROF pprof: type: boolean pprofAddress: type: string pprofEncryption: type: boolean pprofServerKey: type: string pprofServerCert: type: string pprofAllowOrigin: type: string nullable: true deprecated: true pprofAllowOrigins: type: array items: type: string pprofTrustedProxies: type: array items: type: string # Playback server playback: type: boolean playbackAddress: type: string playbackEncryption: type: boolean playbackServerKey: type: string playbackServerCert: type: string playbackAllowOrigin: type: string nullable: true deprecated: true playbackAllowOrigins: type: array items: type: string playbackTrustedProxies: type: array items: type: string # RTSP server rtsp: type: boolean rtspDisable: type: boolean nullable: true deprecated: true protocols: type: array nullable: true items: type: string enum: [udp, multicast, tcp] deprecated: true rtspTransports: type: array items: type: string enum: [udp, multicast, tcp] encryption: type: string nullable: true deprecated: true allOf: - $ref: '#/components/schemas/Encryption' rtspEncryption: $ref: '#/components/schemas/Encryption' rtspAddress: type: string rtspsAddress: type: string rtpAddress: type: string rtcpAddress: type: string multicastIPRange: type: string multicastRTPPort: type: integer format: int64 multicastRTCPPort: type: integer format: int64 srtpAddress: type: string srtcpAddress: type: string multicastSRTPPort: type: integer format: int64 multicastSRTCPPort: type: integer format: int64 rtspServerKey: type: string rtspServerCert: type: string authMethods: type: array nullable: true items: $ref: '#/components/schemas/RTSPAuthMethod' deprecated: true rtspAuthMethods: type: array items: $ref: '#/components/schemas/RTSPAuthMethod' rtspUDPReadBufferSize: type: integer format: uint64 nullable: true deprecated: true # RTMP server rtmp: type: boolean rtmpDisable: type: boolean nullable: true deprecated: true rtmpEncryption: $ref: '#/components/schemas/Encryption' rtmpAddress: type: string rtmpsAddress: type: string rtmpServerKey: type: string rtmpServerCert: type: string # HLS server hls: type: boolean hlsDisable: type: boolean nullable: true deprecated: true hlsAddress: type: string hlsEncryption: type: boolean hlsServerKey: type: string hlsServerCert: type: string hlsAllowOrigin: type: string nullable: true deprecated: true hlsAllowOrigins: type: array items: type: string hlsTrustedProxies: type: array items: type: string hlsAlwaysRemux: type: boolean hlsVariant: $ref: '#/components/schemas/HLSVariant' hlsSegmentCount: type: integer format: int64 hlsSegmentDuration: type: string hlsPartDuration: type: string hlsSegmentMaxSize: type: string hlsDirectory: type: string hlsMuxerCloseAfter: type: string # WebRTC server webrtc: type: boolean webrtcDisable: type: boolean nullable: true deprecated: true webrtcAddress: type: string webrtcEncryption: type: boolean webrtcServerKey: type: string webrtcServerCert: type: string webrtcAllowOrigin: type: string nullable: true deprecated: true webrtcAllowOrigins: type: array items: type: string webrtcTrustedProxies: type: array items: type: string webrtcLocalUDPAddress: type: string webrtcLocalTCPAddress: type: string webrtcIPsFromInterfaces: type: boolean webrtcIPsFromInterfacesList: type: array items: type: string webrtcAdditionalHosts: type: array items: type: string webrtcICEServers2: type: array items: $ref: '#/components/schemas/WebRTCICEServer' webrtcSTUNGatherTimeout: type: string webrtcHandshakeTimeout: type: string webrtcTrackGatherTimeout: type: string webrtcICEUDPMuxAddress: type: string nullable: true deprecated: true webrtcICETCPMuxAddress: type: string nullable: true deprecated: true webrtcICEHostNAT1To1IPs: type: array nullable: true items: type: string deprecated: true webrtcICEServers: type: array nullable: true items: type: string deprecated: true # SRT server srt: type: boolean srtAddress: type: string # Record (deprecated) record: type: boolean nullable: true deprecated: true recordPath: type: string nullable: true deprecated: true recordFormat: type: string nullable: true deprecated: true allOf: - $ref: '#/components/schemas/RecordFormat' recordPartDuration: type: string nullable: true deprecated: true recordSegmentDuration: type: string nullable: true deprecated: true recordDeleteAfter: type: string nullable: true deprecated: true PathConf: type: object properties: name: type: string # General source: type: string sourceFingerprint: type: string sourceOnDemand: type: boolean sourceOnDemandStartTimeout: type: string sourceOnDemandCloseAfter: type: string maxReaders: type: integer format: int64 srtReadPassphrase: type: string fallback: type: string nullable: true deprecated: true useAbsoluteTimestamp: type: boolean # Always available alwaysAvailable: type: boolean alwaysAvailableTracks: type: array items: $ref: '#/components/schemas/AlwaysAvailableTrack' alwaysAvailableFile: type: string # Record record: type: boolean playback: type: boolean nullable: true deprecated: true recordPath: type: string recordFormat: $ref: '#/components/schemas/RecordFormat' recordPartDuration: type: string recordMaxPartSize: type: string recordSegmentDuration: type: string recordDeleteAfter: type: string # Authentication (deprecated) publishUser: type: string nullable: true deprecated: true publishPass: type: string nullable: true deprecated: true publishIPs: type: array nullable: true items: type: string deprecated: true readUser: type: string nullable: true deprecated: true readPass: type: string nullable: true deprecated: true readIPs: type: array nullable: true items: type: string deprecated: true # Publisher source overridePublisher: type: boolean disablePublisherOverride: type: boolean nullable: true deprecated: true srtPublishPassphrase: type: string rtspDemuxMpegts: type: boolean # RTSP source rtspTransport: $ref: '#/components/schemas/RTSPTransport' rtspAnyPort: type: boolean sourceProtocol: type: string nullable: true deprecated: true allOf: - $ref: '#/components/schemas/RTSPTransport' sourceAnyPortEnable: type: boolean nullable: true deprecated: true rtspRangeType: $ref: '#/components/schemas/RTSPRangeType' rtspRangeStart: type: string rtspUDPReadBufferSize: type: integer format: uint64 nullable: true deprecated: true rtspUDPSourcePortRange: type: array minItems: 2 maxItems: 2 items: type: integer format: uint64 # MPEG-TS source mpegtsUDPReadBufferSize: type: integer format: uint64 nullable: true deprecated: true # RTP source rtpSDP: type: string rtpUDPReadBufferSize: type: integer format: uint64 nullable: true deprecated: true # WHEP source whepBearerToken: type: string whepSTUNGatherTimeout: type: string whepHandshakeTimeout: type: string whepTrackGatherTimeout: type: string # Redirect source sourceRedirect: type: string # Raspberry Pi Camera source rpiCameraCamID: type: integer format: uint64 rpiCameraSecondary: type: boolean rpiCameraWidth: type: integer format: uint64 rpiCameraHeight: type: integer format: uint64 rpiCameraHFlip: type: boolean rpiCameraVFlip: type: boolean rpiCameraBrightness: type: number format: double rpiCameraContrast: type: number format: double rpiCameraSaturation: type: number format: double rpiCameraSharpness: type: number format: double rpiCameraExposure: type: string rpiCameraAWB: type: string rpiCameraAWBGains: type: array minItems: 2 maxItems: 2 items: type: number format: double rpiCameraDenoise: type: string rpiCameraShutter: type: integer format: uint64 rpiCameraMetering: type: string rpiCameraGain: type: number format: double rpiCameraEV: type: number format: double rpiCameraROI: type: string rpiCameraHDR: type: boolean rpiCameraTuningFile: type: string rpiCameraMode: type: string rpiCameraFPS: type: number format: double rpiCameraAfMode: type: string rpiCameraAfRange: type: string rpiCameraAfSpeed: type: string rpiCameraLensPosition: type: number format: double rpiCameraAfWindow: type: string rpiCameraFlickerPeriod: type: integer format: uint64 rpiCameraTextOverlayEnable: type: boolean rpiCameraTextOverlay: type: string rpiCameraCodec: type: string rpiCameraIDRPeriod: type: integer format: uint64 rpiCameraBitrate: type: integer format: uint64 rpiCameraProfile: type: string nullable: true deprecated: true rpiCameraLevel: type: string nullable: true deprecated: true rpiCameraHardwareH264Profile: type: string rpiCameraHardwareH264Level: type: string rpiCameraSoftwareH264Profile: type: string rpiCameraSoftwareH264Level: type: string rpiCameraJPEGQuality: type: integer format: uint64 nullable: true deprecated: true rpiCameraMJPEGQuality: type: integer format: uint64 # Hooks runOnInit: type: string runOnInitRestart: type: boolean runOnDemand: type: string runOnDemandRestart: type: boolean runOnDemandStartTimeout: type: string runOnDemandCloseAfter: type: string runOnUnDemand: type: string runOnReady: type: string runOnReadyRestart: type: boolean runOnNotReady: type: string runOnRead: type: string runOnReadRestart: type: boolean runOnUnread: type: string runOnRecordSegmentCreate: type: string runOnRecordSegmentComplete: type: string PathConfList: type: object properties: pageCount: type: integer format: int64 itemCount: type: integer format: int64 items: type: array items: $ref: '#/components/schemas/PathConf' Path: type: object properties: name: type: string confName: type: string source: type: object nullable: true allOf: - $ref: '#/components/schemas/PathSource' ready: type: boolean deprecated: true readyTime: type: string nullable: true deprecated: true available: type: boolean availableTime: type: string nullable: true online: type: boolean onlineTime: type: string nullable: true tracks: type: array deprecated: true items: $ref: '#/components/schemas/PathTrackCodec' tracks2: type: array items: $ref: '#/components/schemas/PathTrack' inboundBytes: type: integer format: uint64 outboundBytes: type: integer format: uint64 inboundFramesInError: type: integer format: uint64 bytesReceived: type: integer format: uint64 deprecated: true bytesSent: type: integer format: uint64 deprecated: true readers: type: array items: $ref: '#/components/schemas/PathReader' PathList: type: object properties: pageCount: type: integer format: int64 itemCount: type: integer format: int64 items: type: array items: $ref: '#/components/schemas/Path' PathSource: type: object properties: type: $ref: '#/components/schemas/PathSourceType' id: type: string PathReader: type: object properties: type: $ref: '#/components/schemas/PathReaderType' id: type: string PathTrack: type: object properties: codec: $ref: '#/components/schemas/PathTrackCodec' codecProps: type: object nullable: true allOf: - $ref: '#/components/schemas/PathTrackCodecProps' PathTrackCodecProps: oneOf: - $ref: '#/components/schemas/PathTrackCodecPropsAV1' - $ref: '#/components/schemas/PathTrackCodecPropsVP9' - $ref: '#/components/schemas/PathTrackCodecPropsH265' - $ref: '#/components/schemas/PathTrackCodecPropsH264' - $ref: '#/components/schemas/PathTrackCodecPropsOpus' - $ref: '#/components/schemas/PathTrackCodecPropsMPEG4Audio' - $ref: '#/components/schemas/PathTrackCodecPropsAC3' - $ref: '#/components/schemas/PathTrackCodecPropsG711' - $ref: '#/components/schemas/PathTrackCodecPropsLPCM' PathTrackCodecPropsAV1: type: object properties: width: type: integer format: int64 height: type: integer format: int64 profile: type: integer format: int64 level: type: integer format: int64 tier: type: integer format: int64 PathTrackCodecPropsVP9: type: object properties: profile: type: integer format: int64 PathTrackCodecPropsH265: type: object properties: width: type: integer format: int64 height: type: integer format: int64 profile: type: string level: type: string PathTrackCodecPropsH264: type: object properties: width: type: integer format: int64 height: type: integer format: int64 profile: type: string level: type: string PathTrackCodecPropsOpus: type: object properties: channelCount: type: integer format: int64 PathTrackCodecPropsMPEG4Audio: type: object properties: sampleRate: type: integer format: int64 channelCount: type: integer format: int64 PathTrackCodecPropsAC3: type: object properties: sampleRate: type: integer format: int64 channelCount: type: integer format: int64 PathTrackCodecPropsG711: type: object properties: muLaw: type: boolean sampleRate: type: integer format: int64 channelCount: type: integer format: int64 PathTrackCodecPropsLPCM: type: object properties: bitDepth: type: integer format: int64 sampleRate: type: integer format: int64 channelCount: type: integer format: int64 HLSMuxer: type: object properties: path: type: string created: type: string lastRequest: type: string outboundBytes: type: integer format: uint64 outboundFramesDiscarded: type: integer format: uint64 bytesSent: type: integer format: uint64 deprecated: true HLSMuxerList: type: object properties: pageCount: type: integer format: int64 itemCount: type: integer format: int64 items: type: array items: $ref: '#/components/schemas/HLSMuxer' Recording: type: object properties: name: type: string segments: type: array items: $ref: '#/components/schemas/RecordingSegment' RecordingList: type: object properties: pageCount: type: integer format: int64 itemCount: type: integer format: int64 items: type: array items: $ref: '#/components/schemas/Recording' RecordingSegment: type: object properties: start: type: string RTMPConn: type: object properties: id: type: string format: uuid created: type: string remoteAddr: type: string state: $ref: '#/components/schemas/RTMPConnState' path: type: string query: type: string user: type: string inboundBytes: type: integer format: uint64 outboundBytes: type: integer format: uint64 outboundFramesDiscarded: type: integer format: uint64 bytesReceived: type: integer format: uint64 deprecated: true bytesSent: type: integer format: uint64 deprecated: true RTMPConnList: type: object properties: pageCount: type: integer format: int64 itemCount: type: integer format: int64 items: type: array items: $ref: '#/components/schemas/RTMPConn' RTSPConn: type: object properties: id: type: string format: uuid created: type: string remoteAddr: type: string session: type: string format: uuid nullable: true tunnel: type: string inboundBytes: type: integer format: uint64 outboundBytes: type: integer format: uint64 bytesReceived: type: integer format: uint64 deprecated: true bytesSent: type: integer format: uint64 deprecated: true RTSPConnList: type: object properties: pageCount: type: integer format: int64 itemCount: type: integer format: int64 items: type: array items: $ref: '#/components/schemas/RTSPConn' RTSPSession: type: object properties: id: type: string format: uuid created: type: string remoteAddr: type: string state: $ref: '#/components/schemas/RTSPSessionState' path: type: string query: type: string user: type: string transport: type: string nullable: true profile: type: string nullable: true conns: type: array items: type: string format: uuid inboundBytes: type: integer format: uint64 inboundRTPPackets: type: integer format: uint64 inboundRTPPacketsLost: type: integer format: uint64 inboundRTPPacketsInError: type: integer format: uint64 inboundRTPPacketsJitter: type: number format: double inboundRTCPPackets: type: integer format: uint64 inboundRTCPPacketsInError: type: integer format: uint64 outboundBytes: type: integer format: uint64 outboundRTPPackets: type: integer format: uint64 outboundRTPPacketsReportedLost: type: integer format: uint64 outboundRTPPacketsDiscarded: type: integer format: uint64 outboundRTCPPackets: type: integer format: uint64 bytesReceived: type: integer format: uint64 deprecated: true bytesSent: type: integer format: uint64 deprecated: true rtpPacketsReceived: type: integer format: uint64 deprecated: true rtpPacketsSent: type: integer format: uint64 deprecated: true rtpPacketsLost: type: integer format: uint64 deprecated: true rtpPacketsInError: type: integer format: uint64 deprecated: true rtpPacketsJitter: type: number format: double deprecated: true rtcpPacketsReceived: type: integer format: uint64 deprecated: true rtcpPacketsSent: type: integer format: uint64 deprecated: true rtcpPacketsInError: type: integer format: uint64 deprecated: true RTSPSessionList: type: object properties: pageCount: type: integer format: int64 itemCount: type: integer format: int64 items: type: array items: $ref: '#/components/schemas/RTSPSession' SRTConn: type: object properties: id: type: string format: uuid created: type: string remoteAddr: type: string state: $ref: '#/components/schemas/SRTConnState' path: type: string query: type: string user: type: string packetsSent: type: integer format: uint64 description: The total number of sent DATA packets, including retransmitted packets packetsReceived: type: integer format: uint64 description: The total number of received DATA packets, including retransmitted packets packetsReceivedBelated: type: integer format: uint64 packetsSentUnique: type: integer format: uint64 description: The total number of unique DATA packets sent by the SRT sender packetsReceivedUnique: type: integer format: uint64 description: The total number of unique original, retransmitted or recovered by the packet filter DATA packets received in time, decrypted without errors and, as a result, scheduled for delivery to the upstream application by the SRT receiver. packetsSendLoss: type: integer format: uint64 description: The total number of data packets considered or reported as lost at the sender side. Does not correspond to the packets detected as lost at the receiver side. packetsReceivedLoss: type: integer format: uint64 description: The total number of SRT DATA packets detected as presently missing (either reordered or lost) at the receiver side packetsRetrans: type: integer format: uint64 description: The total number of retransmitted packets sent by the SRT sender packetsReceivedRetrans: type: integer format: uint64 description: The total number of retransmitted packets registered at the receiver side packetsSentACK: type: integer format: uint64 description: The total number of sent ACK (Acknowledgement) control packets packetsReceivedACK: type: integer format: uint64 description: The total number of received ACK (Acknowledgement) control packets packetsSentNAK: type: integer format: uint64 description: The total number of sent NAK (Negative Acknowledgement) control packets packetsReceivedNAK: type: integer format: uint64 description: The total number of received NAK (Negative Acknowledgement) control packets packetsSentKM: type: integer format: uint64 description: The total number of sent KM (Key Material) control packets packetsReceivedKM: type: integer format: uint64 description: The total number of received KM (Key Material) control packets usSndDuration: type: integer format: uint64 description: The total accumulated time in microseconds, during which the SRT sender has some data to transmit, including packets that have been sent, but not yet acknowledged packetsSendDrop: type: integer format: uint64 description: The total number of dropped by the SRT sender DATA packets that have no chance to be delivered in time packetsReceivedDrop: type: integer format: uint64 description: The total number of dropped by the SRT receiver and, as a result, not delivered to the upstream application DATA packets packetsReceivedUndecrypt: type: integer format: uint64 description: The total number of packets that failed to be decrypted at the receiver side bytesSent: type: integer format: uint64 description: Same as packetsSent, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) bytesReceived: type: integer format: uint64 description: Same as packetsReceived, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) bytesReceivedBelated: type: integer format: uint64 bytesSentUnique: type: integer format: uint64 description: Same as packetsSentUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) bytesReceivedUnique: type: integer format: uint64 description: Same as packetsReceivedUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) bytesReceivedLoss: type: integer format: uint64 description: Same as packetsReceivedLoss, but expressed in bytes, including payload and all the headers (IP, TCP, SRT), bytes for the presently missing (either reordered or lost) packets' payloads are estimated based on the average packet size bytesRetrans: type: integer format: uint64 description: Same as packetsRetrans, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) bytesReceivedRetrans: type: integer format: uint64 description: Same as packetsReceivedRetrans, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) bytesSendDrop: type: integer format: uint64 description: Same as packetsSendDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) bytesReceivedDrop: type: integer format: uint64 description: Same as packetsReceivedDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) bytesReceivedUndecrypt: type: integer format: uint64 description: Same as packetsReceivedUndecrypt, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) usPacketsSendPeriod: type: number format: double description: Current minimum time interval between which consecutive packets are sent, in microseconds packetsFlowWindow: type: integer format: uint64 description: The maximum number of packets that can be "in flight" packetsFlightSize: type: integer format: uint64 description: The number of packets in flight msRTT: type: number format: double description: Smoothed round-trip time (SRTT), an exponentially-weighted moving average (EWMA) of an endpoint's RTT samples, in milliseconds mbpsSendRate: type: number format: double description: Current transmission bandwidth, in Mbps mbpsReceiveRate: type: number format: double description: Current receiving bandwidth, in Mbps mbpsLinkCapacity: type: number format: double description: Estimated capacity of the network link, in Mbps bytesAvailSendBuf: type: integer format: uint64 description: The available space in the sender's buffer, in bytes bytesAvailReceiveBuf: type: integer format: uint64 description: The available space in the receiver's buffer, in bytes mbpsMaxBW: type: number format: double description: Transmission bandwidth limit, in Mbps byteMSS: type: integer format: uint64 description: Maximum Segment Size (MSS), in bytes packetsSendBuf: type: integer format: uint64 description: The number of packets in the sender's buffer that are already scheduled for sending or even possibly sent, but not yet acknowledged bytesSendBuf: type: integer format: uint64 description: Instantaneous (current) value of packetsSndBuf, but expressed in bytes, including payload and all headers (IP, TCP, SRT) msSendBuf: type: integer format: uint64 description: The timespan (msec) of packets in the sender's buffer (unacknowledged packets) msSendTsbPdDelay: type: integer format: uint64 description: Timestamp-based Packet Delivery Delay value of the peer packetsReceiveBuf: type: integer format: uint64 description: The number of acknowledged packets in receiver's buffer bytesReceiveBuf: type: integer format: uint64 description: Instantaneous (current) value of packetsRcvBuf, expressed in bytes, including payload and all headers (IP, TCP, SRT) msReceiveBuf: type: integer format: uint64 description: The timespan (msec) of acknowledged packets in the receiver's buffer msReceiveTsbPdDelay: type: integer format: uint64 description: Timestamp-based Packet Delivery Delay value set on the socket via SRTO_RCVLATENCY or SRTO_LATENCY packetsReorderTolerance: type: integer format: uint64 description: Instant value of the packet reorder tolerance packetsReceivedAvgBelatedTime: type: integer format: uint64 description: Accumulated difference between the current time and the time-to-play of a packet that is received late packetsSendLossRate: type: number format: double description: Percentage of resent data vs. sent data packetsReceivedLossRate: type: number format: double description: Percentage of retransmitted data vs. received data outboundFramesDiscarded: type: integer format: uint64 SRTConnList: type: object properties: pageCount: type: integer format: int64 itemCount: type: integer format: int64 items: type: array items: $ref: '#/components/schemas/SRTConn' AlwaysAvailableTrack: type: object properties: codec: $ref: '#/components/schemas/AlwaysAvailableTrackCodec' sampleRate: type: integer format: int64 channelCount: type: integer format: int64 muLaw: type: boolean WebRTCICEServer: type: object properties: url: type: string username: type: string password: type: string clientOnly: type: boolean WebRTCSession: type: object properties: id: type: string format: uuid created: type: string remoteAddr: type: string peerConnectionEstablished: type: boolean localCandidate: type: string remoteCandidate: type: string state: $ref: '#/components/schemas/WebRTCSessionState' path: type: string query: type: string user: type: string inboundBytes: type: integer format: uint64 inboundRTPPackets: type: integer format: uint64 inboundRTPPacketsLost: type: integer format: uint64 inboundRTPPacketsJitter: type: number format: double inboundRTCPPackets: type: integer format: uint64 outboundBytes: type: integer format: uint64 outboundRTPPackets: type: integer format: uint64 outboundRTCPPackets: type: integer format: uint64 outboundFramesDiscarded: type: integer format: uint64 bytesReceived: type: integer format: uint64 deprecated: true bytesSent: type: integer format: uint64 deprecated: true rtpPacketsReceived: type: integer format: uint64 deprecated: true rtpPacketsSent: type: integer format: uint64 deprecated: true rtpPacketsLost: type: integer format: uint64 deprecated: true rtpPacketsJitter: type: number format: double deprecated: true rtcpPacketsReceived: type: integer format: uint64 deprecated: true rtcpPacketsSent: type: integer format: uint64 deprecated: true WebRTCSessionList: type: object properties: pageCount: type: integer format: int64 itemCount: type: integer format: int64 items: type: array items: $ref: '#/components/schemas/WebRTCSession' paths: /v3/info: get: operationId: info tags: [General] summary: returns informations about the instance. responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/Info' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/auth/jwks/refresh: post: operationId: authJwksRefresh tags: [Authentication] summary: Manually refreshes the JWT JWKS. responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/OK' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/config/global/get: get: operationId: configGlobalGet tags: [Configuration] summary: returns the global configuration. description: '' responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/GlobalConf' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/config/global/patch: patch: operationId: configGlobalSet tags: [Configuration] summary: patches the global configuration. description: all fields are optional. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/GlobalConf' responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/OK' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/config/pathdefaults/get: get: operationId: configPathDefaultsGet tags: [Configuration] summary: returns the default path configuration. description: '' responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/PathConf' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/config/pathdefaults/patch: patch: operationId: configPathDefaultsPatch tags: [Configuration] summary: patches the default path configuration. description: all fields are optional. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PathConf' responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/OK' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/config/paths/list: get: operationId: configPathsList tags: [Configuration] summary: returns all path configurations. description: '' parameters: - name: page in: query description: page number. schema: type: integer default: 0 - name: itemsPerPage in: query description: items per page. schema: type: integer default: 100 responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/PathConfList' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/config/paths/get/{name}: get: operationId: configPathsGet tags: [Configuration] summary: returns a path configuration. description: '' parameters: - name: name in: path required: true description: the name of the path. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/PathConf' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: path not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/config/paths/add/{name}: post: operationId: configPathsAdd tags: [Configuration] summary: adds a path configuration. description: all fields are optional. parameters: - name: name in: path required: true description: the name of the path. schema: type: string requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PathConf' responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/OK' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/config/paths/patch/{name}: patch: operationId: configPathsPatch tags: [Configuration] summary: patches a path configuration. description: all fields are optional. parameters: - name: name in: path required: true description: the name of the path. schema: type: string requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PathConf' responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/OK' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: path not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/config/paths/replace/{name}: post: operationId: configPathsReplace tags: [Configuration] summary: replaces all values of a path configuration. description: all fields are optional. parameters: - name: name in: path required: true description: the name of the path. schema: type: string requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PathConf' responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/OK' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: path not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/config/paths/delete/{name}: delete: operationId: configPathsDelete tags: [Configuration] summary: removes a path configuration. description: '' parameters: - name: name in: path required: true description: the name of the path. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/OK' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: path not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/hlsmuxers/list: get: operationId: hlsMuxersList tags: [HLS] summary: returns all HLS muxers. description: '' parameters: - name: page in: query description: page number. schema: type: integer default: 0 - name: itemsPerPage in: query description: items per page. schema: type: integer default: 100 responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/HLSMuxerList' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/hlsmuxers/get/{name}: get: operationId: hlsMuxersGet tags: [HLS] summary: returns a HLS muxer. description: '' parameters: - name: name in: path required: true description: name of the muxer. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/HLSMuxer' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: muxer not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/paths/list: get: operationId: pathsList tags: [Paths] summary: returns all paths. description: '' parameters: - name: page in: query description: page number. schema: type: integer default: 0 - name: itemsPerPage in: query description: items per page. schema: type: integer default: 100 responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/PathList' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/paths/get/{name}: get: operationId: pathsGet tags: [Paths] summary: returns a path. description: '' parameters: - name: name in: path required: true description: name of the path. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/Path' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: path not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/rtspconns/list: get: operationId: rtspConnsList tags: [RTSP] summary: returns all RTSP connections. description: '' parameters: - name: page in: query description: page number. schema: type: integer default: 0 - name: itemsPerPage in: query description: items per page. schema: type: integer default: 100 responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/RTSPConnList' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/rtspconns/get/{id}: get: operationId: rtspConnsGet tags: [RTSP] summary: returns a RTSP connection. description: '' parameters: - name: id in: path required: true description: ID of the connection. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/RTSPConn' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: connection not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/rtspsessions/list: get: operationId: rtspSessionsList tags: [RTSP] summary: returns all RTSP sessions. description: '' parameters: - name: page in: query description: page number. schema: type: integer default: 0 - name: itemsPerPage in: query description: items per page. schema: type: integer default: 100 responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/RTSPSessionList' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/rtspsessions/get/{id}: get: operationId: rtspSessionsGet tags: [RTSP] summary: returns a RTSP session. description: '' parameters: - name: id in: path required: true description: ID of the connection. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/RTSPSession' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: session not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/rtspsessions/kick/{id}: post: operationId: rtspSessionsKick tags: [RTSP] summary: kicks out a RTSP session from the server. description: '' parameters: - name: id in: path required: true description: ID of the session. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/OK' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: session not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/rtspsconns/list: get: operationId: rtspsConnsList tags: [RTSP] summary: returns all RTSPS connections. description: '' parameters: - name: page in: query description: page number. schema: type: integer default: 0 - name: itemsPerPage in: query description: items per page. schema: type: integer default: 100 responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/RTSPConnList' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/rtspsconns/get/{id}: get: operationId: rtspsConnsGet tags: [RTSP] summary: returns a RTSPS connection. description: '' parameters: - name: id in: path required: true description: ID of the connection. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/RTSPConn' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: connection not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/rtspssessions/list: get: operationId: rtspsSessionsList tags: [RTSP] summary: returns all RTSPS sessions. description: '' parameters: - name: page in: query description: page number. schema: type: integer default: 0 - name: itemsPerPage in: query description: items per page. schema: type: integer default: 100 responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/RTSPSessionList' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/rtspssessions/get/{id}: get: operationId: rtspsSessionsGet tags: [RTSP] summary: returns a RTSPS session. description: '' parameters: - name: id in: path required: true description: ID of the connection. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/RTSPSession' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: session not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/rtspssessions/kick/{id}: post: operationId: rtspsSessionsKick tags: [RTSP] summary: kicks out a RTSPS session from the server. description: '' parameters: - name: id in: path required: true description: ID of the session. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/OK' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: session not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/rtmpconns/list: get: operationId: rtmpConnsList tags: [RTMP] summary: returns all RTMP connections. description: '' parameters: - name: page in: query description: page number. schema: type: integer default: 0 - name: itemsPerPage in: query description: items per page. schema: type: integer default: 100 responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/RTMPConnList' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/rtmpconns/get/{id}: get: operationId: rtmpConnectionsGet tags: [RTMP] summary: returns a RTMP connection. description: '' parameters: - name: id in: path required: true description: ID of the connection. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/RTMPConn' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: connection not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/rtmpconns/kick/{id}: post: operationId: rtmpConnsKick tags: [RTMP] summary: kicks out a RTMP connection from the server. description: '' parameters: - name: id in: path required: true description: ID of the connection. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/OK' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: connection not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/rtmpsconns/list: get: operationId: rtmpsConnsList tags: [RTMP] summary: returns all RTMPS connections. description: '' parameters: - name: page in: query description: page number. schema: type: integer default: 0 - name: itemsPerPage in: query description: items per page. schema: type: integer default: 100 responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/RTMPConnList' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/rtmpsconns/get/{id}: get: operationId: rtmpsConnectionsGet tags: [RTMP] summary: returns a RTMPS connection. description: '' parameters: - name: id in: path required: true description: ID of the connection. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/RTMPConn' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: connection not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/rtmpsconns/kick/{id}: post: operationId: rtmpsConnsKick tags: [RTMP] summary: kicks out a RTMPS connection from the server. description: '' parameters: - name: id in: path required: true description: ID of the connection. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/OK' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: connection not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/srtconns/list: get: operationId: srtConnsList tags: [SRT] summary: returns all SRT connections. description: '' parameters: - name: page in: query description: page number. schema: type: integer default: 0 - name: itemsPerPage in: query description: items per page. schema: type: integer default: 100 responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/SRTConnList' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/srtconns/get/{id}: get: operationId: srtConnsGet tags: [SRT] summary: returns a SRT connection. description: '' parameters: - name: id in: path required: true description: ID of the connection. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/SRTConn' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: connection not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/srtconns/kick/{id}: post: operationId: srtConnsKick tags: [SRT] summary: kicks out a SRT connection from the server. description: '' parameters: - name: id in: path required: true description: ID of the connection. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/OK' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: connection not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/webrtcsessions/list: get: operationId: webrtcSessionsList tags: [WebRTC] summary: returns all WebRTC sessions. description: '' parameters: - name: page in: query description: page number. schema: type: integer default: 0 - name: itemsPerPage in: query description: items per page. schema: type: integer default: 100 responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/WebRTCSessionList' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/webrtcsessions/get/{id}: get: operationId: webrtcSessionsGet tags: [WebRTC] summary: returns a WebRTC session. description: '' parameters: - name: id in: path required: true description: ID of the session. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/WebRTCSession' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: session not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/webrtcsessions/kick/{id}: post: operationId: webrtcSessionsKick tags: [WebRTC] summary: kicks out a WebRTC session from the server. description: '' parameters: - name: id in: path required: true description: ID of the session. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/OK' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: session not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/recordings/list: get: operationId: recordingsList tags: [Recordings] summary: returns all recordings, splitted by path. description: '' parameters: - name: page in: query description: page number. schema: type: integer default: 0 - name: itemsPerPage in: query description: items per page. schema: type: integer default: 100 responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/RecordingList' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/recordings/get/{name}: get: operationId: recordingsGet tags: [Recordings] summary: returns recordings of a path. description: '' parameters: - name: name in: path required: true description: name of the path. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/Recording' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: path not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' /v3/recordings/deletesegment: delete: operationId: recordingsDeleteSegment tags: [Recordings] summary: deletes a recording segment. description: '' parameters: - name: path in: query required: true description: path. schema: type: string - name: start in: query required: true description: starting date of the segment. schema: type: string responses: '200': description: the request was successful. content: application/json: schema: $ref: '#/components/schemas/OK' '400': description: invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: connection not found. content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: server error. content: application/json: schema: $ref: '#/components/schemas/Error' ================================================ FILE: docker/ffmpeg-rpi.Dockerfile ================================================ ################################################################# FROM --platform=linux/amd64 scratch AS binaries ADD binaries/mediamtx_*_linux_armv6.tar.gz /linux/arm/v6 ADD binaries/mediamtx_*_linux_armv7.tar.gz /linux/arm/v7 ADD binaries/mediamtx_*_linux_arm64.tar.gz /linux/arm64 ################################################################# FROM --platform=linux/arm/v7 debian:bullseye-slim AS base-arm-v7 # even though the base image is arm v7, # Raspbian libraries and compilers provide arm v6 compatibility. RUN apt update \ && apt install -y wget gpg \ && echo "deb http://archive.raspbian.org/raspbian bullseye main rpi firmware" > /etc/apt/sources.list \ && echo "deb http://archive.raspberrypi.org/debian bullseye main" > /etc/apt/sources.list.d/raspi.list \ && wget -O- https://archive.raspbian.org/raspbian.public.key | gpg --dearmor -o /etc/apt/trusted.gpg.d/raspbian.gpg \ && wget -O- https://archive.raspberrypi.org/debian/raspberrypi.gpg.key | gpg --dearmor -o /etc/apt/trusted.gpg.d/raspberrypi.gpg \ && rm -rf /var/lib/apt/lists/* RUN apt update && apt install --reinstall -y \ libc6 \ libc-bin \ libstdc++6 \ && rm -rf /var/lib/apt/lists/* ################################################################# FROM --platform=linux/arm64 debian:bullseye-slim AS base-arm64 RUN apt update \ && apt install -y wget gpg \ && echo "deb http://archive.raspberrypi.org/debian bullseye main" > /etc/apt/sources.list.d/raspi.list \ && wget -O- https://archive.raspberrypi.org/debian/raspberrypi.gpg.key | gpg --dearmor -o /etc/apt/trusted.gpg.d/raspberrypi.gpg \ && rm -rf /var/lib/apt/lists/* ################################################################# FROM --platform=linux/amd64 scratch AS base COPY --from=base-arm-v7 / /linux/arm/v6 COPY --from=base-arm-v7 / /linux/arm/v7 COPY --from=base-arm64 / /linux/arm64 ################################################################# FROM scratch ARG TARGETPLATFORM COPY --from=base /$TARGETPLATFORM / RUN apt update \ && apt install -y --no-install-recommends ffmpeg \ && rm -rf /var/lib/apt/lists/* COPY --from=binaries /$TARGETPLATFORM / ENTRYPOINT [ "/mediamtx" ] ================================================ FILE: docker/ffmpeg.Dockerfile ================================================ ################################################################# FROM --platform=linux/amd64 scratch AS binaries ADD binaries/mediamtx_*_linux_amd64.tar.gz /linux/amd64 ADD binaries/mediamtx_*_linux_armv6.tar.gz /linux/arm/v6 ADD binaries/mediamtx_*_linux_armv7.tar.gz /linux/arm/v7 ADD binaries/mediamtx_*_linux_arm64.tar.gz /linux/arm64 ################################################################# FROM alpine:3.22 RUN apk add --no-cache ffmpeg ARG TARGETPLATFORM COPY --from=binaries /$TARGETPLATFORM / ENTRYPOINT [ "/mediamtx" ] ================================================ FILE: docker/rpi.Dockerfile ================================================ ################################################################# FROM --platform=linux/amd64 scratch AS binaries ADD binaries/mediamtx_*_linux_armv6.tar.gz /linux/arm/v6 ADD binaries/mediamtx_*_linux_armv7.tar.gz /linux/arm/v7 ADD binaries/mediamtx_*_linux_arm64.tar.gz /linux/arm64 ################################################################# FROM --platform=linux/arm/v7 debian:bullseye-slim AS base-arm-v7 # even though the base image is arm v7, # Raspbian libraries and compilers provide arm v6 compatibility. RUN apt update \ && apt install -y wget gpg \ && echo "deb http://archive.raspbian.org/raspbian bullseye main rpi firmware" > /etc/apt/sources.list \ && echo "deb http://archive.raspberrypi.org/debian bullseye main" > /etc/apt/sources.list.d/raspi.list \ && wget -O- https://archive.raspbian.org/raspbian.public.key | gpg --dearmor -o /etc/apt/trusted.gpg.d/raspbian.gpg \ && wget -O- https://archive.raspberrypi.org/debian/raspberrypi.gpg.key | gpg --dearmor -o /etc/apt/trusted.gpg.d/raspberrypi.gpg \ && rm -rf /var/lib/apt/lists/* RUN apt update && apt install --reinstall -y \ libc6 \ libc-bin \ libstdc++6 \ && rm -rf /var/lib/apt/lists/* ################################################################# FROM --platform=linux/arm64 debian:bullseye-slim AS base-arm64 RUN apt update \ && apt install -y wget gpg \ && echo "deb http://archive.raspberrypi.org/debian bullseye main" > /etc/apt/sources.list.d/raspi.list \ && wget -O- https://archive.raspberrypi.org/debian/raspberrypi.gpg.key | gpg --dearmor -o /etc/apt/trusted.gpg.d/raspberrypi.gpg \ && rm -rf /var/lib/apt/lists/* ################################################################# FROM --platform=linux/amd64 scratch AS base COPY --from=base-arm-v7 / /linux/arm/v6 COPY --from=base-arm-v7 / /linux/arm/v7 COPY --from=base-arm64 / /linux/arm64 ################################################################# FROM scratch ARG TARGETPLATFORM COPY --from=base /$TARGETPLATFORM / COPY --from=binaries /$TARGETPLATFORM / ENTRYPOINT [ "/mediamtx" ] ================================================ FILE: docker/standard.Dockerfile ================================================ ################################################################# FROM --platform=linux/amd64 scratch AS binaries ADD binaries/mediamtx_*_linux_amd64.tar.gz /linux/amd64 ADD binaries/mediamtx_*_linux_armv6.tar.gz /linux/arm/v6 ADD binaries/mediamtx_*_linux_armv7.tar.gz /linux/arm/v7 ADD binaries/mediamtx_*_linux_arm64.tar.gz /linux/arm64 ################################################################# FROM scratch ARG TARGETPLATFORM COPY --from=binaries /$TARGETPLATFORM / ENTRYPOINT [ "/mediamtx" ] ================================================ FILE: docs/1-kickoff/1-introduction.md ================================================ # Introduction Welcome to the MediaMTX documentation! _MediaMTX_ is a ready-to-use and zero-dependency live media server and media proxy. It has been conceived as a "media router" that routes media streams from one end to the other, with a focus on efficiency and portability. Main features: - [Publish](../2-publish/01-overview.md) live streams to the server with SRT, WebRTC, RTSP, RTMP, HLS, MPEG-TS, RTP, using FFmpeg, GStreamer, OBS Studio, Python , Golang, Unity, Web browsers, Raspberry Pi Cameras and more. - [Read](../3-read/01-overview.md) live streams from the server with SRT, WebRTC, RTSP, RTMP, HLS, using FFmpeg, GStreamer, VLC, OBS Studio, Python , Golang, Unity, Web browsers and more. - Streams are automatically converted from a protocol to another - Serve several streams at once in separate paths - Reload the configuration without disconnecting existing clients (hot reloading) - [Serve always-available streams](../4-other/05-always-available.md) even when the publisher is offline - [Record](../4-other/06-record.md) streams to disk in fMP4 or MPEG-TS format - [Playback](../4-other/07-playback.md) recorded streams - [Authenticate](../4-other/03-authentication.md) users with internal, HTTP or JWT authentication - [Forward](../4-other/08-forward.md) streams to other servers - [Proxy](../4-other/09-proxy.md) requests to other servers - [Control](../4-other/18-control-api.md) the server through the Control API - [Extract metrics](../4-other/19-metrics.md) from the server in a Prometheus-compatible format - [Monitor performance](../4-other/20-performance.md) to investigate CPU and RAM consumption - [Run hooks](../4-other/17-hooks.md) (external commands) when clients connect, disconnect, read or publish streams - Compatible with Linux, Windows and macOS, does not require any dependency or interpreter, it's a single executable Use the menu to navigate through the documentation. ================================================ FILE: docs/1-kickoff/2-install.md ================================================ # Install There are several installation methods available: - [Standalone binary](#standalone-binary): use this if you are running Windows, macOS or you just want to try out _MediaMTX_. - [Docker image](#docker-image): use this if you want to run _MediaMTX_ in an isolated and deterministic way. This is recommended for production environments. - [Arch Linux package](#arch-linux-package): use this if you are running Arch Linux. - [FreeBSD package](#freebsd-package): use this if you are running FreeBSD. - [OpenWrt binary](#openwrt-binary): use this if you are running OpenWrt. ## Standalone binary 1. Visit the [Releases page](https://github.com/bluenviron/mediamtx/releases) on GitHub, download and extract a standalone binary that corresponds to your operating system and architecture (example: `mediamtx_{version_tag}_linux_amd64.tar.gz`). 2. Start the server by double clicking on `mediamtx` (`mediamtx.exe` on Windows) or writing in the terminal: ```sh ./mediamtx ``` ## Docker image Download and launch the `bluenviron/mediamtx:1` image with the following environment variables and ports: ```sh docker run --rm -it \ -e MTX_RTSPTRANSPORTS=tcp \ -e MTX_WEBRTCADDITIONALHOSTS=192.168.x.x \ -p 8554:8554 \ -p 1935:1935 \ -p 8888:8888 \ -p 8889:8889 \ -p 8890:8890/udp \ -p 8189:8189/udp \ bluenviron/mediamtx:1 ``` Fill the `MTX_WEBRTCADDITIONALHOSTS` environment variable with the IP that will be used to connect to the server. The `MTX_RTSPTRANSPORTS=tcp` environment variable is meant to disable the UDP transport protocol of the RTSP server (which requires the real IP address and port of incoming UDP packets, that are sometimes replaced by the Docker network stack). If you want to use it, you need to bypass the Docker network stack through the `--network=host` flag (which is not compatible with Windows, macOS and Kubernetes): ```sh docker run --rm -it --network=host bluenviron/mediamtx:1 ``` There are four image variants: | name | FFmpeg included | RPI Camera support | | -------------------------------- | ------------------ | ------------------ | | bluenviron/mediamtx:1 | :x: | :x: | | bluenviron/mediamtx:1-ffmpeg | :heavy_check_mark: | :x: | | bluenviron/mediamtx:1-rpi | :x: | :heavy_check_mark: | | bluenviron/mediamtx:1-ffmpeg-rpi | :heavy_check_mark: | :heavy_check_mark: | The `1` tag corresponds to the latest `1.x.x` release, that should guarantee backward compatibility when upgrading. It is also possible to bind the image to a specific release, by using the release name as tag (`bluenviron/mediamtx:{docker_version_tag}`). The base image does not contain any utility, in order to minimize size and frequency of updates. If you need additional software (like curl, wget, GStreamer), you can build a custom image by creating a file named `Dockerfile` with this content: ```Dockerfile FROM bluenviron/mediamtx:1 AS mediamtx FROM ubuntu:24.04 COPY --from=mediamtx /mediamtx / COPY --from=mediamtx /mediamtx.yml / # add anything you need. RUN apt update && apt install -y \ gstreamer1.0-tools ENTRYPOINT [ "/mediamtx" ] ``` And then build it: ```sh docker build . -t my-mediamtx ``` In particular, the custom image is using the official _MediaMTX_ image as a base stage, and then adds a Linux-based operating system on top of it. Since _MediaMTX_ binaries are not tied to a specific Linux distribution or version, you can use anything you like. ## Arch Linux package If you are running the Arch Linux distribution, launch: ```sh git clone https://aur.archlinux.org/mediamtx.git cd mediamtx makepkg -si ``` ## FreeBSD package Available via ports tree or using packages (2025Q2 and later) as listed below: ```sh cd /usr/ports/multimedia/mediamtx && make install clean pkg install mediamtx ``` ## OpenWrt binary If the architecture of the OpenWrt device is amd64, armv6, armv7 or arm64, use the [standalone binary method](#standalone-binary) and download a Linux binary that corresponds to your architecture. Otherwise, [compile the server from source](../6-misc/1-compile.md). ================================================ FILE: docs/1-kickoff/3-upgrade.md ================================================ # Upgrade If you have an existing _MediaMTX_ installation, you can upgrade it to the latest version. The procedure depends on how _MediaMTX_ was installed. ## Standalone binary The standalone binary comes with an upgrade utility that can be launched with: ```sh ./mediamtx --upgrade ``` This will replace the _MediaMTX_ executable with its latest version. Privileges to write to the executable location are required. ## Docker image Stop and remove the container: ```sh docker stop id-of-mediamtx-container docker rm id-of-mediamtx-container ``` Remove the _MediaMTX_ image from cache: ```sh docker rm bluenviron/mediamtx:1 ``` Then recreate the container as described in [Install](2-install.md#docker-image). ## Arch Linux package Repeat the installation procedure. ## FreeBSD package Repeat the installation procedure. ## OpenWrt binary If the architecture of the OpenWrt device is amd64, armv6, armv7 or arm64, you can use the standalone binary method. Otherwise, recompile the server from source. ================================================ FILE: docs/1-kickoff/4-basic-usage.md ================================================ # Basic usage 1. [Publish](../2-publish/01-overview.md) a stream. For instance, you can publish a stream from a MP4 file with _FFmpeg_: ```sh ffmpeg -re -stream_loop -1 -i file.mp4 -c copy \ -f rtsp rtsp://localhost:8554/mystream ``` or _GStreamer_: ```sh gst-launch-1.0 rtspclientsink name=s location=rtsp://localhost:8554/mystream filesrc location=file.mp4 \ ! qtdemux name=d d.video_0 ! queue ! s.sink_0 d.audio_0 ! queue ! s.sink_1 ``` 2. [Read](../3-read/01-overview.md) the stream. For instance, you can read the stream with _VLC_: ```sh vlc --network-caching=50 rtsp://localhost:8554/mystream ``` or _GStreamer_: ```sh gst-play-1.0 rtsp://localhost:8554/mystream ``` or _FFmpeg_: ```sh ffmpeg -i rtsp://localhost:8554/mystream -c copy output.mp4 ``` ================================================ FILE: docs/1-kickoff/index.md ================================================ # Kickoff ================================================ FILE: docs/2-publish/01-overview.md ================================================ # Publish a stream Live streams can be published to the server with the following protocols and codecs: | protocol | variants | codecs | | ---------------------------------------------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [SRT clients](02-srt-clients.md) | | **Video**: H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video
**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3
**Other**: KLV | | [SRT cameras and servers](03-srt-cameras-and-servers.md) | | **Video**: H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video
**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3
**Other**: KLV | | [WebRTC clients](04-webrtc-clients.md) | WHIP | **Video**: AV1, VP9, VP8, H265, H264
**Audio**: Opus, G722, G711 (PCMA, PCMU) | | [WebRTC servers](05-webrtc-servers.md) | WHEP | **Video**: AV1, VP9, VP8, H265, H264
**Audio**: Opus, G722, G711 (PCMA, PCMU) | | [RTSP clients](06-rtsp-clients.md) | UDP, TCP, RTSPS | **Video**: AV1, VP9, VP8, H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video, MJPEG
**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G726, G722, G711 (PCMA, PCMU), LPCM
**Other**: KLV, MPEG-TS, any RTP-compatible codec | | [RTSP cameras and servers](07-rtsp-cameras-and-servers.md) | UDP, UDP-Multicast, TCP, RTSPS | **Video**: AV1, VP9, VP8, H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video, MJPEG
**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G726, G722, G711 (PCMA, PCMU), LPCM
**Other**: KLV, MPEG-TS, any RTP-compatible codec | | [RTMP clients](08-rtmp-clients.md) | RTMP, RTMPS, Enhanced RTMP | **Video**: AV1, VP9, H265, H264
**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G711 (PCMA, PCMU), LPCM | | [RTMP cameras and servers](09-rtmp-cameras-and-servers.md) | RTMP, RTMPS, Enhanced RTMP | **Video**: AV1, VP9, H265, H264
**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G711 (PCMA, PCMU), LPCM | | [HLS cameras and servers](10-hls-cameras-and-servers.md) | Low-Latency HLS, MP4-based HLS, legacy HLS | **Video**: AV1, VP9, H265, H264
**Audio**: Opus, MPEG-4 Audio (AAC) | | [MPEG-TS](11-mpeg-ts.md) | MPEG-TS over UDP, MPEG-TS over Unix socket | **Video**: H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video
**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3
**Other**: KLV | | [RTP](12-rtp.md) | RTP over UDP | **Video**: AV1, VP9, VP8, H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video, M-JPEG
**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G726, G722, G711 (PCMA, PCMU), LPCM
**Other**: KLV, MPEG-TS, any RTP-compatible codec | We provide instructions for publishing with the following devices: - [Raspberry Pi Cameras](13-raspberry-pi-cameras.md) - [Generic webcams](14-generic-webcams.md) We provide instructions for publishing with the following software: - [FFmpeg](15-ffmpeg.md) - [GStreamer](16-gstreamer.md) - [OBS Studio](17-obs-studio.md) - [Python and OpenCV](18-python-opencv.md) - [Golang](19-golang.md) - [Unity](20-unity.md) - [Web browsers](21-web-browsers.md) ================================================ FILE: docs/2-publish/02-srt-clients.md ================================================ # SRT clients SRT is a protocol that allows to publish and read live data stream, providing encryption, integrity and a retransmission mechanism. It is usually used to transfer media streams encoded with MPEG-TS. In order to publish a stream to the server with the SRT protocol, use this URL: ``` srt://localhost:8890?streamid=publish:mystream&pkt_size=1316 ``` Replace `mystream` with any name you want. The resulting stream will be available on path `/mystream`. If you need to use the standard stream ID syntax instead of the custom one in use by this server, read [Standard stream ID syntax](../4-other/21-srt-specific-features.md#standard-stream-id-syntax). If you want to publish a stream by using a client in listening mode (i.e. with `mode=listener` appended to the URL), read the next section. Some clients that can publish with SRT are [FFmpeg](15-ffmpeg.md), [GStreamer](16-gstreamer.md), [OBS Studio](17-obs-studio.md). ================================================ FILE: docs/2-publish/03-srt-cameras-and-servers.md ================================================ # SRT cameras and servers In order to ingest a SRT stream from a remote server, camera or client in listening mode (i.e. with `mode=listener` appended to the URL), add the corresponding URL into the `source` parameter of a path: ```yml paths: proxied: # url of the source stream, in the format srt://host:port?streamid=streamid&other_parameters source: srt://original-url ``` ================================================ FILE: docs/2-publish/04-webrtc-clients.md ================================================ # WebRTC clients WebRTC is an API that makes use of a set of protocols and methods to connect two clients together and allow them to exchange live media or data streams. You can publish a stream with WebRTC and a web browser by visiting: ``` http://localhost:8889/mystream/publish ``` The resulting stream will be available on path `/mystream`. WHIP is a WebRTC extension that allows to publish streams by using a URL, without passing through a web page. This allows to use WebRTC as a general purpose streaming protocol. If you are using a software that supports WHIP (for instance, latest versions of OBS Studio), you can publish a stream to the server by using this URL: ``` http://localhost:8889/mystream/whip ``` Be aware that not all browsers can read any codec, check [Supported browsers](../4-other/22-webrtc-specific-features.md#supported-browsers). Depending on the network it might be difficult to establish a connection between server and clients, read [Solving WebRTC connectivity issues](../4-other/22-webrtc-specific-features.md#solving-webrtc-connectivity-issues). Some clients that can publish with WebRTC and WHIP are [FFmpeg](15-ffmpeg.md), [GStreamer](16-gstreamer.md), [OBS Studio](17-obs-studio.md), [Unity](20-unity.md) and [Web browsers](21-web-browsers.md). ================================================ FILE: docs/2-publish/05-webrtc-servers.md ================================================ # WebRTC servers In order to ingest a WebRTC stream from a remote server, add the corresponding WHEP URL into the `source` parameter of a path: ```yml paths: proxied: # url of the source stream, in the format whep://host:port/path (HTTP) or wheps:// (HTTPS) source: wheps://host:port/path ``` If the remote server is a _MediaMTX_ instance, remember to add a `/whep` suffix after the stream name, since in _MediaMTX_ [it's part of the WHEP URL](../3-read/03-webrtc.md): ```yml paths: proxied: source: whep://host:port/mystream/whep ``` ================================================ FILE: docs/2-publish/06-rtsp-clients.md ================================================ # RTSP clients RTSP is a protocol that allows to publish and read streams. It supports several underlying transport protocols and encryption. In order to publish a stream to the server with the RTSP protocol, use this URL: ``` rtsp://localhost:8554/mystream ``` The resulting stream will be available on path `/mystream`. Some clients that can publish with RTSP are [FFmpeg](15-ffmpeg.md), [GStreamer](16-gstreamer.md), [OBS Studio](17-obs-studio.md), [Python and OpenCV](18-python-opencv.md). Advanced RTSP features and settings are described in [RTSP-specific features](../4-other/23-rtsp-specific-features.md). ## MPEG-TS inside RTSP Some RTSP clients encode tracks with MPEG-TS before sending them to the server, causing the server to see a single "MPEG-TS" track, and preventing track conversion from a protocol to another. It's possible to automatically demux these MPEG-TS-encoded streams, by toggling `rtspDemuxMpegts`: ```yml pathDefaults: # Demux MPEG-TS over RTSP into elementary streams. # When enabled, RTSP publishers sending MP2T/90000 will be demultiplexed # and their elementary streams (H.264, H.265, AAC, etc.) exposed as native tracks. # This allows HLS, WebRTC, and other outputs to work transparently with MPEG-TS sources. rtspDemuxMpegts: true ``` ================================================ FILE: docs/2-publish/07-rtsp-cameras-and-servers.md ================================================ # RTSP cameras and servers Most IP cameras expose their video stream by using a RTSP server that is embedded into the camera itself. In particular, cameras that are compliant with ONVIF profile S or T meet this requirement. You can use _MediaMTX_ to connect to one or several existing RTSP servers and read their media streams: ```yml paths: proxied: # url of the source stream, in the format rtsp://user:pass@host:port/path source: rtsp://original-url ``` The resulting stream will be available on path `/proxied`. It is possible to tune the connection by using some additional parameters: ```yml paths: proxied: # url of the source stream, in the format rtsp://user:pass@host:port/path source: rtsp://original-url # Transport protocol used to pull the stream. available values are "automatic", "udp", "multicast", "tcp". rtspTransport: automatic # Support sources that don't provide server ports or use random server ports. This is a security issue # and must be used only when interacting with sources that require it. rtspAnyPort: no # Range header to send to the source, in order to start streaming from the specified offset. # available values: # * clock: Absolute time # * npt: Normal Play Time # * smpte: SMPTE timestamps relative to the start of the recording rtspRangeType: # Available values: # * clock: UTC ISO 8601 combined date and time string, e.g. 20230812T120000Z # * npt: duration such as "300ms", "1.5m" or "2h45m", valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" # * smpte: duration such as "300ms", "1.5m" or "2h45m", valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" rtspRangeStart: # Size of the UDP buffer of the RTSP client. # This can be increased to mitigate packet losses. # It defaults to the default value of the operating system. rtspUDPReadBufferSize: 0 # Range of ports used as source port in outgoing UDP packets. rtspUDPSourcePortRange: [10000, 65535] ``` All available parameters are listed in the [configuration file](../5-references/1-configuration-file.md). Advanced RTSP features and settings are described in [RTSP-specific features](../4-other/23-rtsp-specific-features.md). ================================================ FILE: docs/2-publish/08-rtmp-clients.md ================================================ # RTMP clients RTMP is a protocol that allows to read and publish streams. It supports encryption, read [RTMP-specific features](../4-other/24-rtmp-specific-features.md). Streams can be published to the server by using the URL: ``` rtmp://localhost/mystream ``` The resulting stream will be available on path `/mystream`. Some clients that can publish with RTMP are [FFmpeg](15-ffmpeg.md), [GStreamer](16-gstreamer.md), [OBS Studio](17-obs-studio.md). ================================================ FILE: docs/2-publish/09-rtmp-cameras-and-servers.md ================================================ # RTMP cameras and servers You can use _MediaMTX_ to connect to one or several existing RTMP servers and read their media streams: ```yml paths: proxied: # url of the source stream, in the format rtmp://user:pass@host:port/path source: rtmp://original-url ``` The resulting stream will be available on path `/proxied`. ================================================ FILE: docs/2-publish/10-hls-cameras-and-servers.md ================================================ # HLS cameras and servers HLS is a streaming protocol that works by splitting streams into segments, and by serving these segments and a playlist with the HTTP protocol. You can use _MediaMTX_ to connect to one or several existing HLS servers and read their media streams: ```yml paths: proxied: # url of the playlist of the stream, in the format http://user:pass@host:port/path source: http://original-url/stream/index.m3u8 ``` The resulting stream will be available on path `/proxied`. ================================================ FILE: docs/2-publish/11-mpeg-ts.md ================================================ # MPEG-TS The server supports ingesting MPEG-TS streams, shipped in two different ways (UDP packets or Unix sockets). In order to read a UDP MPEG-TS stream, edit `mediamtx.yml` and replace everything inside section `paths` with the following content: ```yml paths: mypath: source: udp+mpegts://238.0.0.1:1234 ``` Where `238.0.0.1` is the IP for listening packets, in this case a multicast IP. If the listening IP is a multicast IP, _MediaMTX_ will listen for incoming packets on the default multicast interface, picked by the operating system. It is possible to specify the interface manually by using the `interface` parameter: ```yml paths: mypath: source: udp+mpegts://238.0.0.1:1234?interface=eth0 ``` It is possible to restrict who can send packets by using the `source` parameter: ```yml paths: mypath: source: udp+mpegts://0.0.0.0:1234?source=192.168.3.5 ``` Some clients that can publish with UDP and MPEG-TS are [FFmpeg](15-ffmpeg.md) and [GStreamer](16-gstreamer.md). Unix sockets are more efficient than UDP packets and can be used as transport by specifying the `unix+mpegts` scheme: ```yml paths: mypath: source: unix+mpegts:///tmp/socket.sock ``` ================================================ FILE: docs/2-publish/12-rtp.md ================================================ # RTP The server supports ingesting RTP streams, transmitted with UDP packets. In order to read a UDP RTP stream, edit `mediamtx.yml` and replace everything inside section `paths` with the following content: ```yml paths: mypath: source: udp+rtp://238.0.0.1:1234 rtpSDP: | v=0 o=- 123456789 123456789 IN IP4 192.168.1.100 s=H264 Video Stream c=IN IP4 192.168.1.100 t=0 0 m=video 5004 RTP/AVP 96 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42e01e;packetization-mode=1;sprop-parameter-sets=Z0LAHtkDxWhAAAADAEAAAAwDxYuS,aMuMsg== ``` `rtpSDP` must contain a valid SDP, that is a description of the RTP session. Some clients that can publish with UDP and MPEG-TS are [FFmpeg](15-ffmpeg.md) and [GStreamer](16-gstreamer.md). ================================================ FILE: docs/2-publish/13-raspberry-pi-cameras.md ================================================ # Raspberry Pi Cameras _MediaMTX_ natively supports most Raspberry Pi Camera models, enabling high-quality and low-latency video streaming from the camera to any user, for any purpose. There are some additional requirements: 1. The server must run on a Raspberry Pi, with one of the following operating systems: - Raspberry Pi OS Trixie - Raspberry Pi OS Bookworm - Raspberry Pi OS Bullseye Both 32-bit and 64-bit architectures are supported. 2. If you are using Raspberry Pi OS Bullseye, make sure that the legacy camera stack is disabled. Type `sudo raspi-config`, then go to `Interfacing options`, `enable/disable legacy camera support`, choose `no`. Reboot the system. The setup procedure depends on whether you want to run the server outside or inside Docker: - If you want to run the standard (non-Dockerized) version of the server: 1. Download the server executable. If you're using 64-bit version of the operative system, make sure to pick the `arm64` variant. 2. Edit `mediamtx.yml` and replace everything inside section `paths` with the following content: ```yml paths: cam: source: rpiCamera ``` The resulting stream will be available on path `/cam`. - If you want to run the server inside Docker, you need to use the `1-rpi` image and launch the container with some additional flags: ```sh docker run --rm -it \ --network=host \ --privileged \ --tmpfs /dev/shm:exec \ -v /run/udev:/run/udev:ro \ -e MTX_PATHS_CAM_SOURCE=rpiCamera \ bluenviron/mediamtx:1-rpi ``` The Raspberry Pi Camera can be controlled through a wide range of parameters, that are listed in the [configuration file](../5-references/1-configuration-file.md). Be aware that cameras that require a custom `libcamera` (like some ArduCam products) are not compatible with precompiled binaries and Docker images of _MediaMTX_, since these come with a bundled `libcamera`. If you want to use a custom one, you need to [compile from source](../6-misc/1-compile.md#custom-libcamera). ## Adding audio In order to add audio from a USB microphone, install GStreamer and alsa-utils: ```sh sudo apt install -y gstreamer1.0-tools gstreamer1.0-rtsp gstreamer1.0-alsa alsa-utils ``` list available audio cards with: ```sh arecord -L ``` Sample output: ``` surround51:CARD=ICH5,DEV=0 Intel ICH5, Intel ICH5 5.1 Surround output to Front, Center, Rear and Subwoofer speakers default:CARD=U0x46d0x809 USB Device 0x46d:0x809, USB Audio Default Audio Device ``` Find the audio card of the microphone and take note of its name, for instance `default:CARD=U0x46d0x809`. Then create a new path that takes the video stream from the camera and audio from the microphone: ```yml paths: cam: source: rpiCamera cam_with_audio: runOnInit: > gst-launch-1.0 rtspclientsink name=s location=rtsp://localhost:$RTSP_PORT/cam_with_audio rtspsrc location=rtsp://127.0.0.1:$RTSP_PORT/cam latency=0 ! rtph264depay ! s. alsasrc device=default:CARD=U0x46d0x809 ! opusenc bitrate=16000 ! s. runOnInitRestart: yes ``` The resulting stream will be available on path `/cam_with_audio`. ## Secondary stream It is possible to enable a secondary stream from the same camera, with a different resolution, FPS and codec. Configuration is the same of a primary stream, with `rpiCameraSecondary` set to `true` and parameters adjusted accordingly: ```yml paths: # primary stream rpi: source: rpiCamera # Width of frames. rpiCameraWidth: 1920 # Height of frames. rpiCameraHeight: 1080 # FPS. rpiCameraFPS: 30 # secondary stream secondary: source: rpiCamera # This is a secondary stream. rpiCameraSecondary: true # Width of frames. rpiCameraWidth: 640 # Height of frames. rpiCameraHeight: 480 # FPS. rpiCameraFPS: 10 # Codec. in case of secondary streams, it defaults to M-JPEG. rpiCameraCodec: auto # JPEG quality. rpiCameraMJPEGQuality: 60 ``` The secondary stream will be available on path `/secondary`. ================================================ FILE: docs/2-publish/14-generic-webcams.md ================================================ # Generic webcams If the operating system is Linux, edit `mediamtx.yml` and replace everything inside section `paths` with the following content: ```yml paths: cam: runOnInit: ffmpeg -f v4l2 -i /dev/video0 -c:v libx264 -pix_fmt yuv420p -preset ultrafast -b:v 600k -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH runOnInitRestart: yes ``` If the operating system is Windows: ```yml paths: cam: runOnInit: ffmpeg -f dshow -i video="USB2.0 HD UVC WebCam" -c:v libx264 -pix_fmt yuv420p -preset ultrafast -b:v 600k -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH runOnInitRestart: yes ``` Where `USB2.0 HD UVC WebCam` is the name of a webcam, that can be obtained with: ```sh ffmpeg -list_devices true -f dshow -i dummy ``` The resulting stream will be available on path `/cam`. ================================================ FILE: docs/2-publish/15-ffmpeg.md ================================================ # FFmpeg FFmpeg can publish a stream to the server in several ways. The recommended one consists in publishing with RTSP. ## FFmpeg and RTSP ```sh ffmpeg -re -stream_loop -1 -i file.mp4 -c copy -f rtsp rtsp://localhost:8554/mystream ``` The resulting stream will be available on path `/mystream`. ## FFmpeg and RTMP ```sh ffmpeg -re -stream_loop -1 -i file.mp4 -c copy -f flv rtmp://localhost:1935/mystream ``` ## FFmpeg and MPEG-TS over UDP In _MediaMTX_ configuration, add a path with `source: udp+mpegts://238.0.0.1:1234`. Then: ```sh ffmpeg -re -stream_loop -1 -i file.mp4 -c copy -f mpegts 'udp://238.0.0.1:1234?pkt_size=1316' ``` ## FFmpeg and MPEG-TS over Unix socket In _MediaMTX_ configuration, add a path with `source: unix+mpegts:///tmp/socket.sock`. Then: ```sh ffmpeg -re -f lavfi -i testsrc=size=1280x720:rate=30 \ -c:v libx264 -pix_fmt yuv420p -preset ultrafast -b:v 600k \ -f mpegts unix:/tmp/socket.sock ``` ## FFmpeg and RTP over UDP In _MediaMTX_ configuration, add a path with `source: udp+rtp://238.0.0.1:1234` and a valid `rtpSDP` (read [RTP](12-rtp.md)). Then: ```sh ffmpeg -re -f lavfi -i testsrc=size=1280x720:rate=30 \ -c:v libx264 -pix_fmt yuv420p -preset ultrafast -b:v 600k \ -f rtp udp://238.0.0.1:1234?pkt_size=1316 ``` ## FFmpeg and SRT ```sh ffmpeg -re -stream_loop -1 -i file.mp4 -c copy -f mpegts 'srt://localhost:8890?streamid=publish:stream&pkt_size=1316' ``` ## FFmpeg and WebRTC ```sh ffmpeg -re -f lavfi -i testsrc=size=1280x720:rate=30 \ -f lavfi -i "sine=frequency=1000:sample_rate=48000" \ -c:v libx264 -pix_fmt yuv420p -preset ultrafast -b:v 600k \ -c:a libopus -ar 48000 -ac 2 -b:a 128k \ -f whip http://localhost:8889/stream/whip ``` WARNING: in case of FFmpeg 8.0, a video track and an audio track must both be present. ================================================ FILE: docs/2-publish/16-gstreamer.md ================================================ # GStreamer GStreamer can publish a stream to the server in several ways. The recommended one consists in publishing with RTSP. ## GStreamer and RTSP ```sh gst-launch-1.0 rtspclientsink name=s location=rtsp://localhost:8554/mystream \ filesrc location=file.mp4 ! qtdemux name=d \ d.video_0 ! queue ! s.sink_0 \ d.audio_0 ! queue ! s.sink_1 ``` If the stream is video only: ```sh gst-launch-1.0 filesrc location=file.mp4 ! qtdemux name=d \ d.video_0 ! rtspclientsink location=rtsp://localhost:8554/mystream ``` The resulting stream will be available on path `/mystream`. For advanced options, read [RTSP-specific features](../4-other/23-rtsp-specific-features.md). ## GStreamer and RTMP ```sh gst-launch-1.0 -v flvmux name=mux ! rtmpsink location=rtmp://localhost/stream \ videotestsrc ! video/x-raw,width=1280,height=720,format=I420 ! x264enc speed-preset=ultrafast bitrate=3000 key-int-max=60 ! video/x-h264,profile=high ! mux. \ audiotestsrc ! audioconvert ! avenc_aac ! mux. ``` ## GStreamer and MPEG-TS over UDP ```sh gst-launch-1.0 -v mpegtsmux name=mux alignment=1 ! udpsink host=238.0.0.1 port=1234 \ videotestsrc ! video/x-raw,width=1280,height=720,format=I420 ! x264enc speed-preset=ultrafast bitrate=3000 key-int-max=60 ! video/x-h264,profile=high ! mux. \ audiotestsrc ! audioconvert ! avenc_aac ! mux. ``` For advanced options, read [RTSP-specific features](../4-other/23-rtsp-specific-features.md). ## GStreamer and WebRTC Make sure that GStreamer version is at least 1.22, and that if the codec is H264, the profile is baseline. Use the `whipclientsink` element: ```sh gst-launch-1.0 videotestsrc \ ! video/x-raw,width=1920,height=1080,format=I420 \ ! x264enc speed-preset=ultrafast bitrate=2000 \ ! video/x-h264,profile=baseline \ ! whipclientsink signaller::whip-endpoint=http://localhost:8889/mystream/whip ``` ================================================ FILE: docs/2-publish/17-obs-studio.md ================================================ # OBS Studio OBS Studio can publish streams to the server in several ways. The recommended one consists in publishing with RTMP. ## OBS Studio and RTMP In `Settings -> Stream` (or in the Auto-configuration Wizard), use the following parameters: - Service: `Custom...` - Server: `rtmp://localhost/mystream` - Stream key: (empty) Save the configuration and click `Start streaming`. The resulting stream will be available on path `/mystream`. If you want to generate a stream that can be read with WebRTC, open `Settings -> Output -> Recording` and use the following parameters: - FFmpeg output type: `Output to URL` - File path or URL: `rtsp://localhost:8554/mystream` - Container format: `rtsp` - Check `show all codecs (even if potentially incompatible)` - Video encoder: `h264_nvenc (libx264)` - Video encoder settings (if any): `bf=0` - Audio track: `1` - Audio encoder: `libopus` Then use the button `Start Recording` (instead of `Start Streaming`) to start streaming. ## OBS Studio and RTMP, multitrack video OBS Studio can publish multiple video tracks or renditions at once (simulcast). Make sure that the OBS Studio version is ≥ 31.0.0. Open `Settings -> Stream` and use the following parameters: - Service: `Custom...` - Server: `rtmp://localhost/mystream` - Stream key: (empty) - Turn on `Enable Multitrack Video` - Leave `Maximum Streaming Bandwidth` and `Maximum Video Tracks` to `Auto` - Turn on `Enable Config Override` - Fill `Config Override (JSON)` with the following text: ```json { "encoder_configurations": [ { "type": "obs_x264", "width": 1920, "height": 1080, "framerate": { "numerator": 30, "denominator": 1 }, "settings": { "rate_control": "CBR", "bitrate": 6000, "keyint_sec": 2, "preset": "veryfast", "profile": "high", "tune": "zerolatency" }, "canvas_index": 0 }, { "type": "obs_x264", "width": 640, "height": 480, "framerate": { "numerator": 30, "denominator": 1 }, "settings": { "rate_control": "CBR", "bitrate": 3000, "keyint_sec": 2, "preset": "veryfast", "profile": "main", "tune": "zerolatency" }, "canvas_index": 0 } ], "audio_configurations": { "live": [ { "codec": "ffmpeg_aac", "track_id": 1, "channels": 2, "settings": { "bitrate": 160 } } ] } } ``` This can be adjusted according to specific needs. In particular, the `type` field is used to set the video encoder, and these are the available parameters: - `obs_nvenc_av1_tex`: NVIDIA NVENC AV1 - `obs_nvenc_hevc_tex`: NVIDIA NVENC H265 - `obs_nvenc_h264_tex`: NVIDIA NVENC H264 - `av1_texture_amf`: AMD AV1 - `h265_texture_amf`: AMD H265 - `h264_texture_amf`: AMD H264 - `obs_qsv11_av1`: QuickSync AV1 - `obs_qsv11_v2`: QuickSync H264 - `obs_x264`: software H264 Save the configuration and click `Start streaming`. The resulting stream will be available on path `/mystream`. ## OBS Studio and WebRTC Recent versions of OBS Studio can also publish streams to the server with the [WebRTC / WHIP protocol](04-webrtc-clients.md) Use the following parameters: - Service: `WHIP` - Server: `http://localhost:8889/mystream/whip` Save the configuration and click `Start streaming`. The resulting stream will be available on path `/mystream`. ## OBS Studio and WebRTC, multitrack video OBS Studio can publish multiple video tracks or renditions at once (simulcast) with WebRTC / WHIP too. Make sure that the OBS Studio version is ≥ 32.1.0. Open `Settings -> Stream` and use the following parameters: - Service: `WHIP` - Server: `http://localhost:8889/mystream/whip` - Simulcast, Total Layers: `2` (or greater) Currently it's not possible to change resolution or bitrate (or canvas) of renditions, since quality of secondary renditions is hardcoded as a percentage of the main one. You can find details on the [OBS documentation](https://obsproject.com/kb/whip-streaming-guide). Save the configuration and click `Start streaming`. The resulting stream will be available on path `/mystream`. ================================================ FILE: docs/2-publish/18-python-opencv.md ================================================ # Python and OpenCV Python-based software can publish streams to the server with the OpenCV library and its GStreamer plugin, acting as a [RTSP client](06-rtsp-clients.md). OpenCV must be compiled with support for GStreamer, by following this procedure: ```sh sudo apt install -y libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-plugins-ugly gstreamer1.0-rtsp python3-dev python3-numpy git clone --depth=1 -b 4.5.4 https://github.com/opencv/opencv cd opencv mkdir build && cd build cmake -D CMAKE_INSTALL_PREFIX=/usr -D WITH_GSTREAMER=ON .. make -j$(nproc) sudo make install ``` You can check that OpenCV has been installed correctly by running: ```sh python3 -c 'import cv2; print(cv2.getBuildInformation())' ``` Check that the output contains `GStreamer: YES`. Videos can then be published with `cv2.VideoWriter`: ```python from datetime import datetime from time import sleep, time import cv2 import numpy as np fps = 15 width = 800 height = 600 colors = [ (0, 0, 255), (255, 0, 0), (0, 255, 0), ] out = cv2.VideoWriter('appsrc ! videoconvert' + \ ' ! video/x-raw,format=I420' + \ ' ! x264enc speed-preset=ultrafast bitrate=600 key-int-max=' + str(fps * 2) + \ ' ! video/x-h264,profile=baseline' + \ ' ! rtspclientsink location=rtsp://localhost:8554/mystream', cv2.CAP_GSTREAMER, 0, fps, (width, height), True) if not out.isOpened(): raise Exception("can't open video writer") curcolor = 0 start = time() while True: frame = np.zeros((height, width, 3), np.uint8) # create a rectangle color = colors[curcolor] curcolor += 1 curcolor %= len(colors) for y in range(0, int(frame.shape[0] / 2)): for x in range(0, int(frame.shape[1] / 2)): frame[y][x] = color out.write(frame) print("%s frame written to the server" % datetime.now()) now = time() diff = (1 / fps) - now - start if diff > 0: sleep(diff) start = now ``` The resulting stream will be available on path `/mystream`. ================================================ FILE: docs/2-publish/19-golang.md ================================================ # Golang You can publish a stream to the server by using the Go programming language and the following libraries: - [gortsplib](https://github.com/bluenviron/gortsplib) to publish with RTSP. - [gortmplib](https://github.com/bluenviron/gortmplib) to publish with RTMP. Both powers _MediaMTX_ itself. In the repositories of these projects there are several examples on how to connect to a server and push data. ================================================ FILE: docs/2-publish/20-unity.md ================================================ # Unity Software written with the Unity Engine can publish a stream to the server by using the [WebRTC protocol](04-webrtc-clients.md). Create a new Unity project or open an existing one. Open _Window -> Package Manager_, click on the plus sign, _Add Package by name..._ and insert `com.unity.webrtc`. Wait for the package to be installed. In the _Project_ window, under `Assets`, create a new C# Script called `WebRTCPublisher.cs` with this content: ```cs using System.Collections; using UnityEngine; using Unity.WebRTC; using UnityEngine.Networking; public class WebRTCPublisher : MonoBehaviour { public string url = "http://localhost:8889/unity/whip"; public int videoWidth = 1280; public int videoHeight = 720; private RTCPeerConnection pc; private MediaStream videoStream; void Start() { pc = new RTCPeerConnection(); Camera sourceCamera = gameObject.GetComponent(); videoStream = sourceCamera.CaptureStream(videoWidth, videoHeight); foreach (var track in videoStream.GetTracks()) { pc.AddTrack(track); } StartCoroutine(WebRTC.Update()); StartCoroutine(createOffer()); } private IEnumerator createOffer() { var op = pc.CreateOffer(); yield return op; if (op.IsError) { Debug.LogError("CreateOffer() failed"); yield break; } yield return setLocalDescription(op.Desc); } private IEnumerator setLocalDescription(RTCSessionDescription offer) { var op = pc.SetLocalDescription(ref offer); yield return op; if (op.IsError) { Debug.LogError("SetLocalDescription() failed"); yield break; } yield return postOffer(offer); } private IEnumerator postOffer(RTCSessionDescription offer) { var content = new System.Net.Http.StringContent(offer.sdp); content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/sdp"); var client = new System.Net.Http.HttpClient(); var task = System.Threading.Tasks.Task.Run(async () => { var res = await client.PostAsync(new System.UriBuilder(url).Uri, content); res.EnsureSuccessStatusCode(); return await res.Content.ReadAsStringAsync(); }); yield return new WaitUntil(() => task.IsCompleted); if (task.Exception != null) { Debug.LogError(task.Exception); yield break; } yield return setRemoteDescription(task.Result); } private IEnumerator setRemoteDescription(string answer) { RTCSessionDescription desc = new RTCSessionDescription(); desc.type = RTCSdpType.Answer; desc.sdp = answer; var op = pc.SetRemoteDescription(ref desc); yield return op; if (op.IsError) { Debug.LogError("SetRemoteDescription() failed"); yield break; } yield break; } void OnDestroy() { pc?.Close(); pc?.Dispose(); videoStream?.Dispose(); } } ``` In the _Hierarchy_ window, find or create a scene and a camera, then add the `WebRTCPublisher.cs` script as component of the camera, by dragging it inside the _Inspector_ window. then Press the _Play_ button at the top of the page. The resulting stream will be available on path `/unity`. ================================================ FILE: docs/2-publish/21-web-browsers.md ================================================ # Web browsers Web browsers can publish a stream to the server by using the [WebRTC protocol](04-webrtc-clients.md). Start the server and open the web page: ``` http://localhost:8889/mystream/publish ``` The resulting stream will be available on path `/mystream`. This web page can be embedded into another web page by using an iframe: ```html ``` For more advanced setups, you can create and serve a custom web page by starting from the [source code of the WebRTC publish page](https://github.com/bluenviron/mediamtx/blob/{version_tag}/internal/servers/webrtc/publish_index.html). In particular, there's a ready-to-use, standalone JavaScript class for publishing streams with WebRTC, available in [publisher.js](https://github.com/bluenviron/mediamtx/blob/{version_tag}/internal/servers/webrtc/publisher.js). ================================================ FILE: docs/2-publish/index.md ================================================ # Publish ================================================ FILE: docs/3-read/01-overview.md ================================================ # Read a stream Live streams can be read from the server with the following protocols and codecs: | protocol | variants | codecs | | ------------------------------ | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [SRT clients](02-srt.md) | | **Video**: H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video
**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3
**Other**: KLV | | [WebRTC clients](03-webrtc.md) | WHEP | **Video**: AV1, VP9, VP8, H265, H264
**Audio**: Opus, G722, G711 (PCMA, PCMU)
**Other**: KLV | | [RTSP clients](04-rtsp.md) | UDP, UDP-Multicast, TCP, RTSPS | **Video**: AV1, VP9, VP8, H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video, M-JPEG
**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G726, G722, G711 (PCMA, PCMU), LPCM
**Other**: KLV, MPEG-TS, any RTP-compatible codec | | [RTMP clients](05-rtmp.md) | RTMP, RTMPS, Enhanced RTMP | **Video**: AV1, VP9, H265, H264
**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G711 (PCMA, PCMU), LPCM | | [HLS](06-hls.md) | Low-Latency HLS, MP4-based HLS, legacy HLS | **Video**: AV1, VP9, H265, H264
**Audio**: Opus, MPEG-4 Audio (AAC) | We provide instructions for reading with the following software: - [FFmpeg](07-ffmpeg.md) - [GStreamer](08-gstreamer.md) - [VLC](09-vlc.md) - [OBS Studio](10-obs-studio.md) - [Python and OpenCV](11-python-opencv.md) - [Golang](12-golang.md) - [Unity](13-unity.md) - [Web browsers](14-web-browsers.md) ================================================ FILE: docs/3-read/02-srt.md ================================================ # SRT clients SRT is a protocol that allows to publish and read live data stream, providing encryption, integrity and a retransmission mechanism. It is usually used to transfer media streams encoded with MPEG-TS. In order to read a stream from the server with the SRT protocol, use this URL: ``` srt://localhost:8890?streamid=read:mystream ``` Replace `mystream` with the path name. If you need to use the standard stream ID syntax instead of the custom one in use by this server, read [Standard stream ID syntax](../4-other/21-srt-specific-features.md#standard-stream-id-syntax). Some clients that can read with SRT are [FFmpeg](07-ffmpeg.md), [GStreamer](08-gstreamer.md) and [VLC](09-vlc.md). ================================================ FILE: docs/3-read/03-webrtc.md ================================================ # WebRTC clients WebRTC is an API that makes use of a set of protocols and methods to connect two clients together and allow them to exchange live media or data streams. You can read a stream with WebRTC and a web browser by visiting: ``` http://localhost:8889/mystream ``` WHEP is a WebRTC extension that allows to read streams by using a URL, without passing through a web page. This allows to use WebRTC as a general purpose streaming protocol. If you are using a software that supports WHEP, you can read a stream from the server by using this URL: ``` http://localhost:8889/mystream/whep ``` Be aware that not all browsers can read any codec, check [Supported browsers](../4-other/22-webrtc-specific-features.md#supported-browsers). Depending on the network it may be difficult to establish a connection between server and clients, read [Solving WebRTC connectivity issues](../4-other/22-webrtc-specific-features.md#solving-webrtc-connectivity-issues). Some clients that can read with WebRTC and WHEP are [FFmpeg](07-ffmpeg.md), [GStreamer](08-gstreamer.md), [Unity](13-unity.md) and [web browsers](14-web-browsers.md). ================================================ FILE: docs/3-read/04-rtsp.md ================================================ # RTSP clients RTSP is a protocol that allows to publish and read streams. It supports several underlying transport protocols and encryption (read [RTSP-specific features](../4-other/23-rtsp-specific-features.md)). In order to read a stream with the RTSP protocol, use this URL: ``` rtsp://localhost:8554/mystream ``` Some clients that can read with RTSP are [FFmpeg](07-ffmpeg.md), [GStreamer](08-gstreamer.md) and [VLC](09-vlc.md). ================================================ FILE: docs/3-read/05-rtmp.md ================================================ # RTMP clients RTMP is a protocol that allows to read and publish streams. It supports encryption, read [RTMP-specific features](../4-other/24-rtmp-specific-features.md). Streams can be read from the server by using the URL: ``` rtmp://localhost/mystream ``` Some clients that can read with RTMP are [FFmpeg](07-ffmpeg.md), [GStreamer](08-gstreamer.md) and [VLC](09-vlc.md). ================================================ FILE: docs/3-read/06-hls.md ================================================ # HLS HLS is a protocol that works by splitting streams into segments, and by serving these segments and a playlist with the HTTP protocol. You can use _MediaMTX_ to generate an HLS stream, that is accessible through a web page: ``` http://localhost:8888/mystream ``` and can also be accessed without using the browsers, by software that supports the HLS protocol (for instance VLC or _MediaMTX_ itself) by using this URL: ``` http://localhost:8888/mystream/index.m3u8 ``` Some clients that can read with HLS are [FFmpeg](07-ffmpeg.md), [GStreamer](08-gstreamer.md), [VLC](09-vlc.md) and [web browsers](14-web-browsers.md). ================================================ FILE: docs/3-read/07-ffmpeg.md ================================================ # FFmpeg FFmpeg can read a stream from the server in several ways. The recommended one consists in reading with RTSP. ## FFmpeg and RTSP ```sh ffmpeg -i rtsp://localhost:8554/mystream -c copy output.mp4 ``` ## FFmpeg and RTMP ```sh ffmpeg -i rtmp://localhost/mystream -c copy output.mp4 ``` In order to read AV1, VP9, H265, Opus, AC3 tracks and in order to read multiple video or audio tracks, the `-rtmp_enhanced_codecs` flag must be present: ```sh ffmpeg -rtmp_enhanced_codecs ac-3,av01,avc1,ec-3,fLaC,hvc1,.mp3,mp4a,Opus,vp09 \ -i rtmp://localhost/mystream -c copy output.mp4 ``` ## FFmpeg and SRT ```sh ffmpeg -i 'srt://localhost:8890?streamid=read:test' -c copy output.mp4 ``` ================================================ FILE: docs/3-read/08-gstreamer.md ================================================ # GStreamer GStreamer can read a stream from the server in several ways. The recommended one consists in reading with RTSP. ## GStreamer and RTSP ```sh gst-launch-1.0 rtspsrc location=rtsp://127.0.0.1:8554/mystream latency=0 ! decodebin ! autovideosink ``` For advanced options, read [RTSP-specific features](../4-other/23-rtsp-specific-features.md). ## GStreamer and WebRTC GStreamer also supports reading streams with WebRTC/WHEP, although track codecs must be specified in advance through the `video-caps` and `audio-caps` parameters. Furthermore, if audio is not present, `audio-caps` must be set anyway and must point to a PCMU codec. For instance, the command for reading a video-only H264 stream is: ```sh gst-launch-1.0 whepsrc whep-endpoint=http://127.0.0.1:8889/stream/whep use-link-headers=true \ video-caps="application/x-rtp,media=video,encoding-name=H264,payload=127,clock-rate=90000" \ audio-caps="application/x-rtp,media=audio,encoding-name=PCMU,payload=0,clock-rate=8000" \ ! rtph264depay ! decodebin ! autovideosink ``` While the command for reading an audio-only Opus stream is: ```sh gst-launch-1.0 whepsrc whep-endpoint="http://127.0.0.1:8889/stream/whep" use-link-headers=true \ audio-caps="application/x-rtp,media=audio,encoding-name=OPUS,payload=111,clock-rate=48000,encoding-params=(string)2" \ ! rtpopusdepay ! decodebin ! autoaudiosink ``` While the command for reading a H264 and Opus stream is: ```sh gst-launch-1.0 whepsrc whep-endpoint=http://127.0.0.1:8889/stream/whep use-link-headers=true \ video-caps="application/x-rtp,media=video,encoding-name=H264,payload=127,clock-rate=90000" \ audio-caps="application/x-rtp,media=audio,encoding-name=OPUS,payload=111,clock-rate=48000,encoding-params=(string)2" \ ! decodebin ! autovideosink ``` ================================================ FILE: docs/3-read/09-vlc.md ================================================ # VLC VLC can read a stream from the server in several ways. The recommended one consists in reading with RTSP: ```sh vlc --network-caching=50 rtsp://localhost:8554/mystream ``` ## RTSP and Ubuntu compatibility The VLC shipped with Ubuntu 21.10 doesn't support playing RTSP due to a license issue (read [here](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=982299) and [here](https://stackoverflow.com/questions/69766748/cvlc-cannot-play-rtsp-omxplayer-instead-can)). To fix the issue, remove the default VLC instance and install the snap version: ```sh sudo apt purge -y vlc snap install vlc ``` ## Encrypted RTSP compatibility At the moment VLC doesn't support reading encrypted RTSP streams. However, you can use a proxy like [stunnel](https://www.stunnel.org) or [nginx](https://nginx.org/) or a local _MediaMTX_ instance to decrypt streams before reading them. ================================================ FILE: docs/3-read/10-obs-studio.md ================================================ # OBS Studio OBS Studio can read streams from the server by using the [RTSP protocol](04-rtsp.md). Open OBS, click on _Add Source_, _Media source_, _OK_, uncheck _Local file_, insert in _Input_: ``` rtsp://localhost:8554/stream ``` Then _Ok_. ================================================ FILE: docs/3-read/11-python-opencv.md ================================================ # Python and OpenCV Python-based software can read streams from the server with the OpenCV library, acting as a [RTSP client](04-rtsp.md). ```python import cv2 cap = cv2.VideoCapture('rtsp://localhost:8554/mystream') if not cap.isOpened(): raise Exception("can't open video capture") while True: ret, frame = cap.read() if not ret: raise Exception("can't receive frame") cv2.imshow('frame', frame) if cv2.waitKey(1) == ord('q'): break cap.release() cv2.destroyAllWindows() ``` ================================================ FILE: docs/3-read/12-golang.md ================================================ # Golang You can read a stream from the server by using the Go programming language and the following libraries: - [gortsplib](https://github.com/bluenviron/gortsplib) to read with RTSP. - [gortmplib](https://github.com/bluenviron/gortmplib) to read with RTMP. - [gohlslib](https://github.com/bluenviron/gohlslib) to read with HLS. All these power _MediaMTX_ itself. In the repositories of these projects there are several examples on how to connect to a server and read data. ================================================ FILE: docs/3-read/13-unity.md ================================================ # Unity Software written with the Unity Engine can read a stream from the server by using the [WebRTC protocol](03-webrtc.md). Create a new Unity project or open an existing one. Open _Window -> Package Manager_, click on the plus sign, _Add Package by name..._ and insert `com.unity.webrtc`. Wait for the package to be installed. In the _Project_ window, under `Assets`, create a new C# Script called `WebRTCReader.cs` with this content: ```cs using System.Collections; using UnityEngine; using Unity.WebRTC; public class WebRTCReader : MonoBehaviour { public string url = "http://localhost:8889/stream/whep"; private RTCPeerConnection pc; private MediaStream receiveStream; void Start() { UnityEngine.UI.RawImage rawImage = gameObject.GetComponentInChildren(); AudioSource audioSource = gameObject.GetComponentInChildren(); pc = new RTCPeerConnection(); receiveStream = new MediaStream(); pc.OnTrack = e => { receiveStream.AddTrack(e.Track); }; receiveStream.OnAddTrack = e => { if (e.Track is VideoStreamTrack videoTrack) { videoTrack.OnVideoReceived += (tex) => { rawImage.texture = tex; }; } else if (e.Track is AudioStreamTrack audioTrack) { audioSource.SetTrack(audioTrack); audioSource.loop = true; audioSource.Play(); } }; RTCRtpTransceiverInit init = new RTCRtpTransceiverInit(); init.direction = RTCRtpTransceiverDirection.RecvOnly; pc.AddTransceiver(TrackKind.Audio, init); pc.AddTransceiver(TrackKind.Video, init); StartCoroutine(WebRTC.Update()); StartCoroutine(createOffer()); } private IEnumerator createOffer() { var op = pc.CreateOffer(); yield return op; if (op.IsError) { Debug.LogError("CreateOffer() failed"); yield break; } yield return setLocalDescription(op.Desc); } private IEnumerator setLocalDescription(RTCSessionDescription offer) { var op = pc.SetLocalDescription(ref offer); yield return op; if (op.IsError) { Debug.LogError("SetLocalDescription() failed"); yield break; } yield return postOffer(offer); } private IEnumerator postOffer(RTCSessionDescription offer) { var content = new System.Net.Http.StringContent(offer.sdp); content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/sdp"); var client = new System.Net.Http.HttpClient(); var task = System.Threading.Tasks.Task.Run(async () => { var res = await client.PostAsync(new System.UriBuilder(url).Uri, content); res.EnsureSuccessStatusCode(); return await res.Content.ReadAsStringAsync(); }); yield return new WaitUntil(() => task.IsCompleted); if (task.Exception != null) { Debug.LogError(task.Exception); yield break; } yield return setRemoteDescription(task.Result); } private IEnumerator setRemoteDescription(string answer) { RTCSessionDescription desc = new RTCSessionDescription(); desc.type = RTCSdpType.Answer; desc.sdp = answer; var op = pc.SetRemoteDescription(ref desc); yield return op; if (op.IsError) { Debug.LogError("SetRemoteDescription() failed"); yield break; } yield break; } void OnDestroy() { pc?.Close(); pc?.Dispose(); receiveStream?.Dispose(); } } ``` Edit the `url` variable according to your needs. In the _Hierarchy_ window, find or create a scene. Inside the scene, add a _Canvas_. Inside the Canvas, add a _Raw Image_ and an _Audio Source_. Then add the `WebRTCReader.cs` script as component of the canvas, by dragging it inside the _Inspector_ window. then Press the _Play_ button at the top of the page. ================================================ FILE: docs/3-read/14-web-browsers.md ================================================ # Web browsers Web browsers can read a stream from the server in several ways. ## Web browsers and WebRTC You can read a stream by using the [WebRTC protocol](03-webrtc.md) by visiting the web page: ``` http://localhost:8889/mystream ``` See [Embed streams in a website](../4-other/14-embed-streams-in-a-website.md) for instructions on how to embed the stream into an external website. ## Web browsers and HLS Web browsers can also read a stream with the [HLS protocol](06-hls.md). Latency is higher but there are fewer problems related to connectivity between server and clients, furthermore the server load can be balanced by using a common HTTP CDN (like Cloudflare or CloudFront), and this allows to handle an unlimited amount of readers. Visit the web page: ``` http://localhost:8888/mystream ``` See [Embed streams in a website](../4-other/14-embed-streams-in-a-website.md) for instructions on how to embed the stream into an external website. ================================================ FILE: docs/3-read/index.md ================================================ # Read ================================================ FILE: docs/4-other/02-configuration.md ================================================ # Configuration All the configuration parameters are listed and commented in the [configuration file](../5-references/1-configuration-file.md) (`mediamtx.yml`). ## Change the configuration There are several ways to change the configuration: 1. By editing the configuration file, that is - included into the release bundle - available in the root folder of the Docker image (`/mediamtx.yml`); it can be overridden in this way: ```sh docker run --rm -it --network=host -v "$PWD/mediamtx.yml:/mediamtx.yml:ro" bluenviron/mediamtx:1 ``` The configuration can be changed dynamically when the server is running (hot reloading) by writing to the configuration file. Changes are detected and applied without disconnecting existing clients, whenever it's possible. 2. By overriding configuration parameters with environment variables, in the format `MTX_PARAMNAME`, where `PARAMNAME` is the uppercase name of a parameter. For instance, the `rtspAddress` parameter can be overridden in the following way: ``` MTX_RTSPADDRESS="127.0.0.1:8554" ./mediamtx ``` Parameters that have array as value can be overridden by setting a comma-separated list. For example: ``` MTX_RTSPTRANSPORTS="tcp,udp" ``` Parameters in maps can be overridden by using underscores, in the following way: ``` MTX_PATHS_TEST_SOURCE=rtsp://myurl ./mediamtx ``` Parameters in lists can be overridden in the same way as parameters in maps, using their position like an additional key. This is particularly useful if you want to use internal users but define credentials through environment variables: ``` MTX_AUTHINTERNALUSERS_0_USER=username MTX_AUTHINTERNALUSERS_0_PASS=password ``` This method is particularly useful when using Docker; any configuration parameter can be changed by passing environment variables with the `-e` flag: ``` docker run --rm -it --network=host -e MTX_PATHS_TEST_SOURCE=rtsp://myurl bluenviron/mediamtx:1 ``` 3. By using the [Control API](18-control-api.md). ## Encrypt the configuration The configuration file can be entirely encrypted for security purposes by using the `crypto_secretbox` function of the NaCL function. An online tool for performing this operation is [available here](https://play.golang.org/p/rX29jwObNe4). After performing the encryption, put the base64-encoded result into the configuration file, and launch the server with the `MTX_CONFKEY` variable: ``` MTX_CONFKEY=mykey ./mediamtx ``` ================================================ FILE: docs/4-other/03-authentication.md ================================================ # Authentication _MediaMTX_ can be configured to ask clients for credentials, either in the form of username/password or a string-based token. These credentials are then validated through a chosen method. ## Credential validation Credentials can be validated through one of these methods: - Internal database: credentials are stored in the configuration file - External HTTP server: an external HTTP URL is contacted to perform authentication - External JWT provider: an external identity server provides signed tokens that are then verified by the server ### Internal database The internal authentication method is the default one. Users are stored inside the configuration file, in this format: ```yml authInternalUsers: # Username. 'any' means any user, including anonymous ones. - user: any # Password. Not used in case of 'any' user. pass: # IPs or networks allowed to use this user. An empty list means any IP. ips: [] # Permissions. permissions: # Available actions are: publish, read, playback, api, metrics, pprof. - action: publish # Paths can be set to further restrict access to a specific path. # An empty path means any path. # Regular expressions can be used by using a tilde as prefix. path: - action: read path: - action: playback path: ``` Only clients that provide a valid username and password will be able to perform a certain action. If storing plain credentials in the configuration file is a security problem, username and passwords can be stored as hashed strings. The Argon2 and SHA256 hashing algorithms are supported. To use Argon2, the string must be hashed using Argon2id (recommended) or Argon2i: ``` echo -n "mypass" | argon2 saltItWithSalt -id -l 32 -e ``` Then stored with the `argon2:` prefix: ```yml authInternalUsers: - user: argon2:$argon2id$v=19$m=4096,t=3,p=1$MTIzNDU2Nzg$OGGO0eCMN0ievb4YGSzvS/H+Vajx1pcbUmtLp2tRqRU pass: argon2:$argon2i$v=19$m=4096,t=3,p=1$MTIzNDU2Nzg$oct3kOiFywTdDdt19kT07hdvmsPTvt9zxAUho2DLqZw permissions: - action: publish ``` To use SHA256, the string must be hashed with SHA256 and encoded with base64: ``` echo -n "mypass" | openssl dgst -binary -sha256 | openssl base64 ``` Then stored with the `sha256:` prefix: ```yml authInternalUsers: - user: sha256:j1tsRqDEw9xvq/D7/9tMx6Jh/jMhk3UfjwIB2f1zgMo= pass: sha256:BdSWkrdV+ZxFBLUQQY7+7uv9RmiSVA8nrPmjGjJtZQQ= permissions: - action: publish ``` **WARNING**: enable encryption or use a VPN to ensure that no one is intercepting the credentials in transit. ### External HTTP server Authentication can be delegated to an external HTTP server: ```yml authMethod: http authHTTPAddress: http://myauthserver/auth ``` Each time a user needs to be authenticated, the specified URL will be requested with the POST method and this payload: ```json { "user": "user", "password": "password", "token": "token", "ip": "ip", "action": "publish|read|playback|api|metrics|pprof", "path": "path", "protocol": "rtsp|rtmp|hls|webrtc|srt", "id": "id", "query": "query" } ``` If the URL returns a status code that begins with `20` (i.e. `200`), authentication is successful, otherwise it fails. Be aware that it's perfectly normal for the authentication server to receive requests with empty users and passwords, i.e.: ```json { "user": "", "password": "" } ``` This happens because RTSP clients don't provide credentials until they are asked to. In order to receive the credentials, the authentication server must reply with status code `401`, then the client will send credentials. Some actions can be excluded from the process: ```yml # Actions to exclude from HTTP-based authentication. # Format is the same as the one of user permissions. authHTTPExclude: - action: api - action: metrics - action: pprof ``` If the authentication server uses HTTPS and has a self-signed or invalid TLS certificate, you can provide the fingerprint of the certificate to validate it anyway: ```yml authMethod: http authHTTPAddress: https://myauthserver/auth authHTTPFingerprint: 33949e05fffb5ff3e8aa16f8213a6251b4d9363804ba53233c4da9a46d6f2739 ``` The fingerprint can be obtained with: ```sh openssl s_client -connect myauthserver:443 /dev/null | sed -n '/BEGIN/,/END/p' > server.crt openssl x509 -in server.crt -noout -fingerprint -sha256 | cut -d "=" -f2 | tr -d ':' ``` ### External JWT provider Authentication can be delegated to an external identity server, that is capable of generating JWTs and provides a JWKS endpoint. With respect to the HTTP-based method, this has the advantage that the external server is contacted once, and not for every request, greatly improving performance. In order to use the JWT-based authentication method, set `authMethod` and `authJWTJWKS`: ```yml authMethod: jwt authJWTJWKS: http://my_identity_server/jwks_endpoint authJWTClaimKey: mediamtx_permissions ``` Users are expected to pass the encoded JWT as token. The JWT is expected to contain a claim, with a list of permissions in the same format as the one of user permissions: ```json { "mediamtx_permissions": [ { "action": "publish", "path": "" } ] } ``` If the JWKS server uses TLS and has a self-signed or invalid TLS certificate, you can provide the fingerprint of the certificate to validate it anyway: ```yml authMethod: jwt authJWTJWKS: https://my_identity_server/jwks_endpoint authJWTJWKSFingerprint: 33949e05fffb5ff3e8aa16f8213a6251b4d9363804ba53233c4da9a46d6f2739 authJWTClaimKey: mediamtx_permissions ``` The fingerprint can be obtained with: ```sh openssl s_client -connect my_identity_server:443 /dev/null | sed -n '/BEGIN/,/END/p' > server.crt openssl x509 -in server.crt -noout -fingerprint -sha256 | cut -d "=" -f2 | tr -d ':' ``` Optionally, the JWT `iss` (issuer) and `aud` (audience) claims can be validated by setting `authJWTIssuer` and `authJWTAudience`. When set, tokens that don't contain the expected values will be rejected: ```yml authMethod: jwt authJWTJWKS: http://my_identity_server/jwks_endpoint authJWTClaimKey: mediamtx_permissions authJWTIssuer: http://my_identity_server authJWTAudience: mediamtx ``` Leave these fields empty to skip validation of the respective claims. #### Keycloak setup Here's a tutorial on how to setup the [Keycloak identity server](https://www.keycloak.org/) in order to provide JWTs. 1. Start Keycloak: ```sh docker run --name=keycloak -p 8080:8080 \ -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin \ quay.io/keycloak/keycloak:23.0.7 start-dev ``` 2. Open the Keycloak web UI on http://localhost:8080, click on _Administration Console_ and log in. 3. Click on _master_ in the top left corner, _Create realm_, set realm name to `mediamtx`, _Create_. 4. Open page _Client scopes_, _Create client scope_, set name to `mediamtx`, _Save_. 5. Open tab _Mappers_, _Configure a new Mapper_, _User Attribute_: - Name: `mediamtx_permissions` - User Attribute: `mediamtx_permissions` - Token Claim Name: `mediamtx_permissions` - Claim JSON Type: `JSON` - Multivalued: `On` Save. 6. Open page _Clients_, _Create client_, set Client ID to `mediamtx`, _Next_, _Client authentication_ `On`, _Next_, _Save_. 7. Open tab _Credentials_, copy client secret somewhere. 8. Open tab _Client scopes_, set _Assigned type_ of all existing client scopes to _Optional_. This decreases the length of the JWT, since many clients impose limits on it. 9. In tab _Client scopes_, _Add client scope_, Select `mediamtx`, _Add_, _Default_. 10. Open page _Users_, _Add user_, Username `testuser`, _Create_, Tab _Credentials_, _Set password_, pick a password, _Save_. 11. Open tab _Attributes_, _Add an attribute_: - Key: `mediamtx_permissions` - Value: `{"action":"publish", "path": ""}` You can add as many attributes with key `mediamtx_permissions` as you want, each with a single permission in it. 12. In MediaMTX, use the following JWKS URL: ```yml authJWTJWKS: http://localhost:8080/realms/mediamtx/protocol/openid-connect/certs ``` 13. Perform authentication on Keycloak: ``` curl \ -d "client_id=mediamtx" \ -d "client_secret=$CLIENT_SECRET" \ -d "username=$USER" \ -d "password=$PASS" \ -d "grant_type=password" \ http://localhost:8080/realms/mediamtx/protocol/openid-connect/token ``` The JWT is inside the `access_token` key of the response: ```json { "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIyNzVjX3ptOVlOdHQ0TkhwWVk4Und6ZndUclVGSzRBRmQwY3lsM2wtY3pzIn0.eyJleHAiOjE3MDk1NTUwOTIsImlhdCI6MTcwOTU1NDc5MiwianRpIjoiMzE3ZTQ1NGUtNzczMi00OTM1LWExNzAtOTNhYzQ2ODhhYWIxIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9tZWRpYW10eCIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiI2NTBhZDA5Zi03MDgxLTQyNGItODI4Ni0xM2I3YTA3ZDI0MWEiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJtZWRpYW10eCIsInNlc3Npb25fc3RhdGUiOiJjYzJkNDhjYy1kMmU5LTQ0YjAtODkzZS0wYTdhNjJiZDI1YmQiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbIi8qIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIiwiZGVmYXVsdC1yb2xlcy1tZWRpYW10eCJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoibWVkaWFtdHggcHJvZmlsZSBlbWFpbCIsInNpZCI6ImNjMmQ0OGNjLWQyZTktNDRiMC04OTNlLTBhN2E2MmJkMjViZCIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibWVkaWFtdHhfcGVybWlzc2lvbnMiOlt7ImFjdGlvbiI6InB1Ymxpc2giLCJwYXRocyI6ImFsbCJ9XSwicHJlZmVycmVkX3VzZXJuYW1lIjoidGVzdHVzZXIifQ.Gevz7rf1qHqFg7cqtSfSP31v_NS0VH7MYfwAdra1t6Yt5rTr9vJzqUeGfjYLQWR3fr4XC58DrPOhNnILCpo7jWRdimCnbPmuuCJ0AYM-Aoi3PAsWZNxgmtopq24_JokbFArY9Y1wSGFvF8puU64lt1jyOOyxf2M4cBHCs_EarCKOwuQmEZxSf8Z-QV9nlfkoTUszDCQTiKyeIkLRHL2Iy7Fw7_T3UI7sxJjVIt0c6HCNJhBBazGsYzmcSQ_GrmhbUteMTg00o6FicqkMBe99uZFnx9wIBm_QbO9hbAkkzF923I-DTAQrFLxT08ESMepDwmzFrmnwWYBLE3u8zuUlCA", "expires_in": 300, "refresh_expires_in": 1800, "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI3OTI3Zjg4Zi05YWM4LTRlNmEtYWE1OC1kZmY0MDQzZDRhNGUifQ.eyJleHAiOjE3MDk1NTY1OTIsImlhdCI6MTcwOTU1NDc5MiwianRpIjoiMGVhZWFhMWItYzNhMC00M2YxLWJkZjAtZjI2NTRiODlkOTE3IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9tZWRpYW10eCIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9yZWFsbXMvbWVkaWFtdHgiLCJzdWIiOiI2NTBhZDA5Zi03MDgxLTQyNGItODI4Ni0xM2I3YTA3ZDI0MWEiLCJ0eXAiOiJSZWZyZXNoIiwiYXpwIjoibWVkaWFtdHgiLCJzZXNzaW9uX3N0YXRlIjoiY2MyZDQ4Y2MtZDJlOS00NGIwLTg5M2UtMGE3YTYyYmQyNWJkIiwic2NvcGUiOiJtZWRpYW10eCBwcm9maWxlIGVtYWlsIiwic2lkIjoiY2MyZDQ4Y2MtZDJlOS00NGIwLTg5M2UtMGE3YTYyYmQyNWJkIn0.yuXV8_JU0TQLuosNdp5xlYMjn7eO5Xq-PusdHzE7bsQ", "token_type": "Bearer", "not-before-policy": 0, "session_state": "cc2d48cc-d2e9-44b0-893e-0a7a62bd25bd", "scope": "mediamtx profile email" } ``` ## Providing username and password ### RTSP Prepend username and password and a `@` to the host: ``` rtsp rtsp://myuser:mypass@localhost:8554/mystream ``` ### RTMP Use the `user` and `pass` query parameters: ``` rtmp://localhost/mystream?user=myuser&pass=mypass ``` ### SRT Append username and password to `streamid`: ``` srt://localhost:8890?streamid=publish:mystream:user:pass&pkt_size=1316 ``` ### HLS and WebRTC Username and password can be passed through the `Authorization: Basic` HTTP header: ``` Authorization: Basic base64(user:pass) ``` When using a web browser, a dialog is first shown to users, asking for credentials, and then the header is automatically inserted into every request. If you need to automatically fill credentials from a parent web page, read [Embed streams in a website](14-embed-streams-in-a-website.md). If the `Authorization: Basic` header cannot be used (for instance, in software like OBS Studio, which only allows to provide a "Bearer Token"), credentials can be passed through the `Authorization: Bearer` header (i.e. the "Bearer Token" in OBS), where the value is the concatenation of username and password, separated by a colon: ``` Authorization: Bearer username:password ``` ## Providing tokens / JWTs ### RTSP Pass the token as a query parameter: ``` rtsp://localhost:8554/mystream?jwt=jwt ``` WARNING: FFmpeg implementation of RTSP does not support URLs that are longer than 4096 characters (this is the [MAX_URL_SIZE constant](https://github.com/FFmpeg/FFmpeg/blob/f951aa9ef382d6bb517e05d04d52710f751de427/libavformat/internal.h#L30)), therefore you have to configure your identity server in order to produce JWTs that are shorter than this threshold. ### RTMP Pass the token as a query parameter: ``` rtmp://localhost/mystream?jwt=jwt ``` WARNING: FFmpeg implementation of RTMP does not support URLs that are longer than 1024 characters (this is the [TCURL_MAX_LENGTH constant](https://github.com/FFmpeg/FFmpeg/blob/f951aa9ef382d6bb517e05d04d52710f751de427/libavformat/rtmpproto.c#L55)), therefore you have to configure your identity server in order to produce JWTs that are shorter than this threshold. ### SRT Pass the token as password, with an arbitrary user: ``` srt://localhost:8890?streamid=publish:mystream:user:jwt&pkt_size=1316 ``` WARNING: SRT does not support Stream IDs that are longer than 512 characters, therefore you have to configure your identity server in order to produce JWTs that are shorter than this threshold. ### HLS and WebRTC The token can be passed through the `Authorization: Bearer` header: ``` Authorization: Bearer MY_JWT ``` In OBS Studio, this is the "Bearer Token" field. If the `Authorization: Bearer` token cannot be directly provided (for instance, with web browsers that directly access _MediaMTX_ and show a credential dialog), you can pass the token as password, using an arbitrary user. In web browsers, if you need to automatically fill credentials from a parent web page, read [Embed streams in a website](14-embed-streams-in-a-website.md). ================================================ FILE: docs/4-other/04-remuxing-reencoding-compression.md ================================================ # Re-encoding To change the format, codec or compression of a stream, use _FFmpeg_ or _GStreamer_ together with _MediaMTX_. For instance, to re-encode an existing stream, that is available in the `/original` path, and publish the resulting stream in the `/compressed` path, edit `mediamtx.yml` and replace everything inside section `paths` with the following content: ```yml paths: compressed: original: runOnReady: > ffmpeg -i rtsp://localhost:$RTSP_PORT/$MTX_PATH -c:v libx264 -pix_fmt yuv420p -preset ultrafast -b:v 600k -max_muxing_queue_size 1024 -f rtsp rtsp://localhost:$RTSP_PORT/compressed runOnReadyRestart: yes ``` ================================================ FILE: docs/4-other/05-always-available.md ================================================ # Always-available When the publisher or source of a stream is offline, the server can be configured to fill gaps in the stream with an offline segment that is played on repeat until a publisher comes back online. This allows readers to stay connected regardless of the state of the stream. The offline segment and online stream are concatenated without re-encoding any frame, using the original codec. This feature can be enabled by toggling the `alwaysAvailable` flag and filling `alwaysAvailableTracks`: ```yml paths: mypath: alwaysAvailable: true alwaysAvailableTracks: # Available values are: AV1, VP9, H265, H264, Opus, MPEG4Audio, G711, LPCM - codec: H264 # in case of MPEG4Audio, G711, LPCM, sampleRate and ChannelCount must be provided too. # sampleRate: 48000 # channelCount: 2 # in case of G711, muLaw must be provided too. # muLaw: false ``` By default, the server uses a default offline segment with the text "STREAM IS OFFLINE". The segment can be replaced with an external MP4 file: ```yml paths: mypath: alwaysAvailable: true alwaysAvailableFile: "./h264.mp4" ``` ================================================ FILE: docs/4-other/06-record.md ================================================ # Record Live streams be recorded to disk and played back with the following file containers and codecs: | container | codecs | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | fMP4 | **Video**: AV1, VP9, H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video, M-JPEG
**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G711 (PCMA, PCMU), LPCM | | MPEG-TS | **Video**: H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video
**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3 | ## Usage To record available streams to disk, set the `record` parameter in the configuration file: ```yml pathDefaults: # Record streams to disk. record: yes ``` It's also possible to specify additional parameters: ```yml pathDefaults: # Record streams to disk. record: yes # Path of recording segments. # Extension is added automatically. # Available variables are %path (path name), %Y %m %d (year, month, day), # %H %M %S (hours, minutes, seconds), %f (microseconds), %z (time zone), %s (unix epoch). recordPath: ./recordings/%path/%Y-%m-%d_%H-%M-%S-%f # Format of recorded segments. # Available formats are "fmp4" (fragmented MP4) and "mpegts" (MPEG-TS). recordFormat: fmp4 # fMP4 segments are concatenation of small MP4 files (parts), each with this duration. # MPEG-TS segments are concatenation of 188-bytes packets, flushed to disk with this period. # When a system failure occurs, the last part gets lost. # Therefore, the part duration is equal to the RPO (recovery point objective). recordPartDuration: 1s # This prevents RAM exhaustion. recordMaxPartSize: 50M # Minimum duration of each segment. recordSegmentDuration: 1h # Delete segments after this timespan. # Set to 0s to disable automatic deletion. recordDeleteAfter: 1d ``` All available recording parameters are listed in the [configuration file](../5-references/1-configuration-file.md). ## Remote upload To upload recordings to a remote location, you can use _MediaMTX_ together with [rclone](https://github.com/rclone/rclone), a command line tool that provides file synchronization capabilities with a huge variety of services (including S3, FTP, SMB, Google Drive): 1. Download and install [rclone](https://github.com/rclone/rclone). 2. Configure _rclone_: ```sh rclone config ``` 3. Place `rclone` into the `runOnInit` and `runOnRecordSegmentComplete` hooks: ```yml pathDefaults: # this is needed to sync segments after a crash. # replace myconfig with the name of the rclone config. runOnInit: rclone sync -v ./recordings myconfig:/my-path/recordings # this is called when a segment has been finalized. # replace myconfig with the name of the rclone config. runOnRecordSegmentComplete: rclone sync -v --min-age=1ms ./recordings myconfig:/my-path/recordings ``` If you want to delete local segments after they are uploaded, replace `rclone sync` with `rclone move`. ================================================ FILE: docs/4-other/07-playback.md ================================================ # Playback Existing recordings can be played back to users through a dedicated HTTP server, that can be enabled inside the configuration: ```yml playback: yes playbackAddress: :9996 ``` The server provides an endpoint to list recorded timespans: ``` http://localhost:9996/list?path=[mypath]&start=[start]&end=[end] ``` Where: - [mypath] is the name of a path - [start] (optional) is the start date in [RFC3339 format](https://www.utctime.net/) - [end] (optional) is the end date in [RFC3339 format](https://www.utctime.net/) The server will return a list of timespans in JSON format: ```json [ { "start": "2006-01-02T15:04:05Z07:00", "duration": 60.0, "url": "http://localhost:9996/get?path=[mypath]&start=2006-01-02T15%3A04%3A05Z07%3A00&duration=60.0" }, { "start": "2006-01-02T15:07:05Z07:00", "duration": 32.33, "url": "http://localhost:9996/get?path=[mypath]&start=2006-01-02T15%3A07%3A05Z07%3A00&duration=32.33" } ] ``` The server provides an endpoint to download recordings: ``` http://localhost:9996/get?path=[mypath]&start=[start]&duration=[duration]&format=[format] ``` Where: - [mypath] is the path name - [start] is the start date in [RFC3339 format](https://www.utctime.net/) - [duration] is the maximum duration of the recording in seconds - [format] (optional) is the output format of the stream. Available values are "fmp4" (default) and "mp4" All parameters must be [url-encoded](https://www.urlencoder.org/). For instance: ``` http://localhost:9996/get?path=mypath&start=2024-01-14T16%3A33%3A17%2B00%3A00&duration=200.5 ``` The resulting stream uses the fMP4 format, that is natively compatible with any browser, therefore its URL can be directly inserted into a \