Linux (C++)
This topic describes the basic features of DingRTC, which include initializing the software development kit (SDK), joining a channel, publishing local streams, subscribing to remote streams, and leaving a channel.
Procedure
Initialize the SDK.
Create an RtcEngine instance and register a callback.
// Set the log path. RtcEngine::SetLogDirPath(logDirPath); // Create an engine instance. RtcEngine * engine = RtcEngine::Create(extras); // Implement RtcEngineEventListener as needed and set the event callback. engine->SetEngineEventListener(listener);Join a channel.
To join a channel, you must have an authentication token. Your application server typically generates this token. For more information, see Token-based authentication.
struct RtcEngineAuthInfo { /*! The channel ID. */ String channelId; /*! The user ID. */ String userId; /*! The application ID. */ String appId; /*! The token. */ String token; /*! The GSLB server address. */ String gslbServer; }; // Obtain the token. RtcEngineAuthInfo authInfo = getYourAuthInfo(); // Join the channel. engine->JoinChannel(authInfo, userName);Parameter
Description
appId
The application ID. You can create and view the application ID on the Application Management page in the console.
channelId
The channel ID. It must be 1 to 64 characters in length and can contain uppercase letters, lowercase letters, digits, underscores (_), and hyphens (-).
userId
The user ID. It must be 1 to 64 characters in length and can contain uppercase letters, lowercase letters, digits, underscores (_), and hyphens (-).
NoteIf a user logs on from another client with the same user ID, the client that joined the channel first is removed from the channel.
token
The token for channel authentication.
gslbServer
The service endpoint. This parameter can be empty. The default value is
"https://gslb.dingrtc.com". We recommend that you obtain the endpoint from your business server and pass it to the client SDK instead of hardcoding it in the client.Important: We strongly recommend that you do not pass an empty value. Use the GSLB address returned from your business server (AppServer).
Publish the local audio stream.
After you join a channel, the SDK does not publish streams by default. You can call an API method to start publishing. You can also call this method before you join the channel.
Enable audio stream publishing
// Enable audio stream publishing. You can call this method before JoinChannel. engine->PublishLocalAudioStream(true);Publish an external audio stream
If you are using a Linux server or a device without a microphone for audio capture, you can use the external audio source interface to push audio data.
// Enable the external audio source and set parameters such as the audio sampling rate and the number of sound channels. engine->SetExternalAudioSource(true, sampleRate, channels);You typically need to create a separate thread to push audio data in a loop. This ensures that the data is sent to the SDK at a consistent interval, which is similar to how a microphone captures and sends audio data to the SDK at a fixed frequency.
static const int kBytePerSamplePcm16 = 2; // The number of bytes per sample for 16-bit audio. std::string pcm_file_path = "/path/to/my_audio.pcm"; // The path to the PCM file. int audio_sample_rate = 48000; // The sample rate. int audio_channels = 1; // The number of sound channels. int read_ms = 40; // Read 40 ms of audio data at a time. bool push_audio_quit = false; // The quit flag. // The audio publishing thread. It reads audio data from a PCM file and feeds it to the SDK for publishing at a consistent interval. // This simulates an audio capture device that collects audio data at a fixed frequency. std::thread push_audio_thread = std::thread([=]{ // Open the file. FILE *fp = fopen(pcm_file_path.c_str(), "rb"); if (fp) { // Count the number of audio frames to calculate the audio timestamp. int64_t sample_count = 0; // The audio delay. int64_t delay_ms = 0; // The starting system clock. auto start_clock = std::chrono::high_resolution_clock::now(); // The number of audio samples to read at a time. int samples_to_read = audio_sample_rate / (1000 / read_ms); // The number of audio bytes to read at a time. int buffer_size = samples_to_read * audio_channels * kBytePerSamplePcm16; // Allocate a memory buffer to store the read data. std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); // Enter the loop publishing stage. while (true) { // Exit publishing when the quit flag is true. if (push_audio_quit) break; size_t read_bytes = fread(buffer.get(), 1, buffer_size, fp); // If the end of the file is reached, start reading from the beginning. if (read_bytes == 0) { fseek(fp, 0, SEEK_SET); continue; } // Fill the audio frame fields. RtcEngineAudioFrame frame; frame.type = RtcEngineAudioFramePcm16; frame.bytesPerSample = kBytePerSamplePcm16; frame.samplesPerSec = audio_sample_rate; frame.channels = audio_channels; frame.buffer = buffer.get(); frame.samples = read_bytes / frame.bytesPerSample / frame.channels; frame.timestamp = sample_count * 1000 / audio_sample_rate; // Calculate the difference between the audio delay and the system clock. Use sleep to control the input frequency. delay_ms = frame.timestamp; int64_t elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start_clock).count(); if (delay_ms - elapsed_ms > 5) { std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms - elapsed_ms)); } engine->PushExternalAudioFrame(&frame); sample_count += frame.samples; } // Close the file. fclose(fp); } });Stop publishing the audio stream
// Disable the external audio source. engine->SetExternalAudioSource(false, sampleRate, channels); // Stop publishing the audio stream. engine->PublishLocalAudioStream(false);
Publish the local video stream.
After you join a channel, the SDK does not publish streams by default. You can call an API method to start publishing. You can also call this method before you join the channel.
Enable video stream publishing
// Enable video stream publishing. You can call this method before JoinChannel. engine->PublishLocalVideoStream(true);Publish an external video stream
If you are using a Linux server or a device without a camera for video capture, you can use the external video source interface to push video data.
// Enable the external video source to replace the camera stream. engine->SetExternalVideoSource(true, RtcEngineVideoTrackCamera);You typically need to create a separate thread to push video data in a loop. This ensures that the data is sent to the SDK at a consistent interval, which is similar to how a camera captures and sends video data to the SDK at a fixed frequency.
std::string yuv_file_path = "/path/to/my_yuv.yuv"; // The path to the YUV file. RtcEngineVideoPixelFormat video_pixel_format = RtcEngineVideoI420; // The video pixel format. int video_width = 1280; // The video width. int video_height = 720; // The video height. int video_fps = 25; // The video frame rate. bool push_video_quit = false; // The quit flag. // The video publishing thread. It reads video data from a YUV file and feeds it to the SDK for publishing at a consistent interval. // This simulates a video capture device that collects video data at a fixed frequency. std::thread push_video_thread = std::thread([=] { FILE *fp = fopen(yuv_file_path.c_str(), "rb"); if (fp) { // Count the number of video frames to calculate the video timestamp. int64_t frame_count = 0; // The video delay. int64_t delay_ms = 0; // The starting system clock. auto start_clock = std::chrono::high_resolution_clock::now(); // The number of bytes in a video frame. int buffer_size = CalcBufferSize(video_pixel_format, video_width, video_height); // Allocate a memory buffer to store the read data. std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); // Enter the loop publishing stage. while (true) { // Exit publishing when the quit flag is true. if (push_video_quit) break; size_t read_bytes = fread(buffer.get(), 1, buffer_size, fp); // If the end of the file is reached, start reading from the beginning. if (read_bytes == 0) { fseek(fp, 0, SEEK_SET); continue; } // Configure the external video stream parameters. RtcEngineVideoFrame frame; memset(&frame, 0, sizeof(frame)); frame.frameType = RtcEngineVideoFrameRaw; frame.pixelFormat = video_pixel_format; frame.width = video_width; frame.height = video_height; frame.stride[0] = video_width; frame.stride[1] = frame.stride[2] = video_width >> 1; frame.rotation = config.rotation; frame.data = buffer.get(); frame.timestamp = frame_count * 1000 / video_fps; // Calculate the difference between the video delay and the system clock. Use sleep to control the input frequency. delay_ms = frame.timestamp; int64_t elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start_clock).count(); if (delay_ms - elapsed_ms > 5) { std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms - elapsed_ms)); } engine->PushExternalVideoFrame(&frame, RtcEngineVideoTrackCamera); frame_count++; } fclose(fp); } }); size_t CalcBufferSize(RtcEngineVideoPixelFormat pixel_format, int width, int height) { size_t buffer_size = 0; switch (pixel_format) { case RtcEngineVideoI420: case RtcEngineVideoNV12: case RtcEngineVideoNV21: { int half_width = (width + 1) >> 1; int half_height = (height + 1) >> 1; buffer_size = width * height + half_width * half_height * 2; break; } case RtcEngineVideoBGRA: case RtcEngineVideoARGB: case RtcEngineVideoRGBA: case RtcEngineVideoABGR: buffer_size = width * height * 4; break; default: break; } return buffer_size; }Stop publishing the video stream
// Disable the external video source. engine->SetExternalVideoSource(false, RtcEngineVideoTrackCamera); // Stop publishing the video stream. engine->PublishLocalVideoStream(false);
Subscribe to remote audio streams.
By default, the SDK automatically subscribes to remote audio and video streams. To stop subscribing to streams, you can call the relevant API method. You can also call this method before you join the channel.
Subscribe to audio streams
The SDK does not support subscribing to the audio stream of a specific user. You can only subscribe to the mixed audio stream, which contains the audio from all remote users.
// Subscribe to audio streams. You can call this method before JoinChannel. engine->SubscribeAllRemoteAudioStreams(true); // Unsubscribe from audio streams. You can call this method before JoinChannel. engine->SubscribeAllRemoteAudioStreams(false);Listen for audio frame data
// Implement RtcEngineAudioFrameObserver to receive audio data callbacks. class MyAudioFrameObserver : public ding::rtc::RtcEngineAudioFrameObserver { public: void OnPlaybackAudioFrame(ding::rtc::RtcEngineAudioFrame &frame) override {} void OnCapturedAudioFrame(ding::rtc::RtcEngineAudioFrame &frame) override {} void OnProcessCapturedAudioFrame(ding::rtc::RtcEngineAudioFrame &frame) override {} void OnPublishAudioFrame(ding::rtc::RtcEngineAudioFrame &frame) override {} }; MyAudioFrameObserver myObserver; // Register the audio data callback. engine->RegisterAudioFrameObserver(&myObserver); // Enable audio callbacks. You can specify the type of audio data callback to receive. engine->EnableAudioFrameObserver(true, RtcEngineAudioPositionPlayback); // You can specify to receive multiple types of data callbacks at the same time. engine->EnableAudioFrameObserver(true, RtcEngineAudioPositionCaptured | RtcEngineAudioPositionPub | RtcEngineAudioPositionPlayback); // Disable audio callbacks. engine->EnableAudioFrameObserver(false, RtcEngineAudioPositionPlayback); // Unregister the audio data callback. engine->RegisterAudioFrameObserver(nullptr);Listen for speaker volume
/** * @ingroup CPP_DingRtcEngineAudio * @since 3.0 * @brief Sets the volume callback frequency and smoothing coefficient. * @param interval The time interval in milliseconds. The minimum value is 100 ms. A value of 300 to 500 ms is recommended. A value less than or equal to 0 disables the volume and speaker prompts. * @param smooth The smoothing coefficient. A larger value indicates a higher degree of smoothing, while a smaller value provides better real-time performance. A value of 3 is recommended. The value ranges from 0 to 9. * @param reportVad The switch for speaker detection. * - 1: Enable. * - 0: Disable. * @return * - 0: Success. * - A value less than 0: Failure. */ virtual int EnableAudioVolumeIndication(int interval, int smooth, int reportVad) = 0; class MyEventListener : public RtcEngineEventListener { public: void OnAudioVolumeIndication(const ding::rtc::AudioVolumeInfo* speakers, unsigned int speakerNumber) override {} }; // Enable volume callbacks. The OnAudioVolumeIndication callback provides speaker volume information. engine->EnableAudioVolumeIndication(300, 3, 1); // Disable volume callbacks. engine->EnableAudioVolumeIndication(0, 3, 1);
Subscribe to remote video streams.
Subscribe to video streams
// Subscribe to the video streams of all remote users. You can call this method before JoinChannel. engine->SubscribeAllRemoteVideoStreams(true); // Unsubscribe from the video streams of all remote users. You can call this method before JoinChannel. engine->SubscribeAllRemoteVideoStreams(false); // Subscribe to the video stream of a specific remote user. engine->SubscribeRemoteVideoStream(uid, track, true); // Unsubscribe from the video stream of a specific remote user. You can call this method before JoinChannel. engine->SubscribeRemoteVideoStream(uid, track, false);Listen for video frame data
// Implement RtcEngineVideoFrameObserver to receive video data callbacks. class MyVideoFrameObserver : public ding::rtc::RtcEngineVideoFrameObserver { public: ding::rtc::RtcEngineVideoPixelFormat GetVideoFormatPreference() override; bool OnCaptureVideoFrame(ding::rtc::RtcEngineVideoFrame &frame) override; bool OnRemoteVideoFrame(ding::rtc::String uid, ding::rtc::RtcEngineVideoTrack track, ding::rtc::RtcEngineVideoFrame &frame) override; bool OnPreEncodeVideoFrame(ding::rtc::RtcEngineVideoTrack track, ding::rtc::RtcEngineVideoFrame &frame) override; }; MyVideoFrameObserver myObserver; // Register the video data callback. engine->RegisterVideoFrameObserver(&myObserver); // Enable video callbacks. You can specify the type of video data callback to receive. engine->EnableVideoFrameObserver(true, RtcEnginePositionPreRender); // You can specify to receive multiple types of data callbacks at the same time. engine->EnableAudioFrameObserver(true, RtcEnginePositionPostCapture | RtcEnginePositionPreRender | RtcEngineAudioPositionPlayback); // Disable video callbacks. engine->EnableVideoFrameObserver(false, RtcEnginePositionPreRender); // Unregister the video data callback. engine->RegisterVideoFrameObserver(nullptr);
Leave the channel.
// Leave the channel. engine->LeaveChannel();Destroy the RtcEngine instance.
// Unregister the event callback listener.
engine->SetEngineEventListener(nullptr);
// Destroy the SDK.
RtcEngine::Destroy(engine);