我正在为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:
答案 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);