在glSurfaceView上播放视频流

时间:2016-12-12 11:00:17

标签: android video-streaming client-server webrtc

我正在为Kurento Node.js服务器开发一个Android客户端。我要做的是在<org.webrtc.SurfaceViewRenderer>上播放远程和本地流 问题是我的代码不适用于所有类型的设备。在三星S7上运行正常,但在华为P9 Lites上它只显示黑屏。

 package ro.hpm.hypertalk;

    import android.graphics.PixelFormat;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.util.Log;
    import android.view.WindowManager;

    import com.google.gson.Gson;
    import com.google.gson.JsonObject;

    import org.json.JSONException;
    import org.json.JSONObject;
    import org.webrtc.DataChannel;
    import org.webrtc.EglBase;
    import org.webrtc.IceCandidate;
    import org.webrtc.MediaStream;
    import org.webrtc.PeerConnection;
    import org.webrtc.RendererCommon;
    import org.webrtc.SessionDescription;
    import org.webrtc.SurfaceViewRenderer;

    import fi.vtt.nubomedia.webrtcpeerandroid.NBMMediaConfiguration;
    import fi.vtt.nubomedia.webrtcpeerandroid.NBMPeerConnection;
    import fi.vtt.nubomedia.webrtcpeerandroid.NBMWebRTCPeer;
    import io.socket.client.Socket;
    import io.socket.emitter.Emitter;

    public class VideoPeer extends AppCompatActivity implements NBMWebRTCPeer.Observer {


        private Socket mSocket;
        public static String userType ="viewer";
        JSONObject obj = new JSONObject();
        private final String TAG = "VideoPeer";
        private SurfaceViewRenderer remoteRenderer;
        private SurfaceViewRenderer localRenderer;
        private enum CallState { IDLE, PUBLISHING, PUBLISHED, WAITING_REMOTE_USER, RECEIVING_REMOTE_USER };
        private User user = new User();
        private String mData;
        private CallState callState;
        private NBMMediaConfiguration peerConnectionParameters;
        private NBMWebRTCPeer nbmWebRTCPeer;


        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_video_peer);
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
            remoteRenderer = (SurfaceViewRenderer) findViewById(R.id.remote_renderer);
            remoteRenderer.init(EglBase.create().getEglBaseContext(), null);
            localRenderer = (SurfaceViewRenderer) findViewById(R.id.local_renderer);
            localRenderer.init(EglBase.create().getEglBaseContext(), null);
            callState = CallState.IDLE;

        }

        @Override
        protected void onStart() {
            super.onStart();

            remoteRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
            localRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);


            try {
                SocketHandler.emitMessage("create or join");
            } catch (Exception e) {
                Log.e(TAG, e.getMessage());
            }

            SocketHandler.getmSocket().on("created", new Emitter.Listener() {
                @Override
                public void call(Object... args) {
                    Log.i(TAG, "message back:received ");
                    userType = "presenter";
                    presenter();
                }
            });
            SocketHandler.getmSocket().on("joined", new Emitter.Listener() {
                @Override
                public void call(Object... args) {
                    System.out.println("I have joined the room:" + user.getRoom());
                    viewer();
                }
            });
    }


        public void presenter(){

            constrains();
            nbmWebRTCPeer = new NBMWebRTCPeer(peerConnectionParameters, this, localRenderer, this);
            nbmWebRTCPeer.registerMasterRenderer(localRenderer);
            nbmWebRTCPeer.initialize();
            callState = CallState.PUBLISHING;

        }

        public void viewer(){

            constrains();
            nbmWebRTCPeer = new NBMWebRTCPeer(peerConnectionParameters, this, localRenderer, this);
            nbmWebRTCPeer.registerMasterRenderer(localRenderer);
            nbmWebRTCPeer.initialize();
            callState = CallState.WAITING_REMOTE_USER;
        }


        public void constrains(){
            peerConnectionParameters = new NBMMediaConfiguration(
                    NBMMediaConfiguration.NBMRendererType.OPENGLES,
                    NBMMediaConfiguration.NBMAudioCodec.OPUS, 0,
                    NBMMediaConfiguration.NBMVideoCodec.VP8, 0,
                    new NBMMediaConfiguration.NBMVideoFormat(352, 288, PixelFormat.RGB_888, 20),
                    NBMMediaConfiguration.NBMCameraPosition.FRONT);
        }


        @Override
        protected void onDestroy(){
            super.onDestroy();

            SocketHandler.getmSocket().disconnect();
            SocketHandler.getmSocket().off("created", new Emitter.Listener() {
                @Override
                public void call(final Object... args) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Log.i(TAG, "Message received");
                            JSONObject data = (JSONObject) args[0];
                            Log.i(TAG, data.toString());
                            Log.i(TAG, new Gson().toJson(data));
                        }
                    });
                }
            });
        }


        @Override
        public void onInitialize() {
           if(userType.equals("presenter")) {
            nbmWebRTCPeer.generateOffer("local", true);
            }
           else{
                nbmWebRTCPeer.generateOffer("MyRemotePeer", false);
           }
        }



        @Override
        public void onLocalSdpOfferGenerated(final SessionDescription localSdpOffer, NBMPeerConnection connection) {

            Log.i(TAG,"Entered here");


            Log.i(TAG, "sending message");
            if(userType.equals("presenter")) {

                    Log.i(TAG, "Socket Check "+ user.toString());
                SocketHandler.emitMessage("startPresenter");
                Log.i("socket check sdpOffer", SocketHandler.getmSocket().toString());
            } else{
                final JSONObject sdpObjViewer = new JSONObject();
                Constants.userParameters.setUserType("viewer");
                Constants.userParameters.setSdpOffer(localSdpOffer.description);
                SocketHandler.emitMessage("startViewer");
  Constants.userParameters);

            }

            Log.i(TAG, "socket check On create "+ SocketHandler.getmSocket().toString());

            SocketHandler.getmSocket().on("sdpAnswerFromServer-mobile", new Emitter.Listener() {
                @Override
                public void call(final Object... args) {
                    Log.i(TAG, "onSdpAnswerFromServer listener " + args[0]);
                    JSONObject obj = null;
                    try {
                        Log.i(TAG, "onSdpAnswerFromServer listener " + args[0].toString());
                        JSONObject json = new JSONObject(args[0].toString());
                        String data = json.getString("sdpAnswer");
                        Constants.userParameters.setSdpAnswer(data);
                        SessionDescription sd = new SessionDescription(SessionDescription.Type.ANSWER,
                                Constants.userParameters.getSdpAnswer());
                        if (userType.equals("presenter")) {
                            nbmWebRTCPeer.processAnswer(sd, "local");
                        } else {
                            nbmWebRTCPeer.processAnswer(sd, "MyRemotePeer");
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        Log.e(TAG, e.getMessage());
                    }
                }
            });


        }

        private User setUserFromResponse(JSONObject obj){
            Gson gson = new Gson();
            com.google.gson.JsonParser jsonParser = new com.google.gson.JsonParser();
            JsonObject gsonObject = (JsonObject)jsonParser.parse(obj.toString());
            user = gson.fromJson(gsonObject, User.class);
            Log.i(TAG, "User: " + user.toString());
            Log.i(TAG, "SDPAnswer: " + user.getSdpAnswer());
            Log.i(TAG, "SDPOffer: " + user.getSdpOffer());
            Log.i(TAG, "Response: " + user.getResponse());

            return user;
        }



        @Override
        public void onLocalSdpAnswerGenerated(SessionDescription localSdpAnswer, NBMPeerConnection connection) {

        }

        @Override
        public void onIceCandidate(IceCandidate localIceCandidate, NBMPeerConnection connection) {


            JSONObject cand = new JSONObject();
            try {
                cand.put("candidate", localIceCandidate.sdp);
                cand.put("sdpMid",localIceCandidate.sdpMid);
                cand.put("sdpMLineIndex",Integer.toString(localIceCandidate.sdpMLineIndex));
            } catch (JSONException e) {
                e.printStackTrace();
            }

            try {
                obj.put("id","onIceCandidate");
                obj.put("candidate",cand);

            } catch (JSONException e) {
                e.printStackTrace();
            }
            Log.i(TAG, "socket check onIce "+ SocketHandler.getmSocket().toString());
            if(SocketHandler.getmSocket().connected()) {
                SocketHandler.getmSocket().emit("OnIceCandidate", obj);
                Log.i(TAG, "Emit IceCandidate succesfull!" + "\n" + localIceCandidate.toString());
                Log.i(TAG, "Emit IceCandidate succesfull!" + "\n" + obj);
            } else {
                Log.i(TAG, "Socket not connected!");
                SocketHandler.getmSocket().connect();
                SocketHandler.getmSocket().emit("OnIceCandidate", obj);
            }

            SocketHandler.getmSocket().on("iceCandidate-mobile", new Emitter.Listener() {
                @Override
                public void call(final Object... args) {
                    Log.i(TAG, "onIceCandidate listener " + args[0]);
                    JSONObject obj = null;
                    try {
                        Log.i(TAG, "onIceCandidate listener " + args[0].toString());
                        JSONObject json = new JSONObject(args[0].toString());
                        String candidate = json.getJSONObject("candidate").getString("candidate");
                        String sdpMid  = json.getJSONObject("candidate").getString("sdpMid");
                        int sdpMLineIndex = Integer.valueOf(json.getJSONObject("candidate").getString("sdpMLineIndex"));
                        IceCandidate ic = new IceCandidate(sdpMid, sdpMLineIndex,candidate);

                        nbmWebRTCPeer.addRemoteIceCandidate(ic, "local");
                   } catch (Exception e) {
                        e.printStackTrace();
                        Log.e(TAG, e.getMessage());
                    }
                }
            });




        }

        @Override
        public void onIceStatusChanged(PeerConnection.IceConnectionState state, NBMPeerConnection connection) {

        }

        @Override
        public void onRemoteStreamAdded(MediaStream stream, NBMPeerConnection connection) {

            nbmWebRTCPeer.attachRendererToRemoteStream(remoteRenderer, stream);

        }

        @Override
        public void onRemoteStreamRemoved(MediaStream stream, NBMPeerConnection connection) {

        }

        @Override
        public void onPeerConnectionError(String error) {

        }

        @Override
        public void onDataChannel(DataChannel dataChannel, NBMPeerConnection connection) {

        }

        @Override
        public void onBufferedAmountChange(long l, NBMPeerConnection connection, DataChannel channel) {

        }

        @Override
        public void onStateChange(NBMPeerConnection connection, DataChannel channel) {

        }

        @Override
        public void onMessage(DataChannel.Buffer buffer, NBMPeerConnection connection, DataChannel channel) {

        }

        private Emitter.Listener onCreateMessage = new Emitter.Listener() {
            @Override
            public void call(final Object... args) {
                Log.i(TAG, "Inside call");
                System.out.println("Room was created by:"+ user.getName());
                presenter();
                runOnUiThread(new Runnable() {

                    @Override
                    public void run() {
                        if (!SocketHandler.getmSocket().connected()) {
                            SocketHandler.getmSocket().connect();
                        }
                        Log.i(TAG, "Message received");
                        String data = (String) args[0];
                        mData = data;
                        Log.i(TAG, data);
                        Log.i(TAG, new Gson().toJson(data));
                    }
                });
            }
        };

`

活动布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_video_peer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="ro.hpm.hypertalk.VideoPeer">

    <LinearLayout
        android:id="@+id/footer"
        android:background="@color/toolbar"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        android:weightSum="3"
        android:orientation="horizontal">
        <ImageButton
            android:src="@drawable/video_call_btn1"
            android:scaleType="centerInside"
            android:layout_gravity="center"
            android:id="@+id/video_call"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"/>
        <ImageButton
            android:id="@+id/chat"
            android:layout_gravity="center"
            android:scaleType="centerInside"
            android:src="@drawable/chat_btn1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1" />
        <ImageButton
            android:id="@+id/contacts"
            android:layout_gravity="center"
            android:scaleType="centerInside"
            android:src="@drawable/contact_list_btn1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"/>
    </LinearLayout>


    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_above="@id/footer"
        android:layout_height="match_parent">


        <org.webrtc.SurfaceViewRenderer
            android:id="@+id/remote_renderer"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_alignParentTop="true"
            android:layout_alignParentStart="true" />

        <org.webrtc.SurfaceViewRenderer
            android:id="@+id/local_renderer"
            android:layout_width="100dp"
            android:layout_height="150dp"
            android:layout_alignParentStart="false"
            android:layout_alignParentEnd="false"
            android:layout_alignBottom="@id/remote_renderer"
            android:layout_alignRight="@id/remote_renderer"/>

        <android.support.v4.view.ViewPager
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            android:layout_alignParentBottom="true"/>
    </RelativeLayout>



</RelativeLayout>

我无法弄清楚为什么在某些设备上代码可以正常工作,而在其他设备上却没有。我在清单文件中使用了以下权限:

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
    <uses-permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

这是Android Monitor显示的内容:

12-12 12:11:46.261 10881-12624/? W/VideoCapabilities: Unsupported profile 64 for video/avc
12-12 12:11:46.263 10881-12624/? I/VideoCapabilities: Unsupported profile 16384 for video/mp4v-es
12-12 12:11:46.263 10881-12624/? I/VideoCapabilities: Unsupported profile 16384 for video/mp4v-es
12-12 12:11:46.269 10881-12624/? W/VideoCapabilities: Unsupported mime video/x-pn-realvideo
12-12 12:11:46.271 10881-12624/? W/VideoCapabilities: Unsupported mime video/mpeg
12-12 12:11:46.273 10881-12624/? W/VideoCapabilities: Unrecognized profile/level 0/0 for video/mpeg2
12-12 12:11:46.273 10881-12624/? W/VideoCapabilities: Unrecognized profile/level 0/2 for video/mpeg2
12-12 12:11:46.273 10881-12624/? W/VideoCapabilities: Unrecognized profile/level 0/3 for video/mpeg2
12-12 12:11:46.275 10881-12624/? W/VideoCapabilities: Unrecognized profile/level 32768/2 for video/mp4v-es
12-12 12:11:46.281 10881-12624/? W/VideoCapabilities: Unsupported mime video/vc1
12-12 12:11:46.287 10881-12624/? W/VideoCapabilities: Unsupported mime video/x-flv
12-12 12:11:46.291 10881-12624/? W/VideoCapabilities: Unrecognized profile/level 0/0 for video/mpeg2
12-12 12:11:46.291 10881-12624/? W/VideoCapabilities: Unrecognized profile/level 0/2 for video/mpeg2
12-12 12:11:46.291 10881-12624/? W/VideoCapabilities: Unrecognized profile/level 0/3 for video/mpeg2
12-12 12:11:46.307 10881-12624/? I/VideoCapabilities: Unsupported profile 4 for video/mp4v-es
12-12 12:11:46.312 10881-12624/? I/org.webrtc.Logging: MediaCodecVideoDecoder: Trying to find HW decoder for mime video/x-vnd.on2.vp8
12-12 12:11:46.312 10881-12624/? I/org.webrtc.Logging: MediaCodecVideoDecoder: Found candidate decoder OMX.IMG.MSVDX.Decoder.VP8
12-12 12:11:46.312 10881-12624/? I/org.webrtc.Logging: MediaCodecVideoDecoder: Found candidate decoder OMX.google.vp8.decoder
12-12 12:11:46.312 10881-12624/? I/org.webrtc.Logging: MediaCodecVideoDecoder: No HW decoder found for mime video/x-vnd.on2.vp8
12-12 12:11:46.312 10881-12624/? I/org.webrtc.Logging: MediaCodecVideoDecoder: Trying to find HW decoder for mime video/x-vnd.on2.vp9
12-12 12:11:46.312 10881-12624/? I/org.webrtc.Logging: MediaCodecVideoDecoder: Found candidate decoder OMX.google.vp9.decoder
12-12 12:11:46.312 10881-12624/? I/org.webrtc.Logging: MediaCodecVideoDecoder: No HW decoder found for mime video/x-vnd.on2.vp9
12-12 12:11:46.312 10881-12624/? I/org.webrtc.Logging: MediaCodecVideoDecoder: Trying to find HW decoder for mime video/avc
12-12 12:11:46.312 10881-12624/? I/org.webrtc.Logging: MediaCodecVideoDecoder: Found candidate decoder OMX.IMG.MSVDX.Decoder.AVC
12-12 12:11:46.312 10881-12624/? I/org.webrtc.Logging: MediaCodecVideoDecoder: Found candidate decoder OMX.google.h264.decoder
12-12 12:11:46.312 10881-12624/? I/org.webrtc.Logging: MediaCodecVideoDecoder:

2 个答案:

答案 0 :(得分:0)

在评论中收集了有关问题的更多细节后,我注意到MediaCodecVideoDecoder找不到硬件解码器来解码华为P9 Lite上的VP8和VP9视频编解码器。这是有道理的,因为这款手机不支持这些编解码器。

您应该尝试通过将第四个参数设置为false来禁用PeerConnectionFactory.initializeAndroidGlobals调用中的硬件加速。

答案 1 :(得分:0)

如果出现此问题,请检查测试仪支持的相机格式。 libjingle_peerconnection.jar下的CameraEnumerationAndroid类提供了一个API来获取相机格式,然后比较您设置的相机参数,宽度和高度相同,并且frameRate小于系统支持。示例:new NBMVideoFormat(640,480,pixelform.RGB_888,30);