Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Screen Sharing is not working #195

Open
gurjeet734 opened this issue Aug 18, 2021 · 7 comments
Open

Screen Sharing is not working #195

gurjeet734 opened this issue Aug 18, 2021 · 7 comments

Comments

@gurjeet734
Copy link

When the Users are connected into the Channel the one user from them is not able to share the Screen. When the Share Screen Button is clicked then nothing is happening. I am using the API Example to Run Screen Sharing

@nitishat
Copy link

same issue

@fpillet
Copy link

fpillet commented Aug 27, 2021

@gurjeet734 @nitishat I have a PR that enables screenshare to work correctly: #202

@yoreland
Copy link
Contributor

There is branch "ScreenSharing" for temp solution. Formal solution will be provided when next SDK come out

@asif-ullah
Copy link

asif-ullah commented Sep 7, 2021

any solution for android please? still facing this issue and didn't found any solution

@yoreland
Copy link
Contributor

any solution for android please? still facing this issue and didn't found any solution

for android, can you please try this link as audience?
https://webdemo.agora.io/basicLive/index.html
just to make sure you could subscribe multiple video stream from your view.
We verified the same seems can't reproduce this issue, if you still can't see the screen share stream you could share your channel id and we could check it from our backend.

@gurjeet734
Copy link
Author

gurjeet734 commented Sep 14, 2021

Just use the Custom SurfaceView "AgoraSurfaceView" below code is applicable in the Demo app provided by the Agora So you can simply copy and paste there in the your demo App by Agora rest you can change at your end according to your requirement::
Below is the Multiprocess.java


package io.agora.api.example.examples.advanced;

import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.yanzhenjie.permission.AndPermission;
import com.yanzhenjie.permission.runtime.Permission;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;

import io.agora.api.example.MainApplication;
import io.agora.api.example.R;
import io.agora.api.example.annotation.Example;
import io.agora.api.example.common.BaseFragment;
import io.agora.api.example.utils.CommonUtil;
import io.agora.rtc.Constants;
import io.agora.rtc.IRtcEngineEventHandler;
import io.agora.rtc.RtcEngine;
import io.agora.rtc.mediaio.AgoraSurfaceView;
import io.agora.rtc.mediaio.MediaIO;
import io.agora.rtc.models.ChannelMediaOptions;
import io.agora.rtc.ss.ScreenSharingClient;
import io.agora.rtc.video.VideoCanvas;
import io.agora.rtc.video.VideoEncoderConfiguration;

import static io.agora.api.example.common.model.Examples.ADVANCED;
import static io.agora.rtc.video.VideoCanvas.RENDER_MODE_FILL;
import static io.agora.rtc.video.VideoCanvas.RENDER_MODE_HIDDEN;
import static io.agora.rtc.video.VideoEncoderConfiguration.FRAME_RATE.FRAME_RATE_FPS_30;
import static io.agora.rtc.video.VideoEncoderConfiguration.ORIENTATION_MODE.ORIENTATION_MODE_ADAPTIVE;
import static io.agora.rtc.video.VideoEncoderConfiguration.STANDARD_BITRATE;
import static io.agora.rtc2.Constants.LOG_FILTER_DEBUG;

/*This demo demonstrates how to make a one-to-one video call/
@example(
index = 6,
group = ADVANCED,
name = R.string.item_twoProcessScreenShare,
actionId = R.id.action_mainFragment_to_two_process_screen_share,
tipsId = R.string.multiProcessScreenShare
)
public class MultiProcess extends BaseFragment implements View.OnClickListener
{
private static final String TAG = MultiProcess.class.getSimpleName();
private static final Integer SCREEN_SHARE_UID = 10000;

private FrameLayout fl_local, fl_remote;
private Button join, screenShare;
private EditText et_channel;
private RtcEngine engine;
private int myUid;
private boolean joined = false;
private boolean isSharing = false;
private ScreenSharingClient mSSClient;

private final ScreenSharingClient.IStateListener mListener = new ScreenSharingClient.IStateListener() {
    @Override
    public void onError(int error) {
        Log.e(TAG, "Screen share service error happened: " + error);
    }

    @Override
    public void onTokenWillExpire() {
        Log.d(TAG, "Screen share service token will expire");
        mSSClient.renewToken(null); // Replace the token with your valid token
    }
};

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
{
    View view = inflater.inflate(R.layout.fragment_two_process_screen_share, container, false);
    return view;
}

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)
{
    super.onViewCreated(view, savedInstanceState);
    join = view.findViewById(R.id.btn_join);
    screenShare = view.findViewById(R.id.screenShare);
    screenShare.setEnabled(false);
    et_channel = view.findViewById(R.id.et_channel);
    view.findViewById(R.id.btn_join).setOnClickListener(this);
    view.findViewById(R.id.screenShare).setOnClickListener(this);
    fl_local = view.findViewById(R.id.fl_local);
    fl_remote = view.findViewById(R.id.fl_remote);
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState)
{
    super.onActivityCreated(savedInstanceState);
    // Check if the context is valid
    Context context = getContext();
    if (context == null)
    {
        return;
    }
    try
    {
        /**Creates an RtcEngine instance.
         * @param context The context of Android Activity
         * @param appId The App ID issued to you by Agora. See <a href="https://docs.agora.io/en/Agora%20Platform/token#get-an-app-id">
         *              How to get the App ID</a>
         * @param handler IRtcEngineEventHandler is an abstract class providing default implementation.
         *                The SDK uses this class to report to the app on SDK runtime events.*/
        engine = RtcEngine.create(context.getApplicationContext(), getString(R.string.agora_app_id), iRtcEngineEventHandler);

        // Initialize Screen Share Client
        mSSClient = ScreenSharingClient.getInstance();
        mSSClient.setListener(mListener);
    }
    catch (Exception e)
    {
        e.printStackTrace();
        getActivity().onBackPressed();
    }
}

@Override
public void onDestroy()
{
    super.onDestroy();
    /**leaveChannel and Destroy the RtcEngine instance*/
    if(engine != null)
    {
        engine.leaveChannel();
    }
    if (isSharing) {
        mSSClient.stop(getContext());
    }
    handler.post(RtcEngine::destroy);
    engine = null;
}

@Override
public void onClick(View v)
{
    if (v.getId() == R.id.btn_join)
    {
        if (!joined)
        {
            CommonUtil.hideInputBoard(getActivity(), et_channel);
            // call when join button hit
            String channelId = et_channel.getText().toString();
            // Check permission
            if (AndPermission.hasPermissions(this, Permission.Group.STORAGE, Permission.Group.MICROPHONE, Permission.Group.CAMERA))
            {
                joinChannel(channelId);
                return;
            }
            // Request permission
            AndPermission.with(this).runtime().permission(
                    Permission.Group.STORAGE,
                    Permission.Group.MICROPHONE,
                    Permission.Group.CAMERA
            ).onGranted(permissions ->
            {
                // Permissions Granted
                joinChannel(channelId);
            }).start();
        }
        else
        {
            joined = false;
            /**After joining a channel, the user must call the leaveChannel method to end the
             * call before joining another channel. This method returns 0 if the user leaves the
             * channel and releases all resources related to the call. This method call is
             * asynchronous, and the user has not exited the channel when the method call returns.
             * Once the user leaves the channel, the SDK triggers the onLeaveChannel callback.
             * A successful leaveChannel method call triggers the following callbacks:
             *      1:The local client: onLeaveChannel.
             *      2:The remote client: onUserOffline, if the user leaving the channel is in the
             *          Communication channel, or is a BROADCASTER in the Live Broadcast profile.
             * @returns 0: Success.
             *          < 0: Failure.
             * PS:
             *      1:If you call the destroy method immediately after calling the leaveChannel
             *          method, the leaveChannel process interrupts, and the SDK does not trigger
             *          the onLeaveChannel callback.
             *      2:If you call the leaveChannel method during CDN live streaming, the SDK
             *          triggers the removeInjectStreamUrl method.*/
            engine.leaveChannel();
            join.setText(getString(R.string.join));
            mSSClient.stop(getContext());
            screenShare.setText(getResources().getString(R.string.screenshare));
            screenShare.setEnabled(false);
            isSharing = false;
        }
    }
    else if (v.getId() == R.id.screenShare){
        String channelId = et_channel.getText().toString();
        if (!isSharing) {
            mSSClient.start(getContext(), getResources().getString(R.string.agora_app_id), getString(R.string.agora_access_token),
                    channelId, SCREEN_SHARE_UID, new VideoEncoderConfiguration(
                            getScreenDimensions(),
                            FRAME_RATE_FPS_30,
                            STANDARD_BITRATE,
                            ORIENTATION_MODE_ADAPTIVE
                    ));
            screenShare.setText(getResources().getString(R.string.stop));
            isSharing = true;
        } else {
            mSSClient.stop(getContext());
            screenShare.setText(getResources().getString(R.string.screenshare));
            isSharing = false;
        }
    }
}

private VideoEncoderConfiguration.VideoDimensions getScreenDimensions(){
    WindowManager manager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
    DisplayMetrics outMetrics = new DisplayMetrics();
    manager.getDefaultDisplay().getMetrics(outMetrics);
    return new VideoEncoderConfiguration.VideoDimensions(outMetrics.widthPixels / 2, outMetrics.heightPixels / 2);
}

private void joinChannel(String channelId) {
    // Check if the context is valid
    Context context = getContext();
    if (context == null) {
        return;
    }

    // Create render view by RtcEngine
    SurfaceView surfaceView = RtcEngine.CreateRendererView(context);
    // Add to the local container
    if (fl_local.getChildCount() > 0) {
        fl_local.removeAllViews();
    }
    fl_local.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
    // Setup local video to render your local camera preview
    engine.setupLocalVideo(new VideoCanvas(surfaceView, RENDER_MODE_HIDDEN, 0));
    /**Set up to play remote sound with receiver*/
    engine.setDefaultAudioRoutetoSpeakerphone(true);
    engine.setEnableSpeakerphone(true);

    /** Sets the channel profile of the Agora RtcEngine.
     CHANNEL_PROFILE_COMMUNICATION(0): (Default) The Communication profile.
     Use this profile in one-on-one calls or group calls, where all users can talk freely.
     CHANNEL_PROFILE_LIVE_BROADCASTING(1): The Live-Broadcast profile. Users in a live-broadcast
     channel have a role as either broadcaster or audience. A broadcaster can both send and receive streams;
     an audience can only receive streams.*/
    engine.setChannelProfile(Constants.CHANNEL_PROFILE_LIVE_BROADCASTING);
    /**In the demo, the default is to enter as the anchor.*/
    engine.setClientRole(IRtcEngineEventHandler.ClientRole.CLIENT_ROLE_BROADCASTER);
    // Enable video module
    engine.enableVideo();
    // Setup video encoding configs
    engine.setVideoEncoderConfiguration(new VideoEncoderConfiguration(
            ((MainApplication)getActivity().getApplication()).getGlobalSettings().getVideoEncodingDimensionObject(),
            VideoEncoderConfiguration.FRAME_RATE.valueOf(((MainApplication)getActivity().getApplication()).getGlobalSettings().getVideoEncodingFrameRate()),
            STANDARD_BITRATE,
            VideoEncoderConfiguration.ORIENTATION_MODE.valueOf(((MainApplication)getActivity().getApplication()).getGlobalSettings().getVideoEncodingOrientation())
    ));

    /**Please configure accessToken in the string_config file.
     * A temporary token generated in Console. A temporary token is valid for 24 hours. For details, see
     *      https://docs.agora.io/en/Agora%20Platform/token?platform=All%20Platforms#get-a-temporary-token
     * A token generated at the server. This applies to scenarios with high-security requirements. For details, see
     *      https://docs.agora.io/en/cloud-recording/token_server_java?platform=Java*/
    String accessToken = getString(R.string.agora_access_token);
    if (TextUtils.equals(accessToken, "") || TextUtils.equals(accessToken, "<#YOUR ACCESS TOKEN#>")) {
        accessToken = null;
    }
    /** Allows a user to join a channel.
     if you do not specify the uid, we will generate the uid for you*/
    engine.enableAudioVolumeIndication(1000, 3, true);

    ChannelMediaOptions option = new ChannelMediaOptions();
    option.autoSubscribeAudio = true;
    option.autoSubscribeVideo = true;
    engine.enableAudio();
    engine.enableLocalAudio(true);
    engine.setEnableSpeakerphone(true);
    int res = engine.joinChannel(accessToken, channelId, "Extra Optional Data", 0,option);
    if (res != 0) {
        // Usually happens with invalid parameters
        // Error code description can be found at:
        // en: https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html
        // cn: https://docs.agora.io/cn/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html
        showAlert(RtcEngine.getErrorDescription(Math.abs(res)));
        return;
    }
    // Prevent repeated entry
    join.setEnabled(false);
}

// private void joinChannel(String channelId)
// {
// // Check if the context is valid
// Context context = getContext();
// if (context == null)
// {
// return;
// }
//
// // Create render view by RtcEngine
// SurfaceView surfaceView = RtcEngine.CreateRendererView(context);
// if(fl_local.getChildCount() > 0)
// {
// fl_local.removeAllViews();
// }
// // Add to the local container
// fl_local.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
// // Setup local video to render your local camera preview
// engine.setupLocalVideo(new VideoCanvas(surfaceView, RENDER_MODE_HIDDEN, 0));
// // Set audio route to microPhone
// engine.disableAudio();
//// engine.setDefaultAudioRoutetoSpeakerphone(false);
//
// /** Sets the channel profile of the Agora RtcEngine.
// CHANNEL_PROFILE_COMMUNICATION(0): (Default) The Communication profile.
// Use this profile in one-on-one calls or group calls, where all users can talk freely.
// CHANNEL_PROFILE_LIVE_BROADCASTING(1): The Live-Broadcast profile. Users in a live-broadcast
// channel have a role as either broadcaster or audience. A broadcaster can both send and receive streams;
// an audience can only receive streams./
// engine.setChannelProfile(Constants.CHANNEL_PROFILE_LIVE_BROADCASTING);
// /**In the demo, the default is to enter as the anchor.
/
// engine.setClientRole(IRtcEngineEventHandler.ClientRole.CLIENT_ROLE_BROADCASTER);
// // Enable video module
// engine.enableVideo();
// // Setup video encoding configs
// engine.setVideoEncoderConfiguration(new VideoEncoderConfiguration(
// ((MainApplication)getActivity().getApplication()).getGlobalSettings().getVideoEncodingDimensionObject(),
// VideoEncoderConfiguration.FRAME_RATE.valueOf(((MainApplication)getActivity().getApplication()).getGlobalSettings().getVideoEncodingFrameRate()),
// STANDARD_BITRATE,
// VideoEncoderConfiguration.ORIENTATION_MODE.valueOf(((MainApplication)getActivity().getApplication()).getGlobalSettings().getVideoEncodingOrientation())
// ));
//
// /Please configure accessToken in the string_config file.
// * A temporary token generated in Console. A temporary token is valid for 24 hours. For details, see
// * https://docs.agora.io/en/Agora%20Platform/token?platform=All%20Platforms#get-a-temporary-token
// * A token generated at the server. This applies to scenarios with high-security requirements. For details, see
// * https://docs.agora.io/en/cloud-recording/token_server_java?platform=Java*/
// String accessToken = getString(R.string.agora_access_token);
// if (TextUtils.equals(accessToken, "") || TextUtils.equals(accessToken, "<#YOUR ACCESS TOKEN#>"))
// {
// accessToken = null;
// }
// /
Allows a user to join a channel.
// if you do not specify the uid, we will generate the uid for you*/
//
// ChannelMediaOptions option = new ChannelMediaOptions();
// option.autoSubscribeAudio = false;
// option.autoSubscribeVideo = false;
// int res = engine.joinChannel(accessToken, channelId, "Extra Optional Data", 0, option);
// if (res != 0)
// {
// // Usually happens with invalid parameters
// // Error code description can be found at:
// // en: https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html
// // cn: https://docs.agora.io/cn/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html
// showAlert(RtcEngine.getErrorDescription(Math.abs(res)));
// return;
// }
// // Prevent repeated entry
// join.setEnabled(false);
// }

/**
 * IRtcEngineEventHandler is an abstract class providing default implementation.
 * The SDK uses this class to report to the app on SDK runtime events.
 */
private final IRtcEngineEventHandler iRtcEngineEventHandler = new IRtcEngineEventHandler()
{
    /**Reports a warning during SDK runtime.
     * Warning code: https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_warn_code.html*/
    @Override
    public void onWarning(int warn)
    {
        Log.w(TAG, String.format("onWarning code %d message %s", warn, RtcEngine.getErrorDescription(warn)));
    }

    /**Reports an error during SDK runtime.
     * Error code: https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html*/
    @Override
    public void onError(int err)
    {
        Log.e(TAG, String.format("onError code %d message %s", err, RtcEngine.getErrorDescription(err)));
        showAlert(String.format("onError code %d message %s", err, RtcEngine.getErrorDescription(err)));
    }

    /**Occurs when a user leaves the channel.
     * @param stats With this callback, the application retrieves the channel information,
     *              such as the call duration and statistics.*/
    @Override
    public void onLeaveChannel(RtcStats stats)
    {
        super.onLeaveChannel(stats);
        Log.i(TAG, String.format("local user %d leaveChannel!", myUid));
        showLongToast(String.format("local user %d leaveChannel!", myUid));
    }

    /**Occurs when the local user joins a specified channel.
     * The channel name assignment is based on channelName specified in the joinChannel method.
     * If the uid is not specified when joinChannel is called, the server automatically assigns a uid.
     * @param channel Channel name
     * @param uid User ID
     * @param elapsed Time elapsed (ms) from the user calling joinChannel until this callback is triggered*/
    @Override
    public void onJoinChannelSuccess(String channel, int uid, int elapsed)
    {
        Log.i(TAG, String.format("onJoinChannelSuccess channel %s uid %d", channel, uid));
        showLongToast(String.format("onJoinChannelSuccess channel %s uid %d", channel, uid));
        myUid = uid;
        joined = true;
        handler.post(new Runnable()
        {
            @Override
            public void run()
            {
                join.setEnabled(true);
                join.setText(getString(R.string.leave));
                screenShare.setEnabled(true);
            }
        });
    }

    /**Since v2.9.0.
     * This callback indicates the state change of the remote audio stream.
     * PS: This callback does not work properly when the number of users (in the Communication profile) or
     *     broadcasters (in the Live-broadcast profile) in the channel exceeds 17.
     * @param uid ID of the user whose audio state changes.
     * @param state State of the remote audio
     *   REMOTE_AUDIO_STATE_STOPPED(0): The remote audio is in the default state, probably due
     *              to REMOTE_AUDIO_REASON_LOCAL_MUTED(3), REMOTE_AUDIO_REASON_REMOTE_MUTED(5),
     *              or REMOTE_AUDIO_REASON_REMOTE_OFFLINE(7).
     *   REMOTE_AUDIO_STATE_STARTING(1): The first remote audio packet is received.
     *   REMOTE_AUDIO_STATE_DECODING(2): The remote audio stream is decoded and plays normally,
     *              probably due to REMOTE_AUDIO_REASON_NETWORK_RECOVERY(2),
     *              REMOTE_AUDIO_REASON_LOCAL_UNMUTED(4) or REMOTE_AUDIO_REASON_REMOTE_UNMUTED(6).
     *   REMOTE_AUDIO_STATE_FROZEN(3): The remote audio is frozen, probably due to
     *              REMOTE_AUDIO_REASON_NETWORK_CONGESTION(1).
     *   REMOTE_AUDIO_STATE_FAILED(4): The remote audio fails to start, probably due to
     *              REMOTE_AUDIO_REASON_INTERNAL(0).
     * @param reason The reason of the remote audio state change.
     *   REMOTE_AUDIO_REASON_INTERNAL(0): Internal reasons.
     *   REMOTE_AUDIO_REASON_NETWORK_CONGESTION(1): Network congestion.
     *   REMOTE_AUDIO_REASON_NETWORK_RECOVERY(2): Network recovery.
     *   REMOTE_AUDIO_REASON_LOCAL_MUTED(3): The local user stops receiving the remote audio
     *               stream or disables the audio module.
     *   REMOTE_AUDIO_REASON_LOCAL_UNMUTED(4): The local user resumes receiving the remote audio
     *              stream or enables the audio module.
     *   REMOTE_AUDIO_REASON_REMOTE_MUTED(5): The remote user stops sending the audio stream or
     *               disables the audio module.
     *   REMOTE_AUDIO_REASON_REMOTE_UNMUTED(6): The remote user resumes sending the audio stream
     *              or enables the audio module.
     *   REMOTE_AUDIO_REASON_REMOTE_OFFLINE(7): The remote user leaves the channel.
     * @param elapsed Time elapsed (ms) from the local user calling the joinChannel method
     *                  until the SDK triggers this callback.*/
    @Override
    public void onRemoteAudioStateChanged(int uid, int state, int reason, int elapsed)
    {
        super.onRemoteAudioStateChanged(uid, state, reason, elapsed);
        Log.i(TAG, "onRemoteAudioStateChanged->" + uid + ", state->" + state + ", reason->" + reason);
    }

    /**Since v2.9.0.
     * Occurs when the remote video state changes.
     * PS: This callback does not work properly when the number of users (in the Communication
     *     profile) or broadcasters (in the Live-broadcast profile) in the channel exceeds 17.
     * @param uid ID of the remote user whose video state changes.
     * @param state State of the remote video:
     *   REMOTE_VIDEO_STATE_STOPPED(0): The remote video is in the default state, probably due
     *              to REMOTE_VIDEO_STATE_REASON_LOCAL_MUTED(3), REMOTE_VIDEO_STATE_REASON_REMOTE_MUTED(5),
     *              or REMOTE_VIDEO_STATE_REASON_REMOTE_OFFLINE(7).
     *   REMOTE_VIDEO_STATE_STARTING(1): The first remote video packet is received.
     *   REMOTE_VIDEO_STATE_DECODING(2): The remote video stream is decoded and plays normally,
     *              probably due to REMOTE_VIDEO_STATE_REASON_NETWORK_RECOVERY (2),
     *              REMOTE_VIDEO_STATE_REASON_LOCAL_UNMUTED(4), REMOTE_VIDEO_STATE_REASON_REMOTE_UNMUTED(6),
     *              or REMOTE_VIDEO_STATE_REASON_AUDIO_FALLBACK_RECOVERY(9).
     *   REMOTE_VIDEO_STATE_FROZEN(3): The remote video is frozen, probably due to
     *              REMOTE_VIDEO_STATE_REASON_NETWORK_CONGESTION(1) or REMOTE_VIDEO_STATE_REASON_AUDIO_FALLBACK(8).
     *   REMOTE_VIDEO_STATE_FAILED(4): The remote video fails to start, probably due to
     *              REMOTE_VIDEO_STATE_REASON_INTERNAL(0).
     * @param reason The reason of the remote video state change:
     *   REMOTE_VIDEO_STATE_REASON_INTERNAL(0): Internal reasons.
     *   REMOTE_VIDEO_STATE_REASON_NETWORK_CONGESTION(1): Network congestion.
     *   REMOTE_VIDEO_STATE_REASON_NETWORK_RECOVERY(2): Network recovery.
     *   REMOTE_VIDEO_STATE_REASON_LOCAL_MUTED(3): The local user stops receiving the remote
     *               video stream or disables the video module.
     *   REMOTE_VIDEO_STATE_REASON_LOCAL_UNMUTED(4): The local user resumes receiving the remote
     *               video stream or enables the video module.
     *   REMOTE_VIDEO_STATE_REASON_REMOTE_MUTED(5): The remote user stops sending the video
     *               stream or disables the video module.
     *   REMOTE_VIDEO_STATE_REASON_REMOTE_UNMUTED(6): The remote user resumes sending the video
     *               stream or enables the video module.
     *   REMOTE_VIDEO_STATE_REASON_REMOTE_OFFLINE(7): The remote user leaves the channel.
     *   REMOTE_VIDEO_STATE_REASON_AUDIO_FALLBACK(8): The remote media stream falls back to the
     *               audio-only stream due to poor network conditions.
     *   REMOTE_VIDEO_STATE_REASON_AUDIO_FALLBACK_RECOVERY(9): The remote media stream switches
     *               back to the video stream after the network conditions improve.
     * @param elapsed Time elapsed (ms) from the local user calling the joinChannel method until
     *               the SDK triggers this callback.*/
    @Override
    public void onRemoteVideoStateChanged(int uid, int state, int reason, int elapsed)
    {
        super.onRemoteVideoStateChanged(uid, state, reason, elapsed);
        Log.i(TAG, "onRemoteVideoStateChanged->" + uid + ", state->" + state + ", reason->" + reason);
    }

    /**Occurs when a remote user (Communication)/host (Live Broadcast) joins the channel.
     * @param uid ID of the user whose audio state changes.
     * @param elapsed Time delay (ms) from the local user calling joinChannel/setClientRole
     *                until this callback is triggered.*/
    @Override
    public void onUserJoined(int uid, int elapsed) {
        super.onUserJoined(uid, elapsed);
        Log.i(TAG, "onUserJoined->" + uid);
        showLongToast(String.format("user %d joined!", uid));
        /**Check if the context is correct*/
        Context context = getContext();
        if (context == null) {
            return;
        }
        handler.post(() ->
        {
            /**custom remote video surfaceView
             * about AgoraSurfaceView,you can see this(
             * https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1mediaio_1_1_agora_surface_view.html#ab4dcf3124f4bb4b945a043143babe41b)*/
            AgoraSurfaceView surfaceView = new AgoraSurfaceView(getContext());
            surfaceView.init(null);
            surfaceView.setZOrderMediaOverlay(true);
            surfaceView.setBufferType(MediaIO.BufferType.BYTE_BUFFER);
            surfaceView.setPixelFormat(MediaIO.PixelFormat.I420);
            if (fl_remote.getChildCount() > 0) {
                fl_remote.removeAllViews();
            }
            /** Allows a user to join a channel.
             if you do not specify the uid, we will generate the uid for you*/
            engine.enableAudioVolumeIndication(1000, 3, true);

            ChannelMediaOptions option = new ChannelMediaOptions();
            option.autoSubscribeAudio = true;
            option.autoSubscribeVideo = true;
            engine.enableAudio();
            engine.enableLocalAudio(true);
            engine.setEnableSpeakerphone(true);
            fl_remote.addView(surfaceView);
            /**Customizes the remote video renderer.
             * Call this method to add an external remote video renderer to the SDK.
             * @param uid The ID of the remote user.
             * @param render Sets the remote video renderer. See IVideoSink(
             * https://docs.agora.io/en/Voice/API%20Reference/java/v3.0.1/interfaceio_1_1agora_1_1rtc_1_1mediaio_1_1_i_video_sink.html).*/
            engine.setRemoteVideoRenderer(uid, surfaceView);
        });
    }

// public void onUserJoined(int uid, int elapsed)
// {
// // Java
// // Set the log filter to debug
// engine.setLogFilter(LOG_FILTER_DEBUG);
//
// // Get the document path and save to sdcard
// // Get the current timestamp to separate log files
// String ts = new SimpleDateFormat("yyyyMMdd").format(new Date());
// String filepath = ts + ".log";
// File file = new File(getActivity().getCacheDir(), filepath);
// engine.setLogFile(file.getAbsolutePath());
// super.onUserJoined(uid, elapsed);
// Log.i(TAG, "onUserJoined->" + uid);
// showLongToast(String.format("user %d joined!", uid));
// // don't render screen sharing view
// if (SCREEN_SHARE_UID == uid){
// return;
// }
// /*Check if the context is correct/
// Context context = getContext();
// if (context == null) {
// return;
// }
// handler.post(() ->
// {
// /*Display remote video stream/
// SurfaceView surfaceView = null;
// if (fl_remote.getChildCount() > 0)
// {
// fl_remote.removeAllViews();
// }
// // Create render view by RtcEngine
// surfaceView = RtcEngine.CreateRendererView(context);
// surfaceView.setZOrderMediaOverlay(true);
// // Add to the remote container
// fl_remote.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
//
//// new VideoCanvas(
//// surface,
//// VideoCanvas.RENDER_MODE_HIDDEN,
//// uid,
//// Constants.VIDEO_MIRROR_MODES[config().getMirrorRemoteIndex()]
//
// // Setup remote video to render
// engine.setupRemoteVideo(new VideoCanvas(surfaceView, RENDER_MODE_HIDDEN, uid));
// });
// }

    /**Occurs when a remote user (Communication)/host (Live Broadcast) leaves the channel.
     * @param uid ID of the user whose audio state changes.
     * @param reason Reason why the user goes offline:
     *   USER_OFFLINE_QUIT(0): The user left the current channel.
     *   USER_OFFLINE_DROPPED(1): The SDK timed out and the user dropped offline because no data
     *              packet was received within a certain period of time. If a user quits the
     *               call and the message is not passed to the SDK (due to an unreliable channel),
     *               the SDK assumes the user dropped offline.
     *   USER_OFFLINE_BECOME_AUDIENCE(2): (Live broadcast only.) The client role switched from
     *               the host to the audience.*/
    @Override
    public void onUserOffline(int uid, int reason)
    {
        Log.i(TAG, String.format("user %d offline! reason:%d", uid, reason));
        showLongToast(String.format("user %d offline! reason:%d", uid, reason));
        if (SCREEN_SHARE_UID == uid){
            return;
        }
        handler.post(new Runnable() {
            @Override
            public void run() {
                /**Clear render view
                 Note: The video will stay at its last frame, to completely remove it you will need to
                 remove the SurfaceView from its parent*/
                engine.setupRemoteVideo(new VideoCanvas(null, RENDER_MODE_HIDDEN, uid));
            }
        });
    }
};

}

@gurjeet734
Copy link
Author

gurjeet734 commented Sep 14, 2021

You can compare the old code and new code to know the changes that has been done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants