avd
-- is a streaming server that uses libav
under the hood (as an alternative to nginx-module-rtmp
, mediamtx
, MonaServer
or whatnot).
On one hand, libav
is a legendary, powerful, fine-polished and fine-tuned video/audio processing library, that supports RTMP, RTSP, SRT and other protocols out of the box.
On the other hand, libav
lacks capabilities to serve these protocols beyond just accepting a pre-defined stream or/and using the protocols as a client.
avd
fixes that problem, by wrapping around libav
to manage multiple streams, that could be processed pretty similar to how you would do it with a normal streaming server.
The best I found as a streaming server was mediamtx
, but unfortunately it handled pretty poorly all of my edge cases. While I need a tool I can trust: a tool that just works.
Investigating myself all the intricacies of H264, RTMP, RTSP, SRT, HEVC, AAC, etc, to find a way to workaround mediamtx
was taking too much time. Moreover, mediamtx
does not allow really integrating into other projects (because it keeps everything in internal
) which is in a strong conflict with one of my hobby project.
So I decided to just reuse all the fine-polishing of libav
a make a server out of that. Gladfully, I've already previously made a library that makes that easy: avpipeline
.
nginx-module-rtmp
: very poor debugging, does not support the protocols I need; and not integratable into another Go project.mediamtx
: does not work on my edge cases; and not integratable into another Go project.livego
: it was much worse than mediamtx for my use cases (do not remember the exact reasons); and not integratable into another Go project.go2rtc
: it appeared to be just a forwarding/routing server, rather than a normal server (e.g.: ITS#1238); not integratable into another Go project; and even those were not problems by now I'm convinced it would not have handled my edge cases better than mediamtx.- So on.
I also tried to solve my problems with just small libraries/packages, e.g. github.com/yutopp/go-rtmp (see also github.com/xaionaro-go/go-rtmp), but all of them were even further from supporting my edge cases. For example, IIRC, go-rtmp
did not even support multihour streams (the timestamp field in RTMP was overflowing).
The general pattern is that a project:
- Does not support protocols I need.
- Works badly in edge cases.
- Is too difficult to build for Android/iOS/Linux/whatever.
- Is not integratable into an existing Go project.
- Has major bugs/limitations even in normal cases.
So the hope is that if I'll just use libav
I'll avoid these problems better than the other projects, but with focus on solving my personal edge cases.
$ avd --generate-config | tee ~/.avd.conf
ports:
- address: tcp:127.0.0.1:1936
mode: "publishers"
publish_mode: exclusive-ta
8000
keover
protocol_handler:
rtmp: {}
default_route_path: ""
on_end: "close_consumers"
- address: tcp:0.0.0.0:1935
mode: "consumers"
publish_mode: exclusive-takeover
protocol_handler:
rtmp: {}
default_route_path: ""
on_end: "close_consumers"
- address: tcp:0.0.0.0:1937
mode: "consumers"
publish_mode: exclusive-takeover
protocol_handler:
rtmp: {}
default_route_path: ""
on_end: "wait_for_new_publisher"
- address: tcp:127.0.0.1:8555
mode: "publishers"
publish_mode: exclusive-takeover
protocol_handler:
rtsp:
transport_protocol: ""
default_route_path: ""
on_end: "close_consumers"
- address: udp:127.0.0.1:4445
mode: "publishers"
publish_mode: exclusive-takeover
protocol_handler:
mpegts: {}
default_route_path: mystream
on_end: "close_consumers"
endpoints:
mystream:
forwardings:
- destination: {}
recoding:
audio_track_configs:
- input_track_ids:
- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
output_track_ids:
- 0
codec_name: copy
averaging_period: 0s
average_bit_rate: 0
custom_options: []
hardware_device_type: 0
hardware_device_name: ""
video_track_configs:
- input_track_ids:
- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
output_track_ids:
- 1
codec_name: copy
averaging_period: 0s
average_bit_rate: 0
custom_options: []
hardware_device_type: 0
hardware_device_name: ""
$ avd
It should work. Now let's do something useful:
Modify the config to:
ports:
- address: tcp:127.0.0.1:1936
rtmp:
mode: "publishers"
endpoints:
mystream:
forwardings:
- destination:
url: "rtmp://127.0.0.1:1399/test-stream"
Run:
$ ffplay -f flv -listen 1 rtmp://127.0.0.1:1399/test-stream
Run the avd
again:
$ avd
Now send some stream to avd
, e.g.:
$ ffmpeg -re -i /tmp/1.flv -c copy -f flv rtmp://127.0.0.1:1936/mystream
In result, you'll see that ffplay
is playing your stream:
ffmpeg -> avd -> ffplay
An example of an RTMP server:
import (
"fmt"
"net"
"github.com/xaionaro-go/avd/pkg/avd"
)
func serveRTMP(ctx context.Context) error {
srv := avd.NewServer()
_, err = srv.Listen(ctx, "tcp:127.0.0.1:1936", avd.ProtocolRTMP, avd.RTMPModePublishers)
if err != nil {
return fmt.Errorf("unable to listen %s with the RTMP-publishers handler: %w", publishersListener.Addr(), err)
}
_, err = srv.Listen(ctx, "tcp:0.0.0.0:1935", avd.ProtocolRTMP, avd.RTMPModeConsumers)
if err != nil {
return fmt.Errorf("unable to listen %s with the RTMP-consumers handler: %w", consumersListener.Addr(), err)
}
srv.Wait(ctx)
return nil
}
Unfortunately we have to split publishers and consumers to two ports due to internal limitations of libav
.