Linux (Java)

更新时间: 2026-01-18 18:54:50

This topic describes the basic features of DingRTC, including software development kit (SDK) initialization, joining a channel, publishing local streams, subscribing to remote streams, and leaving a channel.

Procedure

Note The implementation methods in this topic are for reference only. You must develop your application based on your specific requirements.
  1. Initialize the SDK.

    Create an RtcEngine instance and register a callback.

    // Set the log path.
    DingRtcEngine.setLogDirPath(logDir);
    
    // Implement and create a listener.
    // Implement callbacks as needed. For example:
    // Determine whether joining or leaving a channel and stream ingest or pulling is successful based on the corresponding callback.
    class MyListener extends DingRtcEngineListener {
      ....
    }
    MyListener listener = new MyListener();
    
    // Create an engine.
    DingRtcEngine engine = DingRtcEngine.createInstance(listener, "");
    
    // Unsubscribe from streams to reduce resource consumption when you do not need to subscribe to any stream.
    engine.subscribeAllRemoteAudioStreams(false);
    engine.subscribeAllRemoteVideoStreams(false);
    
    // Enable audio and video stream ingest.
    // Note: You can also call this method after you join a channel.
    engine.publishLocalAudioStream(true);
    engine.publishLocalVideoStream(true);
  2. Join a channel.

    To join a channel, you must obtain an authentication token. The token is usually generated by your application server. For more information, see Use a token for authentication.

    public static class DingRtcAuthInfo {
        /*! The channel ID. */
        public String channelId;
        /*! The user ID. */
        public String userId;
        /*! The application ID. */
        public String appId;
        /*! The token. */
        public String token;
        /*! The GSLB endpoint. Pass an empty string (""). */
        public String gslb;
    }
    
    // Obtain authentication information. getAuthInfoFromAppServer is implemented by the client.
    DingRtcEngine.DingRtcAuthInfo authInfo = getAuthInfoFromAppServer();
    
    // Join a channel.
    // To check whether you have successfully joined the channel, see the onJoinChannelResult callback of the listener.
    engine.joinChannel(authInfo, "John Doe");

    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 (-).

    Note

    If 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).

  3. Publish the local audio stream.

    After you join a channel, the SDK does not ingest streams by default. You can call an API to enable stream ingest, either before or after you join the channel.

    • Enable audio stream ingest

      // Enable audio ingest. If you have already called this method, you do not need to call it again. You can call this method before you join a channel.
      engine.publishLocalAudioStream(true);
    • Ingest an external audio stream

      • If you use a Linux server or a device without a microphone for audio capture, you can use the external audio source interface to push audio data.

      // Set the external audio file and audio parameters for stream ingest.
      // pcmSampleRate: The sample rate of the audio file. For example, 48000 (48k sample rate).
      // pcmChannels: The number of sound channels in the audio file. For example, 1 or 2 (mono or stereo).
      engine.setExternalAudioSource(true, pcmSampleRate, pcmChannels);
      
      // Generate frame data.
      DingRtcEngine.DingRtcAudioFrame frame = new DingRtcEngine.DingRtcAudioFrame();
      frame.bytesPerSample = 2;
      frame.samplesPerSec = pcmSampleRate;
      frame.numChannels = pcmChannels;
      frame.buffer = buffer;
      frame.numSamples = numChannels;
      frame.timestamp = timestamp;
      
      // Push the frame data.
      engine.pushExternalAudioFrame(frame);
      • You can use a separate thread to push audio data in a loop. This ensures that the data is evenly input to the SDK at regular intervals. This process is similar to how a microphone captures and inputs audio data to the SDK at a fixed epoch.

      static class RtcContext {
        // audio config
        String pcmFilePath;
        int pcmSampleRate = 16000;
        int pcmChannels = 1;
        int pcmReadFreq = 40;
        volatile boolean isPushAudioStarted = false;
        volatile boolean externalAudioFinished = false;
        Thread externalAudioThread;
      }
      
      private static void startPushAudio(RtcContext context) {
        if (context.isPushAudioStarted) {
            return;
        }
        context.isPushAudioStarted = true;
        if (context.engine != null) {
            // Enable stream ingest.
            context.engine.publishLocalAudioStream(true);
            // Set PCM audio stream ingest.
            context.engine.setExternalAudioSource(true, context.pcmSampleRate, context.pcmChannels);
        }
        context.externalAudioThread = new Thread(() -> {
            DingRtcEngine.DingRtcAudioFrame frame = new DingRtcEngine.DingRtcAudioFrame();
            try (RandomAccessFile file = new RandomAccessFile(context.pcmFilePath, "r")) {
                long sampleCount = 0;
                // 16-bit PCM, 2 bytes
                int bytesPerSample = 2;
                int samplesToRead = context.pcmSampleRate / (1000 / context.pcmReadFreq);
                int bufferSize = samplesToRead * context.pcmChannels * bytesPerSample;
                byte[] buffer = new byte[bufferSize];
                long delay = 0;
                long startClock = System.currentTimeMillis();
                long lastStatsClock = startClock;
                while (!context.quit) {
                    int readBytes = file.read(buffer);
                    if (readBytes != bufferSize) {
                        // After the file is read, exit directly.
                        // break;
        
                        // If you want to read in a loop
                        file.seek(0);
                        continue;
                    }
                    frame.bytesPerSample = bytesPerSample;
                    frame.samplesPerSec = context.pcmSampleRate;
                    frame.numChannels = context.pcmChannels;
                    frame.buffer = buffer;
                    frame.numSamples = readBytes / frame.bytesPerSample / frame.numChannels;
                    frame.timestamp = sampleCount * 1000 / context.pcmSampleRate;
        
                    delay = frame.timestamp;
                    long elapsed = System.currentTimeMillis() - startClock;
                    if (delay - elapsed > 5) {
                        sleep(delay - elapsed);
                    }
        
                    if (frame.numSamples > 0) {
                        context.engine.pushExternalAudioFrame(frame);
                        sampleCount += frame.numSamples;
                    }
        
                    long elpasedStats = System.currentTimeMillis() - lastStatsClock;
                    if (elpasedStats >= 2000) {
                        log("[Demo] audioPushThread pushExternalAudioFrame sampleCount:", sampleCount);
                        lastStatsClock = System.currentTimeMillis();
                    }
                }
            } catch (Exception e) {
                log("[Demo] pushAudio error:", e.toString());
            } finally {
                context.externalAudioFinished = true;
            }
        }, "pub-audio");
        context.externalAudioThread.start();
        }
    • Stop audio stream ingest

      // Disable the external audio source.
      engine.setExternalAudioSource(false, pcmSampleRate, pcmChannels);
      
      // Disable audio stream ingest.
      engine.publishLocalAudioStream(false);
  4. Publish the local video stream.

    After you join a channel, the SDK does not ingest streams by default. You can call an API to enable stream ingest, either before or after you join the channel.

    • Enable video stream ingest

      // Enable video ingest. If you have already called this method, you do not need to call it again. You can call this method before you join a channel.
      engine.publishLocalVideoStream(true);
      
    • Ingest an external video stream

      • If you use a Linux server or a device without a camera for video capture, you can use the external video source interface to push video data.

      // Set the external video file and stream type for stream ingest.
      // The track parameter supports only DingRtcVideoTrackCamera and DingRtcVideoTrackScreen.
      // External stream ingest does not support DingRtcVideoTrackBoth.
      engine.setExternalVideoSource(true, DingRtcEngine.DingRtcVideoTrack.DingRtcVideoTrackCamera);
      
      // Generate frame data.
      DingRtcEngine.DingRtcVideoFrame frame = new DingRtcEngine.DingRtcVideoFrame();
      frame.format = context.pixelFormat;
      frame.width = context.videoWidth;
      frame.height = context.videoHeight;
      frame.lineSize[0] = context.videoWidth;
      frame.lineSize[1] = context.videoWidth >> 1;
      frame.lineSize[2] = context.videoWidth >> 1;
      frame.rotation = context.videoRotation;
      frame.videoFrameLength = bufferSize;
      frame.buffer = buffer;
      
      // bufferSize
      // YUV(I420): buffer_size = width * height + half_width * half_height * 2;
      // RGBA:      buffer_size = width * height * 4;
      
      // Push the frame data.
      engine.pushExternalAudioFrame(frame);
      • You can use a separate thread to push video data in a loop. This ensures that the data is evenly input to the SDK at regular intervals. This process is similar to how a camera captures and inputs video data to the SDK at a fixed interval.

      static class RtcContext {
        // video config
        String videoYUVFilePath;
        DingRtcEngine.DingRtcVideoFormat videoPixelFormat = DingRtcEngine.DingRtcVideoFormat.DingRtcVideoI420;
        int videoWidth = 0;
        int videoHeight = 0;
        int videoRotation = 0;
        int videoFps = 25;
        volatile boolean isPushVideoStarted = false;
        volatile boolean externalVideoFinished = false;
        Thread externalVideoThread;
      }
      
      private static void startPushVideo(RtcContext context) {
          if (context.isPushVideoStarted) {
              return;
          }
          context.isPushVideoStarted = true;
          if (context.engine != null) {
              // Enable stream ingest.
              context.engine.publishLocalVideoStream(true);
              // Set video stream ingest.
              context.engine.setExternalVideoSource(true, DingRtcEngine.DingRtcVideoTrack.DingRtcVideoTrackCamera);
          }
          context.externalVideoThread = new Thread(() -> {
              try (RandomAccessFile file = new RandomAccessFile(context.videoYUVFilePath, "r")) {
                  long frameCount = 0;
                  DingRtcEngine.DingRtcVideoFormat pixelFormat = context.videoPixelFormat;
                  int bufferSize = calcBufferSize(pixelFormat, context.videoWidth, context.videoHeight);
      
                  long delay = 0;
                  long startClock = System.currentTimeMillis();
                  long lastStatsClock = startClock;
                  byte[] buffer = new byte[bufferSize];
                  DingRtcEngine.DingRtcVideoFrame frame = new DingRtcEngine.DingRtcVideoFrame();
                  while (!context.quit) {
                      long readStartClock = System.currentTimeMillis();
                      int readBytes = file.read(buffer);
                      if (readBytes != bufferSize) {
                          // After the file is read, exit directly.
                          // break;
      
                          // If you want to read in a loop
                          file.seek(0);
                          continue;
                      }
                      frame.format = pixelFormat;
                      frame.width = context.videoWidth;
                      frame.height = context.videoHeight;
                      frame.lineSize[0] = context.videoWidth;
                      frame.lineSize[1] = context.videoWidth >> 1;
                      frame.lineSize[2] = context.videoWidth >> 1;
                      frame.rotation = context.videoRotation;
                      frame.videoFrameLength = bufferSize;
                      frame.buffer = buffer;
                      frame.timestamp = frameCount * 1000 / context.videoFps;
      
                      delay = frame.timestamp;
                      long elapsed = System.currentTimeMillis() - startClock;
                      if (delay - elapsed > 5) {
                          sleep(delay - elapsed);
                      }
      
                      context.engine.pushExternalVideoFrame(frame, DingRtcEngine.DingRtcVideoTrack.DingRtcVideoTrackCamera);
                      frameCount++;
      
                      long elpasedStats = System.currentTimeMillis() - lastStatsClock;
                      if (elpasedStats >= 5000) {
                          log("[Demo] videoPushThread pushExternalVideoFrame frameCount:", frameCount);
                          lastStatsClock = System.currentTimeMillis();
                      }
                  }
              } catch (Exception e) {
                  log("[Demo] pushVideo error:", e.toString());
              } finally {
                  context.externalVideoFinished = true;
              }
          }, "pub-video");
          context.externalVideoThread.start();
      }
    • Stop video stream ingest

      // Disable the external video source.
      engine.setExternalVideoSource(false, DingRtcEngine.DingRtcVideoTrack.DingRtcVideoTrackCamera);
      
      // Disable video stream ingest.
      engine.publishLocalVideoStream(false);

  5. Subscribe to a remote audio stream.

    By default, the SDK automatically subscribes to and pulls remote audio and video streams. To disable stream pulling, you can call the corresponding API. You can also call this API before you join a channel.

    • Subscribe to an audio stream

      The SDK does not support subscribing to the audio stream of a specified user. You can only subscribe to a mixed audio stream, which is a mix of audio from all remote users.

      // Subscribe to the audio stream. You can call this method before you call joinChannel.
      engine.subscribeAllRemoteAudioStreams(true);
      
      // Unsubscribe from the audio stream. You can call this method before you call joinChannel.
      engine.subscribeAllRemoteAudioStreams(false);
    • Listen for audio frame data

      class MyAudioFrameObserver extends DingRtcAudioFrameObserver {
        ...
      
        public boolean onPlaybackAudioFrame(DingRtcAudioFrame frame) {
          // The PCM data to be played back after the audio from remote users is mixed.
          return false;
        }
      }
      
      // Register an audio data callback object.
      engine.registerAudioFrameObserver(new MyAudioFrameObserver());
      // Enable the playback data callback (audio from remote users).
      engine.enableAudioFrameObserver(true, DingRtcEngine.DingRtcAudioSource.DingRtcAudioSourcePlayback);
      
      // Cancel the audio data callback.
      engine.enableAudioFrameObserver(false, DingRtcEngine.DingRtcAudioSource.DingRtcAudioSourcePlayback);
      engine.registerAudioFrameObserver(null);
    • Listen for the speaker volume

      class MyRtcListener extends DingRtcEngineListener {
        ...
      
        @Override
        public void onAudioVolumeIndication(DingRtcEngine.DingRtcAudioVolumeInfo[] speakers, int speakerNumber) {
            // remote user volume info
        }
        ...
      }
      
      // Enable the volume callback.
      engine.enableAudioVolumeIndication(300, 3, 1);
      
      // Disable the volume callback.
      engine.enableAudioVolumeIndication(0, 3, 1);

  6. Subscribe to a remote video stream.

    • Subscribe to a video stream

      // Method 1: Subscribe to all remote audio streams. (You can call this method before calling joinChannel.)
      engine.subscribeAllRemoteAudioStreams(true);
      
      // Method 2: Subscribe to the video stream of a specific user.
      engine.subscribeRemoteVideoStream(uid, track, true);
    • Listen for video frame data

      class MyVideoFrameObserver extends DingRtcVideoFrameObserver {
        ...
      
        public boolean onCaptureVideoFrame(DingRtcVideoSample dingRtcVideoSample) {
          // The locally captured video data.
          return false;
        }
      
        public boolean onRemoteVideoFrame(String str, DingRtcVideoTrack dingRtcVideoTrack, DingRtcVideoSample dingRtcVideoSample) {
          // The video data of the remote user.
          return false;
        }
      
        public boolean onPreEncodeVideoFrame(DingRtcVideoTrack dingRtcVideoTrack, DingRtcVideoSample dingRtcVideoSample) {
          // The local video data before encoding. Compared with the locally captured video data, this data has been pre-processed.
          return false;
        }
      
        public DingRtcVideoFormat getVideoFormatPreference() {
          // Only I420 is supported.
          return DingRtcVideoFormat.DingRtcVideoI420;
        }
      }
      
      // Register a video data callback object.
      engine.registerVideoFrameObserver(new MyVideoFrameObserver());
      // Enable the video data callback (video from remote users).
      engine.enableVideoFrameObserver(true, DingRtcEngine.DingRtcVideoObservePosition.DingRtcPositionPreRender);
      
      // Cancel the video data callback.
      engine.enableVideoFrameObserver(false, DingRtcEngine.DingRtcVideoObservePosition.DingRtcPositionPreRender);
      engine.registerVideoFrameObserver(null);

  7. Leave the channel.

    // Leave the channel.
    engine.leaveChannel();

  8. Destroy the engine.

// Destroy the SDK.
engine.destroy();
上一篇: Electron 下一篇: Linux (C++)
阿里云首页 音视频通信 相关技术圈