I'm trying create a simple peer to peer video call from this resource.
I just did every thing it need but I still got no rendering from remote
its only render from local camera and onRemoteVideoTrackReceive
never called.
I'm using qb 2.5 sdk
also I compiled the sample sample-videochat-webrtc
from qb-sdk
its works just fine .
here is my project code :
mudol:app
apply plugin: 'com.android.application'
android {
compileSdkVersion 'Google Inc.:Google APIs:23'
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "com.hm.runanddelete"
minSdkVersion 14
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard- android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
testCompile 'junit:junit:4.12'
compile fileTree(dir: 'libs', include: ['*.jar'])
}
Project:____
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.0.0-alpha5'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hm.runanddelete">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera.front" />
<uses-feature android:name="android.hardware.microphone"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature
android:glEsVersion="0x00020000"
android:required="true" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
MainActivity
public class MainActivity extends Activity implements QBRTCClientSessionCallbacks, QBRTCClientVideoTracksCallbacks, QBRTCSignalingCallback, QBRTCSessionConnectionCallbacks {
static final String APP_ID = "...";
static final String AUTH_KEY = "...";
static final String AUTH_SECRET = "...";
static final String ACCOUNT_KEY = "...";
RTCGLVideoView LocalVideoView;
RTCGLVideoView remoteVideoView;
String login = "";
String password = "";
QBChatService chatService;
QBRTCClient rtcClient;
EditText et_user, et_paw, et_id;
Button btn_call, btn_login;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_user = (EditText) findViewById(R.id.et_caller_user);
et_paw = (EditText) findViewById(R.id.et_caller_pw);
et_id = (EditText) findViewById(R.id.et_caller_tid);
btn_call = (Button) findViewById(R.id.btn_caller_call);
btn_login = (Button) findViewById(R.id.btn_caller_login);
LocalVideoView = (RTCGLVideoView) findViewById(R.id.localView);
remoteVideoView = (RTCGLVideoView) findViewById(R.id.opponentView);
btn_login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
login = et_user.getText().toString();
password = et_paw.getText().toString();
initializingQB();
}
});
btn_call.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
starcall(Integer.parseInt(et_id.getText().toString()));
}
});
}
private void initializingQB() {
QBSettings.getInstance().init(getApplicationContext(), APP_ID, AUTH_KEY, AUTH_SECRET);
QBSettings.getInstance().setAccountKey(ACCOUNT_KEY);
final QBUser user = new QBUser(login, password);
// CREATE SESSION WITH USER
// If you use create session with user data,
// then the user will be logged in automatically
QBAuth.createSession(login, password, new QBEntityCallback<QBSession>() {
@Override
public void onSuccess(QBSession session, Bundle bundle) {
user.setId(session.getUserId());
Toast.makeText(MainActivity.this, "Loging in!!!", Toast.LENGTH_LONG).show();
Log.w("qb", "logedin app");
// INIT CHAT SERVICE
chatService = QBChatService.getInstance();
Log.w("qb", "loging chat");
// LOG IN CHAT SERVICE
chatService.login(user, new QBEntityCallback<QBUser>() {
@Override
public void onSuccess(QBUser qbUser, Bundle bundle) {
Log.w("qb", "loged chat");
initQBRTCClient();
}
@Override
public void onError(QBResponseException errors) {
Log.w("qb", "not loged\n" + errors.getMessage());
//error
}
});
}
@Override
public void onError(QBResponseException errors) {
//error
Toast.makeText(MainActivity.this, "ERROR!!!\n" + errors.getMessage(), Toast.LENGTH_LONG).show();
}
});
}
private void initQBRTCClient() {
rtcClient = QBRTCClient.getInstance(this);
// Add signalling manager
QBChatService.getInstance().getVideoChatWebRTCSignalingManager().addSignalingManagerListener(new QBVideoChatSignalingManagerListener() {
@Override
public void signalingCreated(QBSignaling qbSignaling, boolean createdLocally) {
if (!createdLocally) {
rtcClient.addSignaling((QBWebRTCSignaling) qbSignaling);
}
}
});
rtcClient.setCameraErrorHendler(new VideoCapturerAndroid.CameraErrorHandler() {
@Override
public void onCameraError(final String s) {
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, s, Toast.LENGTH_LONG).show();
}
});
}
});
// Configure
//
QBRTCConfig.setMaxOpponentsCount(2);
QBRTCConfig.setDisconnectTime(30);
QBRTCConfig.setAnswerTimeInterval(301);
QBRTCConfig.setDebugEnabled(true);
//rtcClient.addVideoTrackCallbacksListener(this);
// rtcSession.removeVideoTrackCallbacksListener(this);
// Add activity as callback to RTCClient
rtcClient.addSessionCallbacksListener(this);
//rtcClient.addVideoTrackCallbacksListener(this);
//QBRTCClient.getInstance(this).removeSessionCallbacksListener(this);
// Start mange QBRTCSessions according to VideoCall parser's callbacks
rtcClient.prepareToProcessCalls();
}
private void starcall(int tid) {
//Set conference type
//There are two types of calls:
// - QB_CONFERENCE_TYPE_VIDEO - for video call;
// - QB_CONFERENCE_TYPE_AUDIO - for audio call;
QBRTCTypes.QBConferenceType qbConferenceType = QBRTCTypes.QBConferenceType.QB_CONFERENCE_TYPE_VIDEO;
//Initiate opponents list
List<Integer> opponents = new ArrayList<Integer>();
opponents.add(tid); //12345 - QBUser ID
//Set user information
// User can set any string key and value in user info
// Then retrieve this data from sessions which is returned in callbacks
// and parse them as he wish
Map<String, String> userInfo = new HashMap<>();
userInfo.put("key", "value");
//Init session
QBRTCSession session = rtcClient.createNewSessionWithOpponents(opponents, qbConferenceType);
session.addVideoTrackCallbacksListener(this);
session.addSessionCallbacksListener(this);
session.addSessionCallbacksListener(this);
//Start call
session.startCall(userInfo);
Log.w("qb", "startcall: " + String.valueOf(tid));
}
@Override
public void onReceiveNewSession(QBRTCSession qbrtcSession) {
// obtain received user info
Map<String, String> userInfo = qbrtcSession.getUserInfo();
// .....
// ..... your code
// .....
// Set userInfo
// User can set any string key and value in user info
userInfo = new HashMap<String, String>();
userInfo.put("Key", "Value");
// Accept incoming call
qbrtcSession.addVideoTrackCallbacksListener(this);
qbrtcSession.addSessionCallbacksListener(this);
qbrtcSession.acceptCall(userInfo);
Log.w("call", "accepted:" + String.valueOf(qbrtcSession.getSessionID()));
}
@Override
public void onUserNotAnswer(QBRTCSession qbrtcSession, Integer integer) {
}
@Override
public void onCallRejectByUser(QBRTCSession qbrtcSession, Integer integer, Map<String, String> map) {
}
@Override
public void onCallAcceptByUser(QBRTCSession qbrtcSession, Integer integer, Map<String, String> map) {
}
@Override
public void onReceiveHangUpFromUser(QBRTCSession qbrtcSession, Integer integer) {
}
@Override
public void onUserNoActions(QBRTCSession qbrtcSession, Integer integer) {
}
@Override
public void onSessionClosed(QBRTCSession qbrtcSession) {
}
@Override
public void onSessionStartClose(QBRTCSession qbrtcSession) {
}
@Override
public void onLocalVideoTrackReceive(QBRTCSession qbrtcSession, QBRTCVideoTrack qbrtcVideoTrack) {
Log.w("TAG", "onLocalVideoTrackReceive()");
// RTCGLVideoView videoView, QBRTCVideoTrack videoTrack, boolean remoteRenderer
fillVideoView(LocalVideoView, qbrtcVideoTrack, false);
}
@Override
public void onRemoteVideoTrackReceive(QBRTCSession qbrtcSession, QBRTCVideoTrack qbrtcVideoTrack, Integer integer) {
Log.w("TAG", "onRemoteVideoTrackReceive(),,,");
fillVideoView(remoteVideoView, qbrtcVideoTrack, true);
}
private void fillVideoView(RTCGLVideoView videoView, QBRTCVideoTrack videoTrack, boolean remoteRenderer) {
videoTrack.addRenderer(new VideoRenderer(remoteRenderer ?
videoView.obtainVideoRenderer(RTCGLVideoView.RendererSurface.MAIN) :
videoView.obtainVideoRenderer(RTCGLVideoView.RendererSurface.SECOND)));
}
@Override
public void onSuccessSendingPacket(QBSignalingSpec.QBSignalCMD qbSignalCMD, Integer integer) {
}
@Override
public void onErrorSendingPacket(QBSignalingSpec.QBSignalCMD qbSignalCMD, Integer integer, QBRTCSignalException e) {
}
@Override
public void onStartConnectToUser(QBRTCSession qbrtcSession, Integer integer) {
Log.w("connection", "onStartConnectToUser");
}
@Override
public void onConnectedToUser(QBRTCSession qbrtcSession, Integer integer) {
Log.w("connection", "onConnectedToUser");
}
@Override
public void onConnectionClosedForUser(QBRTCSession qbrtcSession, Integer integer) {
Log.w("connection", "onConnectionClosedForUser");
}
@Override
public void onDisconnectedFromUser(QBRTCSession qbrtcSession, Integer integer) {
Log.w("connection", "onDisconnectedFromUser");
}
@Override
public void onDisconnectedTimeoutFromUser(QBRTCSession qbrtcSession, Integer integer) {
Log.w("connection", "onDisconnectedTimeoutFromUser");
}
@Override
public void onConnectionFailedWithUser(QBRTCSession qbrtcSession, Integer integer) {
Log.w("connection", "onConnectionFailedWithUser");
}
@Override
public void onError(QBRTCSession qbrtcSession, QBRTCException e) {
Log.w("connection", "onError");
}
}
layout
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.hm.runanddelete.MainActivity">
<com.quickblox.videochat.webrtc.view.RTCGLVideoView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/localView"
android:layout_width="150dp"
android:layout_height="150dp"
/>
<com.quickblox.videochat.webrtc.view.RTCGLVideoView
android:id="@+id/opponentView"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentBottom="true"
android:layout_alignRight="@+id/btn_caller_login"
android:layout_alignEnd="@+id/btn_caller_login" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="LOGIN"
android:id="@+id/btn_caller_login"
android:layout_below="@+id/et_caller_pw"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/et_caller_user"
android:hint="QB USERNAME"
android:layout_below="@+id/localView"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="25dp"
android:layout_alignRight="@+id/et_caller_pw"
android:layout_alignEnd="@+id/et_caller_pw"
android:text="" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/et_caller_pw"
android:layout_below="@+id/et_caller_user"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignRight="@+id/btn_caller_login"
android:layout_alignEnd="@+id/btn_caller_login"
android:hint="QB PASSWORD"
android:text="" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/et_caller_tid"
android:hint="QB TARGET ID"
android:layout_below="@+id/et_caller_pw"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_toLeftOf="@+id/btn_caller_login"
android:layout_toStartOf="@+id/btn_caller_login"
android:text="" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="MAKE CALL"
android:id="@+id/btn_caller_call"
android:layout_below="@+id/et_caller_tid"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignRight="@+id/btn_caller_login"
android:layout_alignEnd="@+id/btn_caller_login" />
</RelativeLayout>
correct me if i am wrong !
UPDATE:
added addSessionCallbacksListener
log after recive call:
02-23 22:23:09.731 18666-18666/com.hm.runanddelete W/qb: logedin app
02-23 22:23:09.787 18666-18666/com.hm.runanddelete W/qb: loging chat
02-23 22:23:13.051 18666-18958/com.hm.runanddelete W/qb: loged chat
02-23 22:24:10.454 18666-19016/com.hm.runanddelete W/call: accepted:835226fc-d9a5-4bbc-82db-3ad6e0524168
02-23 22:24:11.230 18666-19016/com.hm.runanddelete W/TAG: onLocalVideoTrackReceive()
making call:
02-23 22:25:54.104 20763-20763/com.hm.runanddelete W/qb: logedin app
02-23 22:25:54.163 20763-20763/com.hm.runanddelete W/qb: loging chat
02-23 22:25:59.801 20763-21845/com.hm.runanddelete W/qb: loged chat
02-23 22:26:02.550 20763-20763/com.hm.runanddelete W/qb: startcall: 5510686
02-23 22:26:03.412 20763-21968/com.hm.runanddelete W/TAG: onLocalVideoTrackReceive()
I believe the problem you are facing is that after Call is accepted, the call initiator doesn't receive the accept call back message which fires the "onCallAcceptByUser" then starts rendering the remote video.
All your setup appears to be correct, the change needed to make it work, is that you should implement "QBRTCSessionConnectionCallbacks, QBRTCClientVideoTracksCallbacks" on different Activity or Fragment.
So the MainActivity will implement QBRTCClientSessionCallbacks, QBRTCSessionConnectionCallbacks" and the other Activity or Fragment Let's call it the ConversationFragment" will implement "QBRTCSessionConnectionCallbacks, QBRTCClientVideoTracksCallbacks", in that case the session instance you create in the call initiator logic or the session you receive _in Call Receiver logic will be created in MainActivity and make it accessible to the ConversationFragment to initialise "QBRTCSessionConnectionCallbacks, QBRTCClientVideoTracksCallbacks".
I didn't have time to investigate the reason of this issue which is "Call accept message is not received if all CallBack Listeners are implemented in the same Activity" and how separating them as explained above fixes the issue. But this is how i fixed it.
Note: I faced this issue on both versions 2.5 & 2.5.1. The fix works with both.
Let me know if you needed further help.
Small Updated Note: I faced similar issue on iOS. Creating QBSession object in a view controller and passing it to another view controller prevents "ConnectedToUser" call back method from being called which prevents the call from being completely established. To fix it, just create the QBSession or lets say call "createNewSession" in the same ViewController that will handle the call.