# Realize group video calling

This chapter introduces how to implement group video calling. The API call sequence of group video calling is shown in the figure below:

../../../../_images_en/multivideoworkflow.jpg

# Initialize

Extend the JCMediaChannelCallback (opens new window) object and JCMediaDeviceCallback (opens new window) object, and implement the pure virtual functions in these two objects.

class JCManager : public JCMediaDeviceCallback, public JCMediaChannelCallback
{
public:

    //The callback of MediaChannel state change
    virtual void onMediaChannelStateChange(JCMediaChannelState state, JCMediaChannelState oldState);
    //The callback of channel property change
    virtual void onMediaChannelPropertyChange(JCMediaChannelPropChangeParam propChangeParam);
    //The callback of joining ChannelReason
    virtual void onJoin(bool result, JCMediaChannelReason reason, const char* channelId);
    //The callback of leaving ChannelReason
    virtual void onLeave(JCMediaChannelReason reason, const char* channelId);
    //The callback of channel Stop result
    virtual void onStop(bool result, JCMediaChannelReason reason);
    //The callback of channel Query result
    virtual void onQuery(int operationId, bool result, JCMediaChannelReason reason, JCMediaChannelQueryInfo* queryInfo);
    //The callback of ParticipantJoin
    virtual void onParticipantJoin(JCMediaChannelParticipant* participant);
    //The callback of ParticipantLeft
    virtual void onParticipantLeft(JCMediaChannelParticipant* participant);
    //The callback of ParticipantUpdate
    virtual void onParticipantUpdate(JCMediaChannelParticipant* participant, JCMediaChannelParticipant::ChangeParam changeParam);
    //This callback triggers when messages are received
    virtual void onMessageReceive(const char* type, const char* content, const char* fromUserId);
    //The callback of InviteSipUserResult
    virtual void onInviteSipUserResult(int operationId, bool result, JCMediaChannelReason reason);
    //The volume change of participants
    virtual void onParticipantVolumeChange(JCMediaChannelParticipant* participant);

public:
    //mediaDevice object
    JCMediaDevice* mediaDevice;
    //mediaChannel onject
    JCMediaChannel* mediaChannel;
};

Call createJCMediaDevice (opens new window) and createJCCall (opens new window) to initialize the modules needed for group video calls:

//Initialize
bool JCManager::initialize()
{
    //1. Media class
    mediaDevice = createJCMediaDevice(client, this);
    //1. mediaChannel class
    mediaChannel = createJCMediaChannel(client, mediaDevice, this);
}

Among them:

  • This in the JCMediaDevice create method is a derived class of JCMediaDeviceCallback (opens new window) , which is used to notify the upper layer of media device-related events. Therefore, you need to create a derived class of JCMediaDeviceCallback, and then implement the pure virtual function of JCMediaDeviceCallback in the derived class.

  • This in the JCMediaChannel create method is a derived class of JCMediaChannelCallback (opens new window) , which is used to notify related events in the channel to the upper layer. Therefore, you need to create a derived class of JCMediaChannelCallback, and then implement the pure virtual function of JCMediaChannelCallback in the derived class.

TIP

The object in the callback can only be used in the callback and cannot be saved. The upper layer can obtain the call object through the corresponding method.

# Join a channel

JC SDK does not upload local audio streams by default, so if you need to enter the meeting to hear each other’s voices, you need to pre-open the pload logo of audio stream before joining a channel:

  1. Call enableUploadAudioStream (opens new window) to enable audio streaming:

    // Turn on audio streaming
    JCManager::shared()->mediaChannel->enableUploadAudioStream(true);
    

TIP

This interface can be called before joining a channel or after joining a channel. The differences between the two circumstances are as follows.

If it is called before joining the channel, it only turns on or off the “Upload Audio”, but not send data. When joining the channel successfully, it will determine whether to upload audio data according to the value set by enableUploadAudioStream. At the same time, other members in the channel receive the callback of status change (onParticipantUpdate) of the member “whether to upload audio”.

If called after joining the channel, it will turn on or off sending local audio stream data, and the server will also determine whether to upload audio data according to the value set by enableUploadAudioStream. At the same time, other members in the channel will receive the callback of state change (onParticipantUpdate) of the member “whether to upload audio “.

In addition, this method can also implement the function of turn on/off mute. When the enable value is false, the local audio stream will be stopped, and you can hear the voices of other members , but other members will not hear your voice. Thus, realize the mute function.

  1. Call the join (opens new window) method to join the channel. You need to pass in the following parameters in this method:
  • channelIdOrUri: Channel ID or channel Uri. When uriMode in param is set to true, it means channel Uri, and others mean channel ID. Users with the same channel ID or Uri will enter the same channel.

  • joinParam: Join parameters, if not, fill in NULL. See JCMediaChannelJoinParam (opens new window) object for details.

// Join a channel
JCManager::shared()->mediaChannel->join("channel ID", NULL);
  1. Receive onJoin (opens new window) callback after joining a channel:

    // The callback of joining channel result
    void JCManager::onJoin(bool result, JCMediaChannelReason reason, const char* channelId)
    {
        if (result) {
        //the logic of successful joining in
         ...
        ...
        } else {
        //the logic of failed joining in
        ...
        }
    }
    

# Create local video images

  1. After joining the channel, call the getSelfParticipant (opens new window) method in JCMediaChannel (opens new window) to get the channel object in the channel. This method returns the JCMediaChannelParticipant (opens new window) object:

    // 1. Access the memeber objects in the channel
    JCMediaChannelParticipant* participant = JCManager::shared()->mediaChannel->getSelfParticipant();
    
  2. Call the startVideo (opens new window) method in the JCMediaChannelParticipant (opens new window) class to open the local video preview. This method will return a JCMediaDeviceVideoCanvas (opens new window) object, which is used to render the video to the canvas and manage the rendering method. (Call this Method will turn on the camera):

    // 2. Open local video preview
    JCMediaDeviceVideoCanvas* mConfSelfCanvas;
    if (mediaChannel->getUploadLocalVideo() && mConfSelfCanvas == NULL)
                {
                        if (strlen(JCManager::shared()->mediaDevice->getCamera().cameraId) > 0)
                        {
                //Create local and remote video images
                                mConfSelfCanvas = mediaChannel->getSelfParticipant()->startVideo(
                    mWndConfSelfVideo.m_hWnd,
                    JCMediaDeviceRenderModeFullContent,
                    JCMediaChannelPictureSizeLarge);
                        }
                }
    

# Create remote video images

You usually need to see other users during a video call. Obtain all member objects in the channel through the participants property in JCMediaChannel (opens new window) .

Call the startVideo (opens new window) method in the JCMediaChannelParticipant (opens new window) class to set the video of remote users. Calling this method will return a JCMediaDeviceVideoCanvas (opens new window) object, which is used to render the video to the canvas and manage the rendering method:

//Obtain all member objects in the channel
JCMediaDeviceVideoCanvas* mConfOtherCanvas;
std::list<JCMediaChannelParticipant*>* participants = NULL;
JCMediaChannelParticipant* other = NULL;
participants = mediaChannel->getParticipants();
for (JCMediaChannelParticipant* participant : *participants) {
    if (!participant->isSelf())
    {
        other = participant;
        break;
    }
}
if (other != NULL && other->isVideo())
{
    if (mConfOtherCanvas == NULL)
    {
        //Create remote image
        mConfOtherCanvas = participant->startVideo(
                        mWndConfOtherVideo.m_hWnd,
                        JCMediaDeviceRenderModeFullContent,
                        JCMediaChannelPictureSizeLarge);
    }
}

# Leave a channel

Call the leave (opens new window) method to leave the current channel:

JCManager::shared()->mediaChannel->leave();

After leaving the channel, you receive the onLeave (opens new window) callback, and other members receive the onParticipantLeft (opens new window) callback at the same time:

// The callback of leaving the channel
void JCManager::onLeave(JCMediaChannelReason reason, const char* channelId);
{
    //The logic of leaving a channel
}

# Destroy local and remote video images

After the video is hung up, call stopVideo (opens new window) to destroy local and remote video images in JCMediaChannelParticipant (opens new window):

if (!mediaChannel->getUploadLocalVideo() && mConfSelfCanvas != NULL)
{
    //Destroy the local video image
    mediaChannel->getSelfParticipant()->stopVideo();
    mConfSelfCanvas = NULL;
    mWndConfSelfVideo.Invalidate();
}
if (mConfOtherCanvas != NULL)
{
    for (JCMediaChannelParticipant* participant : *participants)
    {
        if (!participant->isSelf())
        {
            //Destroy the local video image
            participant->stopVideo();
        }
    }
    mConfOtherCanvas = NULL;
    mWndConfOtherVideo.Invalidate();
}

# Destroy a channel

If you want to destroy a channel, you can call the following interface, and all members will be quit:

// End the channel
JCManager::shared()->mediaChannel->stop();

After the channel is stopped, the member that initiated the termination receives the onStop (opens new window) callback, and other members receive the onLeave (opens new window) callback at the same time. Please refer to JCMediaChannelReason (opens new window) for the enumeration value of the reason for failure.

void JCManager::onStop(bool result, JCMediaChannelReason reason)
{
    //The processing logic of ending a channel
}

After stopping the channel, you also need to call stopVideo (opens new window) in the JCMediaChannelParticipant (opens new window) to destroy local and remote video images.

Now, you have completed the basic group video call function.