androidquickbloxquickblox-android

QuickBlox onRemoteVideoTrackReceive never called


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()

Solution

  • 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.