stream RTMP to VLC Network stream

Hello, currently we are using membrane_rtmp_plugin to receive RTMP Stream as source (with help of Membrane.RTMPServer and Membrane.RTMP.SourceBin). All is fine, we migrated successfully to 0.26.0 version, which simplifies pipeline a lot. Also we did a POC of streaming RTMP to streaming service (Youtube) and everything is working as expected. I am curious, is there any way to stream RTMP to VLC Player (probably it is called pull approach)? I mean File -> Open Network -> Specify URL (eg. rtmp://localhost:1935/app/stream1)
7 Replies
mat_hek
mat_hek2mo ago
Hi @oleg.okunevych, if it doesn't work out of the box, then probably we don't support this method. However, since our RTMP egress is based on FFmpeg, it should be quite straightforward to add if FFmpeg supports it. However, streaming to ffplay should work if you can use it instead of VLC 😉
oleg.okunevych
oleg.okunevychOP2mo ago
Hey @mat_hek thanks for your answer. Using ffplay to validate the stream is totally fine for our purposes, the thing is that I still getting an error. Currently we run the RTMP server to receive the stream using following code:
handle_new_client = fn client_ref, app, stream_key ->
Logger.info("New RTMP connection: #{inspect(client_ref)}")
send(parent_process_pid, {:client_ref, client_ref, app, stream_key})
Membrane.RTMP.Source.ClientHandlerImpl
end

{:ok, _server} =
RTMPServer.start_link(
handle_new_client: handle_new_client,
port: Keyword.get(state.opts, :server_port, 1935),
use_ssl?: Keyword.get(state.opts, :use_ssl?, false)
)
handle_new_client = fn client_ref, app, stream_key ->
Logger.info("New RTMP connection: #{inspect(client_ref)}")
send(parent_process_pid, {:client_ref, client_ref, app, stream_key})
Membrane.RTMP.Source.ClientHandlerImpl
end

{:ok, _server} =
RTMPServer.start_link(
handle_new_client: handle_new_client,
port: Keyword.get(state.opts, :server_port, 1935),
use_ssl?: Keyword.get(state.opts, :use_ssl?, false)
)
Our pipeline looks following:
child(:rtmp_funnel, Membrane.Funnel),
child(:rtmp_tcp_sink, %Membrane.TCP.Sink{
connection_side: :server,
local_address: {127, 0, 0, 1},
local_port_no: 1937,
close_on_eos: false,
on_connection_closed: :drop_buffers
}),
child(:source, %SourceBin{
client_ref: state.client_ref
})
|> via_out(:video)
|> get_child(:rtmp_funnel)
|> get_child(:rtmp_tcp_sink),
get_child(:source)
|> via_out(:audio)
|> get_child(:rtmp_funnel)
|> get_child(:rtmp_tcp_sink)
child(:rtmp_funnel, Membrane.Funnel),
child(:rtmp_tcp_sink, %Membrane.TCP.Sink{
connection_side: :server,
local_address: {127, 0, 0, 1},
local_port_no: 1937,
close_on_eos: false,
on_connection_closed: :drop_buffers
}),
child(:source, %SourceBin{
client_ref: state.client_ref
})
|> via_out(:video)
|> get_child(:rtmp_funnel)
|> get_child(:rtmp_tcp_sink),
get_child(:source)
|> via_out(:audio)
|> get_child(:rtmp_funnel)
|> get_child(:rtmp_tcp_sink)
I assume that this pipeline should pass the payload from the RTMP server through the funnel into :rtmp_tcp_sink which creates a TCP connection on the 1937 port. Using netcat, I see that we are receiving the payload over the TCP Connection, also Membrane Pipeline visualization tool show that TCP Sink processes the data. However, when using ffplay ffplay rtmp://localhost:1937/app/stream1 we get an error:
[rtmp @ 0x60000331ab50] Server response validating failed
rtmp://localhost:1937/app/stream1: Input/output error
[rtmp @ 0x60000331ab50] Server response validating failed
rtmp://localhost:1937/app/stream1: Input/output error
Should I use Membrane.TCP.Sink, or we may consider other library/module/approach for our use case?
mat_hek
mat_hek2mo ago
Well, this pipeline seems to send H264 and AAC through TCP. RTMP is more than that, like it has session establishment. It also requires the stream to be muxed in FLV before sending. Long story short, you should probably use https://hexdocs.pm/membrane_rtmp_plugin/Membrane.RTMP.Sink.html 😉
oleg.okunevych
oleg.okunevychOP2mo ago
Exactly, using Membrane.RMTP.Sink module was first that came into my mind, however it can be used to push the stream to something that already waiting the stream itself. It worked for me with Youtube. However, we need the ability to send RTMP stream on demand, for instance user clicks the link, we handle this http response adding some Sink to the pipeline and respond user with rtmp url that he can use with ffplay or some other tool I believe that the idea of RTMP.Sink is to push stream to some endpoint that is already listening for incoming connections
mat_hek
mat_hek2mo ago
I believe that the idea of RTMP.Sink is to push stream to some endpoint that is already listening for incoming connections
That's right. But to use the TCP Sink, you'd have to add all the RTMP-specific stuff into it, which would be quite messy. I think the way would be to create an RTMP sink that could get the client_ref from the RTMP server, like the source does. @Łukasz Kita WDYT?
varsill
varsill2mo ago
Hello! Indeed, currently RTMP server only allows clients to publish their streams (and RTMP server's user can get data from the published stream via handle_data callback in the ClientHandler behaviour). To make RTMP server feature-complete the RTMP server would need to handle play command as well - with this feature, you could use it in your scenario. It would require some work, in particular we would need to parse play commands and some other messages etc. Currently, our plan for development is more focused on getting rid of FFmpeg dependency and rewritting RTMP.Sink (which provides sender client). Once that is done, adding handling of play command in the server should be relatively easy.
oleg.okunevych
oleg.okunevychOP2mo ago
@mat_hek @Łukasz Kita thank you for details, will come back to that feature in a while
Want results from more Discord servers?
Add your server