Webrtcbin H.264 connection cannot be established if Chrome offer has to be answered
I'm sorry to bother with this, but I can't come to a satisfying solution here. I was asked to re-open a new issue regarding problem 2 of #1644 (comment 1033231), so here it is.
I have refactored the code, so that I now no longer use pre-canned string pipelines, but constructing them on the fly.
For this I have a couple of functions:
def build_webrtcbin(self):
self.webrtcbin = Gst.ElementFactory.make("webrtcbin")
self.webrtcbin.set_property("bundle-policy", "max-bundle")
self.webrtcbin.set_property("stun-server", "stun://stun.l.google.com:19302")
self.webrtcbin.connect('on-negotiation-needed', self.on_negotiation_needed)
self.webrtcbin.connect('on-ice-candidate', self.on_ice_candidate)
self.webrtcbin.connect('pad-added', self.on_pad_added)
self.webrtcbin.connect('notify::connection-state', self.on_connection_state)
self.webrtcbin.connect('notify::signaling-state', self.on_signaling_state)
self.webrtcbin.connect('notify::ice-connection-state', self.on_ice_connection_state)
self.webrtcbin.connect('notify::ice-gathering-state', self.on_ice_gathering_state)
self.pipeline = Gst.Pipeline.new()
self.pipeline.add(self.webrtcbin)
build_webrtcbin()
is called shortly after the connection to the signaling server has been established, generally just once per connection.
Then on an incoming offer in the function which receives and forwards the incoming SDP I'm doing this:
_, sdpmsg = GstSdp.SDPMessage.new()
GstSdp.sdp_message_parse_buffer(bytes(sdp.encode()), sdpmsg)
promise = Gst.Promise.new()
sdptype = GstWebRTC.WebRTCSDPType.ANSWER if sdp_type == 'answer' else GstWebRTC.WebRTCSDPType.OFFER
desc = GstWebRTC.WebRTCSessionDescription.new(sdptype, sdpmsg)
self.logger.info('received sdp {}:\n{}'.format(sdp_type, desc.sdp.as_text()))
self.pipeline = Gst.Pipeline.new()
self.pipeline.add(self.webrtcbin)
self.start_pipeline()
self.webrtcbin.emit('set-remote-description', desc, promise)
promise.interrupt()
# Create an answer if offer received
if sdptype == GstWebRTC.WebRTCSDPType.OFFER:
self.on_create_answer(self.webrtcbin)
The start_pipeline()
looks like so in case I'm focused to send H.264 only:
def start_pipeline(self):
videotestsrc = Gst.ElementFactory.make("videotestsrc", "videotestsrc")
videotestsrc.set_property("is-live", True)
videotestsrc.set_property("pattern", "smpte")
videotestsrc_caps = Gst.caps_from_string("video/x-raw")
videotestsrc_caps.set_value("width", 1280)
videotestsrc_caps.set_value("height", 720)
videotestsrc_caps_filter = Gst.ElementFactory.make("capsfilter")
videotestsrc_caps_filter.set_property("caps", videotestsrc_caps)
videoconvert = Gst.ElementFactory.make("videoconvert", "videoconvert")
x264enc = Gst.ElementFactory.make("x264enc", "x264enc")
rtph264pay = Gst.ElementFactory.make("rtph264pay", "rtph264pay")
rtph264pay.set_property("config-interval", 1)
rtph264pay_caps = Gst.caps_from_string("application/x-rtp")
rtph264pay_caps.set_value("media", "video")
rtph264pay_caps.set_value("encoding-name", "H264")
rtph264pay_caps_filter = Gst.ElementFactory.make("capsfilter")
rtph264pay_caps_filter.set_property("caps", rtph264pay_caps)
self.pipeline.add(videotestsrc)
self.pipeline.add(videotestsrc_caps_filter)
self.pipeline.add(videoconvert)
self.pipeline.add(x264enc)
self.pipeline.add(rtph264pay)
self.pipeline.add(rtph264pay_caps_filter)
Gst.Element.link(videotestsrc, videotestsrc_caps_filter)
Gst.Element.link(videotestsrc_caps_filter, videoconvert)
Gst.Element.link(videoconvert, x264enc)
Gst.Element.link(x264enc, rtph264pay)
Gst.Element.link(rtph264pay, rtph264pay_caps_filter)
Gst.Element.link(rtph264pay_caps_filter, self.webrtcbin)
self.pipeline.set_state(Gst.State.PLAYING)
The result: Even though the answer contains sendrecv and a payload-type as offered the video does not appear on the other side. There is just a spinning wheel in the window.
The same flow works if I start_pipeline()
with a VP8 setup:
def start_pipeline(self):
videotestsrc = Gst.ElementFactory.make("videotestsrc", "videotestsrc")
videotestsrc.set_property("is-live", True)
videotestsrc.set_property("pattern", "ball")
videoconvert = Gst.ElementFactory.make("videoconvert", "videoconvert")
vp8enc = Gst.ElementFactory.make("vp8enc", "vp8enc")
vp8enc.set_property("deadline", 1)
rtpvp8pay = Gst.ElementFactory.make("rtpvp8pay", "rtpvp8pay")
rtpvp8pay_caps = Gst.caps_from_string("application/x-rtp")
rtpvp8pay_caps.set_value("media", "video")
rtpvp8pay_caps.set_value("encoding-name", "VP8")
rtpvp8pay_caps_filter = Gst.ElementFactory.make("capsfilter")
rtpvp8pay_caps_filter.set_property("caps", rtpvp8pay_caps)
self.pipeline.add(videotestsrc)
self.pipeline.add(videoconvert)
self.pipeline.add(vp8enc)
self.pipeline.add(rtpvp8pay)
self.pipeline.add(rtpvp8pay_caps_filter)
Gst.Element.link(videotestsrc, videoconvert)
Gst.Element.link(videoconvert, vp8enc)
Gst.Element.link(vp8enc, rtpvp8pay)
Gst.Element.link(rtpvp8pay, rtpvp8pay_caps_filter)
Gst.Element.link(rtpvp8pay_caps_filter, self.webrtcbin)
self.pipeline.set_state(Gst.State.PLAYING)
Video appears on remote end.
Both full webrtc traces attached. I have no clue what else I could try.