# live interactive video streaming

This guide introduces how to implement live interactive video streaming. The API call sequence of live interactive vieo streaming 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 in the channel
    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 object
    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.

# Role setting

There are two roles for users in live streaming: the host and audience. The audience can only see the image of the host and hear the voice of the host.

Before joining the channel, you must set the role at first. The host needs to upload the local audio and video streams, but the audience does not.

The role value can be customized according to the JCMediaChannelCustomRole (opens new window) enumeration value, such as:

//Customize the role of the host according to the CustomState enumeration value
JCMediaChannelCustomRole ROLE_BROASCASTER = JCMediaChannelCustomRole0;
//Customize the role of audiences according to the CustomState enumeration value
JCMediaChannelCustomRole ROLE_AUDIENCE = JCMediaChannelCustomRole1;

Call setCustomRole (opens new window) to set your own role to enter the channel:

// Set the role; the value of participant (the second parameter) is null, which means that you set your own role
JCManager::shared()->mediaChannel->setCustomRole(ROLE_BROASCASTER, NULL);

TIP

After joining a channel, you can also call the setCustomRole method if you want to switch user roles.

# Join a channel

Before joining a channel, you need to control the upload of audio and video streams according to the role of the member.The host needs to upload the local audio and video streams, but the audience does not.

  1. Call enableUploadAudioStream (opens new window) to enable the audio stream, and call enableUploadVideoStream (opens new window) to enable the video stream:

    //Upload local audio and video streams according to roles
    JCManager::shared()->mediaChannel->enableUploadAudioStream(customRole == ROLE_BROASCASTER);
    JCManager::shared()->mediaChannel->enableUploadVideoStream(customRole == ROLE_BROASCASTER);
    

TIP

  • These two interfaces can be called before joining a channel, or after joining a channel.

  • If called before joining a channel, it will only pre-open or close the “upload audio and video stream” symbol, but will not send data.. After joining a channel, the server will determine whether to upload audio and video data according to the value passed in the parameter.

  • If the value passed in the enableUploadVideoStream method is false before joining a channel, the voice call mode will be automatically turned on after joining a channel.

  • In addition, call the enableUploadVideoStream method to send local video stream data according to whether the camera has been opened.

  • After the interface is called, when joining a channel, other members in the channel will receive the status change callback (onParticipantUpdate) of the member “whether to upload audio and video”.

  1. After the role is set, call the join (opens new window) method to create and join a channel. You need to pass in the following parameters in the 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);
    
  2. The onJoin (opens new window) callback triggers after joining the channel:

    // The callback of joining ChannelReason
    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 remote video. 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, they 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 a 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.