Explorar o código

分机广播功能

weizhengliang %!s(int64=2) %!d(string=hai) anos
pai
achega
97d51e381d

+ 66 - 11
callingbed/src/main/java/com/wdkl/app/ncs/callingbed/activity/CallingbedActivity.kt

@@ -59,6 +59,9 @@ import com.wdkl.ncs.android.middleware.tcp.enums.TcpType
 import com.wdkl.ncs.android.middleware.udp.ServerInfoUtil
 import com.wdkl.ncs.android.middleware.utils.CommonUtils
 import com.wdkl.ncs.android.middleware.utils.StringUtil
+import com.wdkl.ncs.janus.client.JanusClient
+import com.wdkl.ncs.janus.client.StreamingCallback
+import com.wdkl.ncs.janus.rtc.WebRTCEngine
 import com.wdkl.ncs.janus.util.JanusConstant
 import kotlinx.android.synthetic.main.callingbed_main_lay.*
 import kotlinx.android.synthetic.main.view_title_layout.*
@@ -115,6 +118,8 @@ class CallingbedActivity :BaseActivity<CallingbedActivityPresenter, CallingbedMa
 
     private var serverSuccess = false
 
+    private var janusClient: JanusClient? = null
+
     //主信息
     private val mainFragment = "main_fragment"
     //医嘱
@@ -445,6 +450,7 @@ class CallingbedActivity :BaseActivity<CallingbedActivityPresenter, CallingbedMa
         handler.removeCallbacksAndMessages(null)
         SoundPoolManager.getInstance().release()
         isTimeWorking = false
+        stopBroadcast(false)
     }
 
     fun initDevice() {
@@ -1170,18 +1176,23 @@ class CallingbedActivity :BaseActivity<CallingbedActivityPresenter, CallingbedMa
 
     //开始呼叫
     fun startCall(type: Int) {
-        //通话之前先判断webrtc socket和tcp是否连接正常,否则不能建立通话
-        if (Constant.TCP_CONNECTED && !TextUtils.isEmpty(Constant.SIP_ID)) {
-            //去电界面
-            Constant.CALL_TYPE = type
-            Constant.CALL_STATE = Constant.CALL_OUTGOING
-            var fragment = SkyCallFragment()
-            var bundle = Bundle()
-            bundle.putInt("call_state", 0)
-            fragment.arguments = bundle
-            addCallFragment(fragment)
+        if (Constant.CUSTOM_ID != -1) {
+            stopBroadcast(false)
+            //通话之前先判断webrtc socket和tcp是否连接正常,否则不能建立通话
+            if (Constant.TCP_CONNECTED && !TextUtils.isEmpty(Constant.SIP_ID)) {
+                //去电界面
+                Constant.CALL_TYPE = type
+                Constant.CALL_STATE = Constant.CALL_OUTGOING
+                var fragment = SkyCallFragment()
+                var bundle = Bundle()
+                bundle.putInt("call_state", 0)
+                fragment.arguments = bundle
+                addCallFragment(fragment)
+            } else {
+                showMessage(R.string.call_init_error)
+            }
         } else {
-            showMessage(R.string.call_init_error)
+            showMessage(R.string.custom_empty)
         }
     }
 
@@ -1244,6 +1255,7 @@ class CallingbedActivity :BaseActivity<CallingbedActivityPresenter, CallingbedMa
                     Log.d("TCP", "received tcp action: " + tcpModel.action + ", type: " + tcpModel.type)
                     if (tcpModel.getType() == TcpType.VOICE) {
                         if (tcpModel.data != null) {
+                            stopBroadcast(false)
                             val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
                             if (tcpModel.action == TcpAction.VoiceAction.CALL) {
                                 Constant.fromId = tcpModel.fromId
@@ -1294,6 +1306,19 @@ class CallingbedActivity :BaseActivity<CallingbedActivityPresenter, CallingbedMa
                             //紧急呼叫已处理
                             SOSHelper.sosStop()
                         }
+                    } else if (tcpModel.type == TcpType.BROADCAST) {
+                        if (tcpModel.action == TcpAction.BroadcastAction.START) {
+                            if (Constant.TCP_CONNECTED && !TextUtils.isEmpty(Constant.SIP_ID)) {
+                                if (tcpModel.data != null) {
+                                    val channelId = tcpModel.data.toString()
+                                    startBroadcast(channelId)
+                                }
+                            } else {
+                                showMessage("服务未连接或sipId为空")
+                            }
+                        } else if (tcpModel.action == TcpAction.BroadcastAction.STOP) {
+                            stopBroadcast(true)
+                        }
                     } else if (tcpModel.type == TcpType.DEVICE) {
                         //检查APP版本
                         if (tcpModel.getAction() == TcpAction.DeviceAction.APP_UPDATE) {
@@ -1376,6 +1401,36 @@ class CallingbedActivity :BaseActivity<CallingbedActivityPresenter, CallingbedMa
         }
     }
 
+    private fun startBroadcast(channelId: String) {
+        showMsgMain(getString(R.string.broadcast_start))
+        //初始化 engine
+        WebRTCEngine.getInstance().init(true, this)
+        //初始化 janusClient
+        janusClient = JanusClient(JanusConstant.JANUS_URL, Constant.SIP_ID.toBigInteger())
+        //初始化 StreamingCallback
+        val streamingCallback = StreamingCallback(janusClient, Constant.SIP_ID.toBigInteger(), channelId.toBigInteger())
+        janusClient?.setJanusCallback(streamingCallback)
+
+        janusClient?.connect()
+
+        runOnUiThread {
+            ll_broadcast_view.visibility = View.VISIBLE
+        }
+    }
+
+    private fun stopBroadcast(showMsg: Boolean) {
+        if (showMsg) {
+            showMsgMain(getString(R.string.broadcast_stop))
+        }
+
+        runOnUiThread {
+            ll_broadcast_view.visibility = View.GONE
+        }
+        if (janusClient != null) {
+            janusClient?.destroySession()
+        }
+    }
+
     /**
      * Sip状态灯显示
      *

+ 26 - 0
callingbed/src/main/res/layout/callingbed_main_lay.xml

@@ -37,6 +37,32 @@
             android:layout_toRightOf="@id/rl_left_view"
             android:layout_below="@id/activity_calling_bed_layout_title" />
 
+        <LinearLayout
+            android:id="@+id/ll_broadcast_view"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_toRightOf="@id/rl_left_view"
+            android:layout_alignParentBottom="true"
+            android:layout_marginRight="10dp"
+            android:layout_marginBottom="10dp"
+            android:padding="10dp"
+            android:gravity="center"
+            android:background="@color/trans_bg_color"
+            android:visibility="gone">
+
+            <com.wdkl.ncs.android.lib.widget.MarqueeTextView
+                android:layout_width="160dp"
+                android:layout_height="wrap_content"
+                android:ellipsize="marquee"
+                android:focusable="true"
+                android:focusableInTouchMode="true"
+                android:marqueeRepeatLimit="-1"
+                android:singleLine="true"
+                android:text="正在广播中......"
+                android:textSize="32sp"
+                android:textColor="@color/title_text"/>
+        </LinearLayout>
+
         <!--通话界面-->
         <FrameLayout
             android:id="@+id/call_frame"

+ 3 - 0
callingbed/src/main/res/values/colors.xml

@@ -23,6 +23,9 @@
     <!--红色按钮颜色-->
     <color name="button_color">#f57550</color>
 
+    <color name="trans_bg_color">#44d2d2d2</color>
+    <color name="title_text">#F78B8F</color>
+
     <!-- 黄色颜色 -->
     <color name="yellow_color">#f8c255</color>
     <!-- 红色颜色 -->

+ 141 - 0
janus/src/main/java/com/wdkl/ncs/janus/client/JanusClient.java

@@ -5,6 +5,7 @@ import android.util.Log;
 import com.wdkl.ncs.janus.rtc.WebRTCEngine;
 import com.wdkl.ncs.janus.util.EnumType;
 
+import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.webrtc.IceCandidate;
@@ -631,6 +632,146 @@ public class JanusClient implements WebSocketChannel.WebSocketCallback {
     }
 
 
+    //---------------------------------------------------- 广播用 开始
+    public static final int ERROR_STREAMING_REQUEST = 0x04;
+
+    public void requestList(){
+        String tid = randomString(12);
+        transactions.put(tid, new Transaction(tid) {
+            @Override
+            public void onSuccess(JSONObject msg) throws Exception {
+                JSONObject data = msg.getJSONObject("plugindata").getJSONObject("data");
+                JSONArray array = data.getJSONArray("list");
+                /*
+                audio_age_ms: 236984209
+                description: "Opus live stream coming from external source"
+                enabled: true
+                id: 112         //来自数据库
+                metadata: "12"  //partId,
+                type: "live"
+                 */
+            }
+
+            @Override
+            public void onError() {
+                janusCallback.onError(ERROR_STREAMING_REQUEST, "获取Streaming List失败");
+            }
+        });
+
+        JSONObject message = new JSONObject();
+        JSONObject body = new JSONObject();
+        try {
+            body.putOpt("request", "list");
+            message.put("body", body);
+
+            message.putOpt("janus", "message");
+            message.putOpt("transaction", tid);
+            message.putOpt("session_id", sessionId);
+            message.putOpt("handle_id", currentHandleId);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        sendMessage(message.toString());
+    }
+
+    public void requestInfo(BigInteger channelId){
+        String tid = randomString(12);
+        transactions.put(tid, new Transaction(tid) {
+            @Override
+            public void onSuccess(JSONObject msg) throws Exception {
+                JSONObject data = msg.getJSONObject("plugindata").getJSONObject("data");
+                JSONObject array = data.getJSONObject("info");
+                /*
+                audio: true
+                audio_age_ms: 243502007
+                audioport: 5004
+                audiopt: 111
+                audiortcpport: 0
+                audiortpmap: "opus/48000/2"
+                description: "Opus live stream coming from external source"
+                enabled: true
+                id: 112
+                metadata: "测试一下测试一下"
+                name: "rtp-audio"
+                type: "live"
+                viewers: 1
+                 */
+            }
+
+            @Override
+            public void onError() {
+                janusCallback.onError(ERROR_STREAMING_REQUEST, "获取Streaming Info失败");
+            }
+        });
+
+        JSONObject message = new JSONObject();
+        JSONObject body = new JSONObject();
+        try {
+            body.putOpt("request", "info");
+            body.putOpt("id", channelId);
+            message.put("body", body);
+
+            message.putOpt("janus", "message");
+            message.putOpt("transaction", tid);
+            message.putOpt("session_id", sessionId);
+            message.putOpt("handle_id", currentHandleId);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        sendMessage(message.toString());
+    }
+
+    public void requestWatch(BigInteger channelId){
+        String tid = randomString(12);
+        JSONObject message = new JSONObject();
+        JSONObject body = new JSONObject();
+        try {
+            body.putOpt("request", "watch");
+            body.putOpt("id", channelId);
+            message.put("body", body);
+
+            message.putOpt("janus", "message");
+            message.putOpt("transaction", tid);
+            message.putOpt("session_id", sessionId);
+            message.putOpt("handle_id", currentHandleId);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        sendMessage(message.toString());
+    }
+
+    public void requestStart(BigInteger channelId, SessionDescription sdp){
+        String tid = randomString(12);
+
+        JSONObject message = new JSONObject();
+        JSONObject body = new JSONObject();
+        try {
+            body.putOpt("request", "start");
+            body.putOpt("id", channelId);
+            message.put("body", body);
+
+            message.putOpt("janus", "message");
+            message.putOpt("transaction", tid);
+            message.putOpt("session_id", sessionId);
+            message.putOpt("handle_id", currentHandleId);
+
+            if (sdp != null) {
+                JSONObject jsep = new JSONObject();
+                jsep.putOpt("type", sdp.type);
+                jsep.putOpt("sdp", sdp.description);
+                message.putOpt("jsep", jsep);
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        sendMessage(message.toString());
+    }
+    //---------------------------------------------------- 广播用 结束
+
     private synchronized void sendMessage(String message){
         if(webSocketChannel!=null){
             webSocketChannel.sendMessage(message);

+ 197 - 0
janus/src/main/java/com/wdkl/ncs/janus/client/StreamingCallback.java

@@ -0,0 +1,197 @@
+package com.wdkl.ncs.janus.client;
+
+import android.util.Log;
+
+import com.wdkl.ncs.janus.rtc.Peer;
+import com.wdkl.ncs.janus.rtc.WebRTCEngine;
+import com.wdkl.ncs.janus.rtc.observer.CreateAnswerCallback;
+import com.wdkl.ncs.janus.rtc.observer.CreatePeerConnectionCallback;
+import com.wdkl.ncs.janus.util.EnumType;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.webrtc.IceCandidate;
+import org.webrtc.MediaStream;
+import org.webrtc.SdpObserver;
+import org.webrtc.SessionDescription;
+
+import java.math.BigInteger;
+
+public class StreamingCallback implements JanusClient.JanusCallback {
+
+    private final static String TAG = StreamingCallback.class.getSimpleName();
+
+    private JanusClient janusClient;
+    private Peer peer;
+    private BigInteger userId;
+    private BigInteger currentHandleId;
+    private BigInteger channelId;
+
+    public StreamingCallback(JanusClient janusClient, BigInteger userId, BigInteger channelId){
+        this.janusClient = janusClient;
+        this.userId = userId;
+        this.channelId = channelId;
+    }
+
+    @Override
+    public void onCreateSession(BigInteger sessionId) {
+        janusClient.attachPlugin("janus.plugin.streaming");
+    }
+
+    @Override
+    public void onCreateRoom(BigInteger handleId) {
+
+    }
+
+    @Override
+    public void onAttached(BigInteger handleId) {
+        Log.d(TAG, "onAttached");
+        this.currentHandleId = handleId;
+        janusClient.requestWatch(channelId);
+        janusClient.requestInfo(channelId);
+    }
+
+    @Override
+    public void onSubscribeAttached(BigInteger subscribeHandleId, BigInteger feedId) {
+
+    }
+
+    @Override
+    public void onDetached(BigInteger handleId) {
+
+    }
+
+    @Override
+    public void onHangup(BigInteger handleId) {
+
+    }
+
+    //接收 JanusClient 810 行处理
+    @Override
+    public void onMessage(BigInteger sender, BigInteger handleId, JSONObject data, JSONObject jsep) {
+        if (!data.has("streaming")) {
+            return;
+        }
+
+        try {
+            String type = data.getString("streaming");
+            switch (type){
+                case "event":
+                    String status = data.getJSONObject("result").getString("status");
+                    //频道准备中
+                    if (status.equals("preparing")){
+                        // sdp 协商成功,收到网关转发来的sdp offer,开始接收
+                        String sdp = jsep.getString("sdp");
+                        //创建本地PeerConnection
+                        WebRTCEngine.getInstance().createLocalPeer(userId, peerConnectionCallback);
+                        this.peer = WebRTCEngine.getInstance().getPeer(userId);
+
+                        this.peer.getPeerConnection().setRemoteDescription(new SdpObserver() {
+                            @Override
+                            public void onCreateSuccess(SessionDescription sdp) {
+                                Log.d(TAG, "setRemoteDescription onCreateSuccess");
+                            }
+
+                            @Override
+                            public void onSetSuccess() {
+                                Log.d(TAG, "setRemoteDescription onSetSuccess");
+                                // 回复网关一个 start ,附带自己的 sdp answer
+                                WebRTCEngine.getInstance().getPeer(userId).createAnswer(new CreateAnswerCallback() {
+                                    @Override
+                                    public void onSetAnswerSuccess(SessionDescription sdp) {
+                                        janusClient.requestStart(channelId, sdp);
+                                    }
+
+                                    @Override
+                                    public void onSetAnswerFailed(String error) {
+
+                                    }
+                                });
+                            }
+
+                            @Override
+                            public void onCreateFailure(String error) {
+                                Log.d(TAG, "setRemoteDescription onCreateFailure " + error);
+                            }
+
+                            @Override
+                            public void onSetFailure(String error) {
+                                Log.d(TAG, "setRemoteDescription onSetFailure " + error);
+                            }
+                        }, new SessionDescription(SessionDescription.Type.OFFER, sdp));
+                    } else if (status.equals("starting")){
+
+                    } else if (status.equals("started")){
+
+                    }
+                    break;
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void onIceCandidate(BigInteger handleId, JSONObject candidate) {
+
+    }
+
+    @Override
+    public void onDestroySession(BigInteger sessionId) {
+        this.currentHandleId = null;
+        this.channelId = null;
+        janusClient.disConnect();
+    }
+
+    @Override
+    public void onError(int errorCode, String error) {
+
+    }
+
+    //收集candidate并发送
+    private CreatePeerConnectionCallback peerConnectionCallback = new CreatePeerConnectionCallback() {
+        @Override
+        public void onIceGatheringComplete() {
+            janusClient.trickleCandidateComplete(currentHandleId);
+        }
+
+        @Override
+        public void onIceCandidate(IceCandidate candidate) {
+            janusClient.trickleCandidate(currentHandleId, candidate);
+        }
+
+        @Override
+        public void onIceCandidatesRemoved(IceCandidate[] candidates) {
+            peer.getPeerConnection().removeIceCandidates(candidates);
+        }
+
+        @Override
+        public void onAddStream(MediaStream stream) {
+            if (stream.videoTracks.size() > 0) {
+                stream.audioTracks.get(0).setEnabled(true);
+                WebRTCEngine.getInstance().getPeer(userId)._remoteStream = stream;
+                janusClient.setCallState(EnumType.CallState.Connected);
+            }
+        }
+
+        @Override
+        public void onRemoveStream(MediaStream stream) {
+            WebRTCEngine.getInstance().getPeer(userId)._remoteStream = null;
+        }
+
+        @Override
+        public void onIceConnected() {
+
+        }
+
+        @Override
+        public void onIceDisconnected() {
+
+        }
+
+        @Override
+        public void onIceConnectFail() {
+
+        }
+    };
+}

+ 1 - 3
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/channel/DeviceChannel.java

@@ -64,9 +64,6 @@ public class DeviceChannel {
                     EventBus.getDefault().post(new MessageEvent(tcpModel, EVENT_TCP_MSG));
                 }
                 break;
-            case VIDEO:
-                EventBus.getDefault().post(new MessageEvent(tcpModel, EVENT_TCP_MSG));
-                break;
             case IM:
                 if (tcpModel.getAction()==TcpAction.IMAction.MSG){
                     //todo: 从接口重新获取左侧数据;使用通话中界面,显示有语音留言,停留3秒,结束至正常界面
@@ -90,6 +87,7 @@ public class DeviceChannel {
                     EventBus.getDefault().post(new MessageEvent(tcpModel, EVENT_TCP_MSG));
                 }
                 break;
+            case VIDEO:
             case DEVICE:
             case TIME:
             case BROADCAST:

+ 3 - 0
resource/src/main/res/values-es/string.xml

@@ -58,8 +58,11 @@
     <string name="net_error">Error de red</string>
     <string name="no_custom">Error de llamada</string>
     <string name="call_init_error">Error de red o sip, espere</string>
+    <string name="custom_empty">No custom</string>
     <string name="call_fail">Call failed</string>
     <string name="tips_reboot">Reinicie el dispositivo</string>
+    <string name="broadcast_start">Start broadcast</string>
+    <string name="broadcast_stop">Stop broadcast</string>
     <string name="no_response">Sin respuesta</string>
     <string name="call_success">Llamada exitosa</string>
     <string name="call_in_calling">Llamando…</string>

+ 3 - 0
resource/src/main/res/values-zh/strings.xml

@@ -58,8 +58,11 @@
     <string name="net_error">网络异常</string>
     <string name="no_custom">床位未入住或未绑定设备,无法呼叫</string>
     <string name="call_init_error">网络异常或sipId异常,请稍后</string>
+    <string name="custom_empty">床位未入住,无法呼叫</string>
     <string name="call_fail">呼叫失败</string>
     <string name="tips_reboot">请重启设备</string>
+    <string name="broadcast_start">开始广播</string>
+    <string name="broadcast_stop">广播停止</string>
 
     <string name="no_response">无人接听</string>
     <string name="call_success">呼叫成功,等待接听</string>

+ 3 - 0
resource/src/main/res/values/strings.xml

@@ -58,8 +58,11 @@
     <string name="net_error">Network error</string>
     <string name="no_custom">Calling error</string>
     <string name="call_init_error">Network or sip error, please wait</string>
+    <string name="custom_empty">No custom</string>
     <string name="call_fail">Call failed</string>
     <string name="tips_reboot">Please reboot device</string>
+    <string name="broadcast_start">Start broadcast</string>
+    <string name="broadcast_stop">Stop broadcast</string>
 
     <string name="no_response">No response</string>
     <string name="call_success">Call success</string>