Using webrtc data channel with PixelStreaming2

Hi guys,

Has anyone found a way to communicate with the stream broadcaster using data channels?

When I create a new data channel and send messages to the broadcaster it gets to the FEpicRtcStreamer , but then errors out because it cannot find a player that is waiting for the message.

That is pretty much what I expected, but I cannot find any way of creating a data channel so a player can receive the messages

Cheers

Paul

Hi @PaulBlythe

I guess, I found the root cause.

You’ve only set up a one-way data channel. When you create a data channel and send messages to the broadcaster, it reaches the FEpicRtcStreamer but fails because there’s no corresponding data channel established on the player side to receive responses.

You need to establish data channels on BOTH sides of the connection:

On the broadcaster side (Epic RTC Streamer), create an outbound data channel when a player connects using createDataChannel(), listen for incoming data channels from players using the datachannel event listener on your RTCPeerConnection, and wait for both channels to be in “open” state before attempting to send messages.

On the player side (web browser), create your own data channel to send messages to the broadcaster, listen for the broadcaster’s data channel using the datachannel event, and handle both incoming and outgoing message flows.

To fix this: modify your broadcaster to create data channels for each connecting player during WebRTC negotiation, update your players to create their own data channels to the broadcaster (not just send on an existing one), and make sure both sides wait for channel state to be “open” before attempting communication.

The remote peer can receive data channels by listening for the datachannel event on the RTCPeerConnection object.

The error happens because WebRTC data channels require reciprocal establishment - both peers need to participate in the channel setup process.

If you want a simpler no-code pixel streaming solution, check out Vagon Streams - it offers the lowest latency pixel streaming with worldwide coverage thanks to 20+ datacenters globally, eliminating the need for complex WebRTC data channel setup.

Hi @PaulBlythe

I guess, I found the root cause.

You’ve only set up a one-way data channel. When you create a data channel and send messages to the broadcaster, it reaches the FEpicRtcStreamer but fails because there’s no corresponding data channel established on the player side to receive responses.

You need to establish data channels on BOTH sides of the connection:

On the broadcaster side (Epic RTC Streamer), create an outbound data channel when a player connects using createDataChannel(), listen for incoming data channels from players using the datachannel event listener on your RTCPeerConnection, and wait for both channels to be in “open” state before attempting to send messages.

On the player side (web browser), create your own data channel to send messages to the broadcaster, listen for the broadcaster’s data channel using the datachannel event, and handle both incoming and outgoing message flows.

To fix this: modify your broadcaster to create data channels for each connecting player during WebRTC negotiation, update your players to create their own data channels to the broadcaster (not just send on an existing one), and make sure both sides wait for channel state to be “open” before attempting communication.

The remote peer can receive data channels by listening for the datachannel event on the RTCPeerConnection object.

The error happens because WebRTC data channels require reciprocal establishment - both peers need to participate in the channel setup process.

If you want a simpler no-code pixel streaming solution, check out Vagon Streams - it offers the lowest latency pixel streaming with worldwide coverage thanks to 20+ datacenters globally, eliminating the need for complex WebRTC data channel setup.

Yes I am aware that I need to create the data channel at both ends, however this is not trivial as most of pixel streaming is hidden from top level code.

I don’t even know of a clean way of getting the peer connection so creating a data channel is not easy.

I will dig more into the input handler as that uses data channels

Hey @PaulBlythe ,

It seems @Serd is confused by your question as the response isn’t quite accurate.

The FEpicRtcStreamer is UE side code that doesn’t expose a createDataChannel method. Instead, the FEpicRtcStreamer should automatically create a data channel for you.

In your question, you state “When I create a new data channel". How are you doing this?

As far as I understand, you need to create a whole new “player” by sending the subscribe message as part of the signalling protocol. Failing to do so will mean the streamer has no idea of an associating player for your data channel and will result in the error your seeing.

Let me know if you need any more help.

Cheers

Hi , thanks for replying

On the client end I do have access to the peer connection, so I create a data channel as normal.

When I send a message to this data channel I get the “cannot find player” error.

I have sent a subscribe message , and everything else works. I get video and sound and I can even interact by using a back channel UDP messaging system, but this system is prone to lag and not really fit for purpose.

Which is why I am trying to get a data channel working.

If I create my own data channel , the client reports that the data channel is not open.

So I dug around a little more.

I found that there is a data channel open used by the SendPixelStreamingResponse node. This channel is rather unhelpfully named “0”

I can send messages to that without the client reporting an error , but the broadcaster still errors out.

Looking through the docs I see there is no matching ReceivePIxelStreamingResponse node so now I have to think again.

My point of attack this morning is the OnDataTrackOpen event

Cheers

Paul

Some more details

I have added an input component to my player controller and put some logging into it

[2025.09.08-08.40.27:852][700]LogPixelStreaming2RTC: FEpicRtcStreamer::OnDataTrackState(DataTrack=[Player0], Player=[Player0], State=[Active])
[2025.09.08-08.40.27:853][700]LogBlueprintUserMessages: [KFCPlayerController_C_2147482393] DefaultStreamer
[2025.09.08-08.40.27:853][700]LogPixelStreaming2RTC: FEpicRtcStreamer::OnDataTrackUpdate(Participant [Player0], DataTrack [input])
[2025.09.08-08.40.27:853][700]LogPixelStreaming2RTC: FEpicRtcStreamer::OnDataTrackUpdate(Participant [Player0], DataTrack [0])
[2025.09.08-08.40.27:853][700]LogPixelStreaming2RTC: Warning: FEpicRtcStreamer::OnDataTrackState(Failed to find a player for data track [input])
[2025.09.08-08.40.27:853][700]LogPixelStreaming2RTC: Warning: FEpicRtcStreamer::OnDataTrackState(Failed to find a player for data track [0])
[2025.09.08-08.40.27:863][701]LogPixelStreaming2RTC: FEpicRtcStreamer::OnDataTrackMessage(Failed to find a player for data track [input])

You can see the system is trying to connect up two data channels to the player, but failing

A connected issue is that pixel streaming 2 input component is not working at all

Seems that the “input” data channel never gets bound to a player and hence input events never get passed through

Heya @PaulBlythe ,

Let’s try and work through these.

As far as I understand it, you don’t need to manually create a data channel on the client side. As the application will add one to the peer connection, all you need to do on the browser side is listen to the onDataChannel as they do in the PixelStreamingInfrastructure (here).

Receiving messages is done by binding to the OnInputEvent of the PixelStreaming2InputComponent.

To re-iterate, you shouldn’t need to be manually creating anything. The application already adds all of the tracks for you. You can listen to the appropriate listeners on the client side, or send messages from the broadcaster with the appropriate blueprint interface.

Cheers

Hi @BiggestBlokeAU1

I am trying to send messages from the client , in your terms browser side, to the broadcaster.

Binding to the OnInputEvent produces nothing. No events,

Doesn’t matter if I send messages on any webrtc data channels they are never attached to a player , hence the error messages.

Even keyboard and mouse inputs are not received by the broadcaster, they just produce the same error as above.

I am still debugging the input system to try and figure out why.

Cheers

Paul

So my problem is that the EpicRTCStreamer creates the data channels here….

EpicRtcDataSource DataSource = {
	._label = ParticipantInterface->GetId(),
	._maxRetransmitTime = 0,
	._maxRetransmits = 0,
	._isOrdered = true,
	._protocol = EpicRtcDataSourceProtocol::Sctp
};
ParticipantConnection->AddDataSource(DataSource);

This calls down to the sealed library EpicRTC

However nothing connects these data channels to the rest of the system

So when a message arrives at the streamer it is not associated with a player and cannot be routed to a handler so it errors out

Okay , it’s actually worse than I thought.

The one data track created by the code is used to send messages to the client , and the player context can only hold a single data track, so I am screwed.

Does anyone know what this function actually does ?

ParticipantConnection->AddDataSource(DataSource);

It is hidden away in a library so I cannot see it.

Webrtc allows you to use as many data tracks as you want, I need to modify the code to allow that but I cannot see how the above code can create a EpicRtcDataTrackInterface

Hey @PaulBlythe,

Your correct in identifying where the PS2 streamer adds that data track. All this does is signal to EpicRtc that the connection should include a data track. Once the connection is established and tracks are properly created, the FEpicRtcStreamer::OnDataTrackUpdate callback will be executed which links the created data track to the participant.

You are also correct in stating that WebRTC allows for numerous data tracks, but it seems the PS2 only allows a single data track to be used between a streamer and participant. Are you able to re-use this existing track?

Hi @BiggestBlokeAU1 , no I will not be able to re-use that data track.

I am going to have to modify the player context to allow multiple data tracks and expose an interface to allow blueprints to use them.

Pain in the proverbial as it means I will have to maintain this code as PS2 advances, but it is an absolute necessity for me.

Thanks for your time.

Cheers

Paul