# Realize One-to-One Video Calling

This guide introduces how to achieve one-to-one video calling. API call sequence for one-to-one call is as shown below:

../../../../_images_en/1-1workflowios.jpg

# Initialize

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

class JCManager : public JCMediaDeviceCallback, public JCCallCallback
{
public:
  //This callback triggers when the callItem is added
  virtual void onCallItemAdd(JCCallItem* item);
  //This callback triggers when the callItem is removed
  virtual void onCallItemRemove(JCCallItem* item, JCCallReason reason, const char* description);
  //This callback triggers when the callItem‘s status is updated
  virtual void onCallItemUpdate(JCCallItem* item, JCCallItemChangeParam changeParam);
  //This callback triggers when messages are received
  virtual void onMessageReceive(const char* type, const char* content, JCCallItem* item);
  //This callback triggers when get missed calls
  virtual void onMissedCallItem(JCCallItem* item);
  //This callback triggers when the camera is switched
  virtual void onCameraUpdate();

public:
    //JCMediaDevice object
    JCMediaDevice* mediaDevice;
    //JCCall object
    JCCall* call;
};

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.

Call createJCMediaDevice (opens new window) and createJCCall (opens new window) to initialize the modules needed for one-to-one calling:

bool JCManager::initialize()
{
  //1. Media class
  mediaDevice = createJCMediaDevice(client, this);
  //2. Call class
  call = createJCCall(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 JCCall create method is a derived class of JCCallCallback (opens new window), which is used to notify call-related events to the upper layer. Therefore, you need to create a derived class of JCCallback, and then implement the pure virtual function of JCCallCallback in the derived class.

# Make a call

To call call (opens new window) to initiate a video call, the parameters to be filled are:

  • userID Fill in the user ID of the other party.

  • video Select whether to call a video call, and true means to make a video call, while false means to make a voice call.

  • extraParam is a custom pass-through string, which can be obtained through the extraParam property in the JCCallItem object.

// Initiate a video call
void JCSampleDlg::OnBnClickedButtonVideocall()
{
  JCManager::shared()->call->call("userID", true, "custom pass-through string");
}

After making a call, both the caller and the called party will receive the callback onCallItemAdd (opens new window) for the new call, and the call state will change to JCCallStatePending. You can implement the onCallItemAdd (opens new window) method in the upper layer and process related logic:

// Receive a new call callback
void JCManager::onCallItemAdd(JCCallItem* item) {
    // Business logic
    if (item->direction == JCCallDirectionIn) {
        // If it is an incoming call
        ...
    } else {
        // If it is an outgoing call
        ...
    }
}

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. If the caller wants to cancel the call, he/she can go directly to the hang up part. After calling the hang up interface, the call status becomes the JCCallStateCancel.

# Create local video images

After initiating a call, call the startSelfVideo (opens new window) in the JCCallItem (opens new window) class to create a local video image. This method returns a JCMediaDeviceVideoCanvas (opens new window) object. This object is used to render the video to the canvas and manage the rendering method. (Calling this method will open the camera):

void JCManager::onCallItemAdd(JCCallItem* item) {
    JCMediaDeviceVideoCanvas* mCallLocalCanvas;
    if (mCallLocalCanvas == NULL && item->getUploadVideoStreamSelf())
                {
        // Create local and remote video images
        mCallLocalCanvas = item->startSelfVideo((void*)mWndCallLocalVideo.m_hWnd, (JCMediaDeviceRenderMode)JCMediaDeviceRenderModeFullContent);
    }
}

# Answer a call

  1. After the caller initiates the call successfully, the called party will receive the onCallItemAdd (opens new window) callback. At this time, the getVideo() method and getDirection() method in the JCCallItem (opens new window) object in the callback can be used to determine whether it is a video call or a voice call. Deal with it accordingly:

    void JCManager::onCallItemAdd(JCCallItem* item) {
        // 1. If it is a video call and is ringing
        if (item->getDirection() == JCCallDirectionIn && item->getState() == JCCallStatePending) {
            // 2. Make corresponding processing, such as "ringing" on the interface
             ...
        }
    }
    
  2. Call answer (opens new window) to answer the call, the video call can be answered by voice or video:

    // Get active calls
    JCCallItem* item = JCManager::shared()->call->getActiveCallItem();
    // Answer the call
    JCManager::shared()->call->answer(item, item->getVideo());
    

After the call is answered, the call status changes to JCCallStateConnecting.

TIP

If you want to reject the call at this time, you can go directly to the hang up part. After calling the hang up interface, the call state becomes JCCallStateCanceled.

# Create remote video images

After the called party answers the call, the two parties will establish a connection. At this time, both the caller and the called party will receive the updated callback of the call (onCallItemUpdate), and the call state will change to the JCCallStateTalking.

Call the startOtherVideo (opens new window) in the JCCallItem (opens new window) class to create a remote video streaming. This method returns a JCMediaDeviceVideoCanvas (opens new window) object, which is used to render the video to the canvas and manage the rendering method:

void JCManager::onCallItemUpdate(JCCallItem* item, JCCallItemChangeParam changeParam) {
    JCMediaDeviceVideoCanvas *mCallRemoteCanvas;
    // If the peer is uploading a video streaming (uploadVideoStreamOther)
    if (mCallRemoteCanvas == NULL && item->getUploadVideoStreamOther())
    {
      // Remote video rendering
      mCallRemoteCanvas = item->startOtherVideo(mWndCallRemoteVideo.m_hWnd, (JCMediaDeviceRenderMode)JCMediaDeviceRenderModeFullContent);
    }
}

# Hang up a call

Both the calling party and the called party can hang up the call.

  1. First call getActiveCallItem (opens new window) to get the currently active call object;

  2. After obtaining the current active call object, call term (opens new window) to hang up the current active call:

    void JCSampleDlg::OnBnClickedButtonTermcall()
    {
      // 1. Get the current active call
      JCCallItem* item = JCManager::shared()->call->getActiveCallItem();
      if (item != NULL)
      {
        // 2. Hang up the current active call
        JCManager::shared()->call->term(item, JCCallReasonNone, "term");
      }
    }
    

# Destroy local and remote video images

After the call is hung up, the onCallItemRemove callback in JCCallCallback (opens new window) will be triggered, and the call state will change to JCCallStateOk. At this time, you need to call the stopSelfVideo (opens new window) and stopOtherVideo (opens new window) methods to destroy the local and remote video images:

void JCManager::onCallItemRemove(JCCallItem* item, JCCallReason reason, const char* description) { //This callback triggers when the call is removed callback
  // Local video destruction
  if (mCallLocalCanvas != NULL && !item->getUploadVideoStreamSelf())
  {
    item->stopSelfVideo();
    mCallLocalCanvas = NULL;
    mWndCallLocalVideo.Invalidate();
  }
  // Remote video destruction
  if (mCallRemoteCanvas != NULL && !item->getUploadVideoStreamOther())
  {
    item->stopOtherVideo();
    mCallRemoteCanvas = NULL;
    mWndCallRemoteVideo.Invalidate();
  }
}

Now, you have completed the basic function of one-to-one video calling.