WebRTC - Xamarin。尝试设置本地描述失败

WebRTC - Xamarin. Trying to set local description fails

我已经从 google 存储库编译了 WebRTC 源代码,并在 Android 绑定库项目中使用了 jar 以及各自体系结构文件夹中的本机库(.so 文件)。库编译没有错误。

我有主 Android 应用程序并引用了绑定库的输出 (.dll)。该应用程序也可以正确编译。

  1. 我可以添加音轨和视频轨,并且相机出现了。
  2. 这是我的 ISDPObserver 实现
public class SDPObserver: Java.Lang.Object, ISDPObserver
{
    public void OnCreateFailure(string error)
    {
        _classContext.ReportError("createSDP error: " + error);
    }
    public void OnCreateSuccess(SessionDescription origSdp)
    {
        if (_localSdp != null)
        {
            _classContext.ReportError("Multiple SDP create.");
            return;
        }
        string sdpDescription = origSdp.Description;
        if (_preferIsac)
        {
            sdpDescription = PreferCodec(sdpDescription, _AUDIO_CODEC_ISAC, true);
        }
        if (_videoCallEnabled)
        {
            sdpDescription = PreferCodec(sdpDescription, _preferredVideoCodec, false);
        }
        SessionDescription sdp = new SessionDescription(origSdp.Type, sdpDescription);
        _localSdp = sdp;
        _executor.Execute(new SDPOnCreateSuccessHandler(sdp));
    }
    // THIS IS WHERE THE FREAKING THING FAILS. SESSION DESCRIPTION IS NULL
    public void OnSetFailure(string error)
    {
        _classContext.ReportError("setSDP error: " + error);
    }
    public void OnSetSuccess()
    {
        _executor.Execute(new SDPOnSetSuccessHandler(_localSdp));
    }
}
public class SDPOnCreateSuccessHandler: Java.Lang.Object, Java.Lang.IRunnable
{
    private SessionDescription __sdp;
    public SDPOnCreateSuccessHandler(SessionDescription sdp)
    {
        __sdp = sdp;
    }
    public void Run()
    {
        if (_peerConnection != null && !_isError)
        {
            try
            {
                Log.Debug(TAG, "Set local SDP from " + __sdp.Type);
                _peerConnection.SetLocalDescription(_sdpObserver, __sdp);
            }
            catch (Java.Lang.Exception e)
            {}
            catch (System.Exception e)
            {}
        }
    }
}
public class SDPOnSetSuccessHandler: Java.Lang.Object, Java.Lang.IRunnable
{
    private SessionDescription __localSdp;
    public SDPOnSetSuccessHandler(SessionDescription localSdp)
    {
        __localSdp = localSdp;
    }
    public void Run()
    {
        if (_peerConnection == null || _isError)
        {
            return;
        }
        if (_isInitiator)
        {
            // For offering peer connection we first create offer and set
            // local SDP, then after receiving answer set remote SDP.
            if (_peerConnection.RemoteDescription == null)
            {
                // We've just set our local SDP so time to send it.
                Log.Debug(TAG, "Local SDP set succesfully");
                _events.OnLocalDescription(__localSdp);
            }
            else
            {
                // We've just set remote description, so drain remote
                // and send local ICE candidates.
                Log.Debug(TAG, "Remote SDP set succesfully");
                DrainCandidates();
            }
        }
        else
        {
            // For answering peer connection we set remote SDP and then
            // create answer and set local SDP.
            if (_peerConnection.LocalDescription != null)
            {
                // We've just set our local SDP so time to send it, drain
                // remote and send local ICE candidates.
                Log.Debug(TAG, "Local SDP set succesfully");
                _events.OnLocalDescription(__localSdp);
                DrainCandidates();
            }
            else
            {
                // We've just set remote SDP - do nothing for now -
                // answer will be created soon.
                Log.Debug(TAG, "Remote SDP set succesfully");
            }
        }
    }
}
  1. OnCreateSuccess 按预期触发,我能够使用 autio 编解码器和 cideo 编解码器值更新 SessionDescription。接下来,执行运行方法SDPOnCreateSuccessHandler。这是它无法设置本地描述的地方。 _peerConnection.SetLocalDescription(_sdpObserver, __sdp) 调用.NET生成的代码如下我愿意):
static IntPtr id_setLocalDescription_Lorg_webrtc_SdpObserver_Lorg_webrtc_SessionDescription_;
// Metadata.xml XPath method reference: path="/api/package[@name='org.webrtc']/class[@name='PeerConnection']/method[@name='setLocalDescription' and count(parameter)=2 and parameter[1][@type='org.webrtc.SdpObserver'] and parameter[2][@type='org.webrtc.SessionDescription']]"
[Register("setLocalDescription", "(Lorg/webrtc/SdpObserver;Lorg/webrtc/SessionDescription;)V", "GetSetLocalDescription_Lorg_webrtc_SdpObserver_Lorg_webrtc_SessionDescription_Handler")]
public virtual unsafe void SetLocalDescription(global::Org.Webrtc.ISdpObserver p0, global::Org.Webrtc.SessionDescription p1)
{
if (id_setLocalDescription_Lorg_webrtc_SdpObserver_Lorg_webrtc_SessionDescription_ == IntPtr.Zero)
    id_setLocalDescription_Lorg_webrtc_SdpObserver_Lorg_webrtc_SessionDescription_ = JNIEnv.GetMethodID(class_ref, "setLocalDescription", "(Lorg/webrtc/SdpObserver;Lorg/webrtc/SessionDescription;)V");
try
{
    JValue * __args = stackalloc JValue[2];
    __args[0] = new JValue(p0);
    __args[1] = new JValue(p1);
    if (GetType() == ThresholdType)
        JNIEnv.CallVoidMethod(((global::Java.Lang.Object) this).Handle, id_setLocalDescription_Lorg_webrtc_SdpObserver_Lorg_webrtc_SessionDescription_, __args);
    else
        JNIEnv.CallNonvirtualVoidMethod(((global::Java.Lang.Object) this).Handle, ThresholdClass, JNIEnv.GetMethodID(ThresholdClass, "setLocalDescription", "(Lorg/webrtc/SdpObserver;Lorg/webrtc/SessionDescription;)V"), __args);
}
finally
{}
}

函数计算结果为 "if" 条件并在行中失败:

JNIEnv.CallVoidMethod (((global::Java.Lang.Object) this).Handle, id_setLocalDescription_Lorg_webrtc_SdpObserver_Lorg_webrtc_SessionDescription_, __args);
  1. 这是更新后的会话描述值:
v = 0
o = -5449345243432399727 2 IN IP4 127.0.0.1
s = - t = 0 0
a = group: BUNDLE audio video data
a = msid - semantic: WMS ARDAMS
m = audio 9 UDP / TLS / RTP / SAVPF 111 103 104 9 102 0 8 106 105 13 110 112 113 126
c = IN IP4 0.0.0.0
a = rtcp: 9 IN IP4 0.0.0.0
a = ice - ufrag: Jyke
a = ice - pwd: j85eXW9UUZMcqKnQ7ZOErU28
a = ice - options: renomination
a = fingerprint: sha - 256 E8: 3A: 8B: 26: CC: 6D: 9C: 8B: 7F: 48: A0: E7: D0: 81: 3F: B2: 2E: E1: 2E: 9A: 07: 4C: CC: 66: 77: F3: 64: FC: 51: 1C: 9B: 85
a = setup: actpass
a = mid: audio
a = extmap: 1 urn: ietf: params: rtp - hdrext: ssrc - audio - level
a = sendrecv
a = rtcp - mux
a = rtpmap: 111 opus / 48000 / 2
a = rtcp - fb: 111 transport - cc
a = fmtp: 111 minptime = 10;
useinbandfec = 1
a = rtpmap: 103 ISAC / 16000
a = rtpmap: 104 ISAC / 32000
a = rtpmap: 9 G722 / 8000
a = rtpmap: 102 ILBC / 8000
a = rtpmap: 0 PCMU / 8000
a = rtpmap: 8 PCMA / 8000
a = rtpmap: 106 CN / 32000
a = rtpmap: 105 CN / 16000
a = rtpmap: 13 CN / 8000
a = rtpmap: 110 telephone - event / 48000
a = rtpmap: 112 telephone - event / 32000
a = rtpmap: 113 telephone - event / 16000
a = rtpmap: 126 telephone - event / 8000
a = ssrc: 701282247 cname: rwclcMyxn371tKuJ
a = ssrc: 701282247 msid: ARDAMS ARDAMSa0
a = ssrc: 701282247 mslabel: ARDAMS
a = ssrc: 701282247 label: ARDAMSa0
m = video 9 UDP / TLS / RTP / SAVPF 96 98 100 127 125 97 99 101 124
c = IN IP4 0.0.0.0
a = rtcp: 9 IN IP4 0.0.0.0
a = ice - ufrag: Jyke
a = ice - pwd: j85eXW9UUZMcqKnQ7ZOErU28
a = ice - options: renomination
a = fingerprint: sha - 256 E8: 3A: 8B: 26: CC: 6D: 9C: 8B: 7F: 48: A0: E7: D0: 81: 3F: B2: 2E: E1: 2E: 9A: 07: 4C: CC: 66: 77: F3: 64: FC: 51: 1C: 9B: 85
a = setup: actpass
a = mid: video
a = extmap: 2 urn: ietf: params: rtp - hdrext: toffset
a = extmap: 3 http: //www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a = extmap: 4 urn: 3gpp: video - orientation
a = extmap: 5 http: //www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a = extmap: 6 http: //www.webrtc.org/experiments/rtp-hdrext/playout-delay
a = sendrecv
a = rtcp - mux
a = rtcp - rsize
a = rtpmap: 96 VP8 / 90000
a = rtcp - fb: 96 ccm fir
a = rtcp - fb: 96 nack
a = rtcp - fb: 96 nack pli
a = rtcp - fb: 96 goog - remb
a = rtcp - fb: 96 transport - cc
a = rtpmap: 98 VP9 / 90000
a = rtcp - fb: 98 ccm fir
a = rtcp - fb: 98 nack
a = rtcp - fb: 98 nack pli
a = rtcp - fb: 98 goog - remb
a = rtcp - fb: 98 transport - cc
a = rtpmap: 100 red / 90000
a = rtpmap: 127 ulpfec / 90000
a = rtpmap: 125 H264 / 90000
a = rtcp - fb: 125 ccm fir
a = rtcp - fb: 125 nack
a = rtcp - fb: 125 nack pli
a = rtcp - fb: 125 goog - remb
a = rtcp - fb: 125 transport - cc
a = fmtp: 125 level - asymmetry - allowed = 1;
packetization - mode = 1;
profile - level - id = 42e01f
a = rtpmap: 97 rtx / 90000
a = fmtp: 97 apt = 96
a = rtpmap: 99 rtx / 90000
a = fmtp: 99 apt = 98a = rtpmap: 101 rtx / 90000
a = fmtp: 101 apt = 100
a = rtpmap: 124 rtx / 90000
a = fmtp: 124 apt = 125
a = ssrc - group: FID 1610329415 966940881
a = ssrc: 1610329415 cname: rwclcMyxn371tKuJ
a = ssrc: 1610329415 msid: ARDAMS ARDAMSv0
a = ssrc: 1610329415 mslabel: ARDAMS
a = ssrc: 1610329415 label: ARDAMSv0
a = ssrc: 966940881 cname: rwclcMyxn371tKuJ
a = ssrc: 966940881 msid: ARDAMS ARDAMSv0
a = ssrc: 966940881 mslabel: ARDAMS
a = ssrc: 966940881 label: ARDAMSv0
m = application 9 UDP / TLS / RTP / SAVPF 109
c = IN IP4 0.0.0.0
b = AS: 30
a = rtcp: 9 IN IP4 0.0.0.0
a = ice - ufrag: Jyke
a = ice - pwd: j85eXW9UUZMcqKnQ7ZOErU28
a = ice - options: renomination
a = fingerprint: sha - 256 E8: 3A: 8B: 26: CC: 6D: 9C: 8B: 7F: 48: A0: E7: D0: 81: 3F: B2: 2E: E1: 2E: 9A: 07: 4C: CC: 66: 77: F3: 64: FC: 51: 1C: 9B: 85
a = setup: actpass
a = mid: data
a = sendrecv
a = rtcp - mux
a = rtpmap: 109 google - data / 90000
a = ssrc: 3087089883 cname: rwclcMyxn371tKuJ
a = ssrc: 3087089883 msid: ApprtcDemo data ApprtcDemo data
a = ssrc: 3087089883 mslabel: ApprtcDemo data
a = ssrc: 3087089883 label: ApprtcDemo data

哪位大神能帮我看看有没有什么遗漏的?

好的。我搞定了。

两件事:

1.会话描述格式不正确,TURN服务器将拒绝任何格式不正确的信息。

2. 与 TURN 服务器的所有通信都应该在单个线程中而不是并行线程中。 IScheduledExecutorService 接口的方法 NewSingleThreadScheduledExecutor() 正是这样做的。在构造函数中实例化的接口对象应该被重用来执行 IRunnable 处理程序。这将确保应用程序是线程安全的,并且事件在触发时得到处理。