Implement your first group video call
PlanetKit lets you integrate audio and video call functionality for 1-to-1 calls or group calls into your app. This guide shows how to get started with the implementation of a group video call in your Windows app.
- For faster development, you can implement your app on top of our quick start.
- In this getting started guide, we provide an example of using MFC as the application framework.
Prerequisites
- Make sure that your system meets the system requirements.
- Create an API key. For more information, see Development environment.
- Implement app server code to generate an access token. For more information, see How to generate access tokens.
- Install Visual Studio 2022 or higher and the following components. (If Visual Studio is already installed, you can install components by navigating to Tools > Get Tools and Features.)
- Desktop development with C++ workload: On the Workloads tab, select Desktop development with C++.
- C++ MFC for latest v143 build tools (x86 & x64): On the Individual components tab, select C++ MFC for latest v143 build tools (x86 & x64).
- Download the PlanetKit SDK through PlanetKit Windows.
Create a project
To get started, open Visual Studio and create a new project as follows:
- From the main menu, select File > New > Project.
- In the project creation window, select a project template from the list. To create an MFC application, select MFC App.
- In Project name, enter your application project name. Click Create.
- In the MFC Application window, under Application Type, select Dialog based in the Application type dropdown, and click Finish.
Install the SDK
Install the SDK in your project as follows:
- Right-click the project name in Solution Explorer, and click Properties to configure the following project properties.
- Go to C/C++ > General > Additional Include Directories, click Edit..., and select the location of the
include
directory of the PlanetKit SDK. - Go to Linker > General > Additional Library Directories, click Edit..., and select the location of the
bin
directory of the PlanetKit SDK. - Go to Linker > Input > Additional Dependencies, click Edit..., and enter the name of the PlanetKit library depending on the platform used for the development.
- If the platform is x64, enter
PlanetKit64.lib
. - If the platform is Win32, enter
Planetkit.lib
.
- If the platform is x64, enter
- Go to C/C++ > General > Additional Include Directories, click Edit..., and select the location of the
- Click OK.
Initialize the SDK
To call the PlanetKit APIs, you must initialize PlanetKit first. Initialize PlanetKit using PlanetKitManager::Initialize()
with a Configuration
object.
After that, you must set the server URL (planet_base_url
) with PlanetKitManager::UpdateServerUrl()
. Make sure that you use an appropriate planet_base_url
depending on the development environment that you're using.
void YourApplication::InitializePlanetkit()
{
PlanetKit::ConfigurationPtr pConfiguration = PlanetKit::Configuration::Create(L"./", L"./");
PlanetKit::PlanetKitManager::Initialize(pConfiguration);
PlanetKit::PlanetKitManagerPtr pPlanetKitManager = PlanetKit::PlanetKitManager::GetInstance();
pPlanetKitManager->UpdateServerUrl(planet_base_url);
}
The PlanetKitManager::Initialize()
method must be called once initially in your app. For a dialog-based MFC application, it is recommended to initialize the SDK in the override function of OnInitDialog()
in your app.
Get the access token
In the client app, request your app server to generate an access token and get the generated access token.
You must get a new access token and use it each time you call PlanetKitManager::JoinConference()
.
Join a group video call
To join a group video call, implement callbacks of the IConferenceEvent
interface and call PlanetKitManager::JoinConference()
with a ConferenceParamPtr
object.
class YourConferenceEventListener : public IConferenceEvent {
public:
// This is called when the call is connected.
// Write your own code in function.
void OnConnected(PlanetKitConferencePtr pPlanetKitConference, ConferenceConnectedParamPtr pConnectedParam);
// This is called when the call is disconnected.
// Write your own code in function.
void OnDisconnected(PlanetKitConferencePtr pPlanetKitConference, ConferenceDisconnectedParamPtr pDisconnectedParam);
// This is called when the list of peers is updated.
// Write your own code here.
void OnPeerListUpdate(PlanetKitConferencePtr pPlanetKitConference, ConferencePeerUpdateParamPtr pParam);
// This is called when the video of one or more peers is updated.
// Write your own code in function.
void OnPeersVideoUpdated(PlanetKitConferencePtr pPlanetKitConference, ConferenceVideoUpdatedParamPtr pParam);
...
//
// Also, you should implement other override functions.
//
}
class YourApplication {
private :
// Prepare callback event listener instance.
PlanetKit::SharedPtr<YourConferenceEventListener> m_pYourConferenceEventListener = PlanetKit::MakeAutoPtr<YourConferenceEventListener>();
// Prepare PlanetKitConference instance.
PlanetKit::PlanetKitConferencePtr m_pConference;
};
void YourApplication::JoinConferenceExample(const std::wstring strRoomId, const std::wstring strAccessToken) {
// Prepare local user's ID.
std::wstring strUserId = "local user's user id";
std::wstring strServiceId = "local user's service id";
// Create the UserId object.
PlanetKit::UserIdPtr pUserId = PlanetKit::UserId::Create(strUserId.c_str(), strServiceId.c_str());
// Create MakeCallParam with Create API.
PlanetKit::ConferenceParamPtr pConferenceParam = PlanetKit::ConferenceParam::CreateWithAccessToken(
pUserId,
strRoomId.c_str(),
strServiceId.c_str(),
strAccessToken.c_str()
);
// Set required parameter.
pConferenceParam->SetConferenceEvent(m_pYourConferenceEventListener);
// Set the call type to video call
pConferenceParam->SetIsVideoCall(true);
// Now you have to create a PlanetKit::PlanetKitConference type object
// through the PlanetKit::PlanetKitManager::JoinConference() function.
// Please note that the PlanetKit::PlanetKitConference type object is the main call instance
// that controls call-related functions after the call setup completion.
PlanetKit::PlanetKitManagerPtr pPlanetKitManager = PlanetKit::PlanetKitManager::GetInstance();
// The currently used microphone device must be passed to JoinConference() as an argument.
// If the microphone in use has not been set, set it using the ChangeMic() of AudioManager.
// If the microphone is not set, PlanetKit::NullOptional will be retrieved by GetCurrentMic().
// Passing PlanetKit::NullOptional to JoinConference() will start the conference without a microphone device.
auto pMic = pPlanetKitManager->GetAudioManager()->GetCurrentMic();
PlanetKit::SStartResult sStartResult = pPlanetKitManager->JoinConference(pConferenceParam, pMic, &m_pConference);
if (sStartResult.bSuccess == false) {
// Handle an error by referring to sStartResult.reason
}
}
Users need the room ID to enter the room from the client app, so the room ID must be shared with other users through an application-defined communication channel.
Render a video view for the local user
There are two ways to display the local user's video: using an HWND
or receiving raw video frames.
Using an HWND
Prepare your HWND
to render the local user's video and add the HWND
to PlanetKitConference
using PlanetKitConference::AddMyVideoView()
.
class YourApplication {
public:
void AddMyVideoView(HWND hMyVideoWindow) {
m_pConference->AddMyVideoView(hMyVideoWindow);
}
}
Using raw video frames
Declare an IVideoReceiver
class for receiving raw video frames.
class YourVideoReceiver : public PlanetKit::IVideoReceiver {
public:
void OnVideo(const SVideoFrame& pVideoFrame, UserIdPtr pUserID) {
// Render video using raw video data here.
}
}
Create an instance of YourVideoReceiver
and add it to PlanetKitConference
using PlanetKitConference::AddMyVideoReceiver()
.
class YourApplication {
private:
// Create an instance of YourVideoReceiver.
PlanetKit::SharedPtr<YourVideoReceiver> m_yourVideoReceiver = PlanetKit::MakeAutoPtr<YourVideoReceiver>();
public:
void AddMyVideoReceiver() {
m_pConference->AddMyVideoReceiver(m_yourVideoReceiver);
}
}
Render a video view for the remote user
There are also two ways to display the remote user's video: using an HWND
or receiving raw video frames.
Create a PeerControl
instance
Declare an IPeerControlEvent
class for handling the remote user's video state.
class YourPeerControlEvent : public PlanetKit::IPeerControlEvent {
/// Called when a peer is initialized.
void OnInitialized(PlanetKit::PeerControlPtr pPeerControl, bool bResult) override;
/// Called when a peer's video status is updated.
void OnVideoUpdated(PlanetKit::PeerControlPtr pPeerControl, PlanetKit::SubgroupPtr pSubgroup, const PlanetKit::VideoStatus& videoStatus) override;
...
}
Create an instance of PeerControl
using PlanetKit::Peer::CreatePeerControl()
.
class YourConferenceEventListener : public PlanetKit::IConferenceEvent {
private:
YourApplication* m_pYourApplication;
public:
// This is called when the list of peers is updated.
// Write your own code here.
void OnPeerListUpdate(PlanetKitConferencePtr pPlanetKitConference, ConferencePeerUpdateParamPtr pParam) {
// Call event callback API of owner class instance or dispatch event.
m_pYourApplication->OnPeerListUpdate(pPlanetKitConference, pParam);
}
};
// Example of a custom data structure for a remote user
class PeerContainer {
private:
PlanetKit::PeerPtr m_pPeer;
PlanetKit::PeerControlPtr m_pPeerControl;
PlanetKit::SharedPtr<IPeerControlEvent> m_pPeerControlEvent = PlanetKit::MakeAutoPtr<YourPeerControlEvent>();
public:
PeerContainer(PlanetKit::PeerPtr pPeer) {
m_pPeer = pPeer;
// Create PeerControl instance
m_pPeerControl = m_pPeer->CreatePeerControl();
// Register event class for handling the remote user's video state
m_pPeerControl->Register(m_pPeerControlEvent);
}
};
class YourApplication {
private:
std::map<std::wstring, PeerContainer*> m_remoteUsers;
public:
void OnPeerListUpdate(PlanetKitConferencePtr pPlanetKitConference, ConferencePeerUpdateParamPtr pParam) {
// Example of handling a remote user
auto const& arrAdded = pParam->GetAddedPeer();
for (int index = 0; index < arrAdded.Size(); ++index) {
auto pPeer = arrAdded.At(index);
PeerContainer* pPeerContainer = new PeerContainer(pPeer);
m_remoteUsers[pPeer->GetUserID()->GetID().c_str()] = pPeerContainer;
}
}
};
Display the remote user's video
Using an HWND
Prepare your HWND
to render the remote user's video and set the HWND
to PeerControl
using PeerControl::SetView()
.
class PeerContainer {
public:
void SetView(HWND hRemoteVideoWindow) {
m_pPeerControl->SetView(hRemoteVideoWindow);
}
}
Using raw video frames
Declare an IVideoReceiver
class for receiving raw video frames.
class YourVideoReceiver : public PlanetKit::IVideoReceiver {
public:
void OnVideo(const SVideoFrame& pVideoFrame, UserIdPtr pUserID) {
// Render video using raw video data here.
}
}
Create an instance of YourVideoReceiver
and register it to PlanetKitConference
using PeerControl::RegisterReceiver()
.
class PeerContainer {
private:
// Create an instance of YourVideoReceiver.
PlanetKit::SharedPtr<YourVideoReceiver> m_yourVideoReceiver = PlanetKit::MakeAutoPtr<YourVideoReceiver>();
public:
void RegisterRemoteVideoReceiver() {
// Register video receiver instance
m_pPeerControl->RegisterReceiver(m_yourVideoReceiver);
}
}
Start or stop rendering the remote user's video
Start or stop rendering the remote user's video using PeerControl::StartVideo()
or PeerControl::StopVideo()
.
class PeerContainer {
public:
void StartVideo() {
m_pPeerControl->StartVideo(PlanetKit::NullOptional, PlanetKit::EVideoResolution::PLNK_VIDEO_RESOLUTION_RECOMMENDED);
}
void StopVideo() {
m_pPeerControl->StopVideo();
}
}
Handle the remote user's video state
Handle the remote user's video state through the IPeerControlEvent
instance.
class YourPeerControlEvent : public PlanetKit::IPeerControlEvent {
/// Called when a peer's video status is updated.
void OnVideoUpdated(PlanetKit::PeerControlPtr pPeerControl, PlanetKit::SubgroupPtr pSubgroup, const PlanetKit::VideoStatus& videoStatus) {
switch (videoStatus) {
case PlanetKit::EVideoState::PLNK_VIDEO_STATE_DISABLED:
// Disabled remote user's video
break;
case PlanetKit::EVideoState::PLNK_VIDEO_STATE_ENABLED:
// Enabled remote user's video
break;
case PlanetKit::EVideoState::PLNK_VIDEO_STATE_PAUSED:
// Paused remote user's video
break;
}
}
}
Next steps
See the following documents to learn more about the various features provided by PlanetKit and how to use each feature.
- Call flow: Familiarize yourself with the call flow for each call type.
- Subgroup: Read the guides on subgroups, which allows you to implement advanced features such as multi-subgroup rooms or translation rooms.
- Extended functions: Explore the guides on extended functions, such as screen share and data sessions.
- Code examples: See the code examples that you can use for implementing your own app.
- Reference: Refer to the API reference, API changelog, and release notes.