Skip to content

joinChannelEx always return -2 if use shared rtcEngine #2254

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
4 of 5 tasks
peilinok opened this issue Apr 1, 2025 · 1 comment
Closed
4 of 5 tasks

joinChannelEx always return -2 if use shared rtcEngine #2254

peilinok opened this issue Apr 1, 2025 · 1 comment
Assignees
Labels
bug Something isn't working

Comments

@peilinok
Copy link
Contributor

peilinok commented Apr 1, 2025

Version of the agora_rtc_engine

6.3.0
6.5.0
6.5.1

Platforms affected

  • Android
  • iOS
  • macOS
  • Windows
  • Web

Steps to reproduce

  1. replace RtcEngine with RtcEngineEx in process_video_raw_data.dart
  2. replace createAgoraRtcEngine with createAgoraRtcEngineEx in process_video_raw_data.dart
  3. replace joinChannel with joinChannelEx in process_video_raw_data.dart

Expected results

joinChannel success

Actual results

joinChannelEx always return -2

Code sample

Code sample
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:agora_rtc_engine_example/config/agora.config.dart' as config;
import 'package:agora_rtc_engine_example/components/example_actions_widget.dart';
import 'package:agora_rtc_engine_example/components/log_sink.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

/// ProcessVideoRawData Example
///
/// This example demonstrates how to create a `RtcEngine` (Android)/`AgoraRtcEngineKit` (iOS)
/// and share the native handle with the Flutter side. By doing so, the `agora_rtc_engine`
/// acts as a proxy, allowing you to invoke the functions of the `RtcEngine` (Android)/`AgoraRtcEngineKit` (iOS).
///
/// The key point of how to use it:
/// * Initializes the `RtcEngine` (Android)/`AgoraRtcEngineKit` (iOS) on the native side.
/// * Retrieves the native handle through the `RtcEngine.getNativeHandle`(Android)/`AgoraRtcEngineKit.getNativeHandle`(iOS)
///   function on the native side, and passes it to the Flutter side through the Flutter `MethodChannel`.
/// * Passes the native handle to the `createAgoraRtcEngine`(Flutter) on the Flutter side,
///   then the `RtcEngine`(Flutter) can call the functions through the shared native handle.
///
/// This example creates a `RtcEngine` (Android)/`AgoraRtcEngineKit` (iOS) on the native side
/// and registers the video frame observer to modify the video raw data. It makes the local
/// preview appear in gray for demonstration purposes.
///
/// The native side implementation can be found at:
/// - Android: `example/android/app/src/main/kotlin/io/agora/agora_rtc_flutter_example/VideoRawDataController.kt`
/// - iOS: `example/ios/Runner/VideoRawDataController.m`
class ProcessVideoRawData extends StatefulWidget {
  /// Construct the [ProcessVideoRawData]
  const ProcessVideoRawData({Key? key}) : super(key: key);

  @override
  State<StatefulWidget> createState() => _State();
}

class _State extends State<ProcessVideoRawData> {
  late final RtcEngineEx _engine;
  bool _isReadyPreview = false;

  bool isJoined = false, switchCamera = true, switchRender = true;
  Set<int> remoteUid = {};
  late TextEditingController _controller;
  bool _isUseFlutterTexture = false;
  ChannelProfileType _channelProfileType =
      ChannelProfileType.channelProfileLiveBroadcasting;

  final MethodChannel _sharedNativeHandleChannel =
      const MethodChannel('agora_rtc_engine_example/shared_native_handle');

  @override
  void initState() {
    super.initState();
    _controller = TextEditingController(text: config.channelId);

    _initEngine();
  }

  @override
  void dispose() {
    super.dispose();
    _dispose();
  }

  Future<void> _dispose() async {
    await _engine.leaveChannel();
    await _engine.release();
    // Destroys the `RtcEngine`(Android)/`AgoraRtcEngineKit`(iOS) on native side.
    // Note that this should be called after the Flutter side `RtcEngine.release` function.
    //
    // See native side implementation:
    // Android: `example/android/app/src/main/kotlin/io/agora/agora_rtc_flutter_example/MainActivity.kt`
    // iOS: `example/ios/Runner/AppDelegate.m`
    await _sharedNativeHandleChannel.invokeMethod('native_dispose');
  }

  Future<void> _initEngine() async {
    // Initializes the `RtcEngine`(Android)/`AgoraRtcEngineKit`(iOS) on native side,
    // and retrieves the native handle of `RtcEngine`(Android)/`AgoraRtcEngineKit`(iOS).
    //
    // See native side implementation:
    // Android: `example/android/app/src/main/kotlin/io/agora/agora_rtc_flutter_example/MainActivity.kt`
    // iOS: `example/ios/Runner/AppDelegate.m`
    final sharedNativeHandle = await _sharedNativeHandleChannel.invokeMethod(
      'native_init',
      {'appId': config.appId},
    );
    // Passes the native handle from `RtcEngine`(Android)/`AgoraRtcEngineKit`(iOS) on native side.
    _engine = createAgoraRtcEngineEx(sharedNativeHandle: sharedNativeHandle);
    await _engine.initialize(RtcEngineContext(
      appId: config.appId,
      logConfig: const LogConfig(level: LogLevel.logLevelDebug),
    ));
    await _engine.setLogFilter(LogFilterType.logFilterInfo);

    _engine.registerEventHandler(RtcEngineEventHandler(
      onError: (ErrorCodeType err, String msg) {
        logSink.log('[onError] err: $err, msg: $msg');
      },
      onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
        logSink.log(
            '[onJoinChannelSuccess] connection: ${connection.toJson()} elapsed: $elapsed');
        setState(() {
          isJoined = true;
        });
      },
      onUserJoined: (RtcConnection connection, int rUid, int elapsed) {
        logSink.log(
            '[onUserJoined] connection: ${connection.toJson()} remoteUid: $rUid elapsed: $elapsed');
        setState(() {
          remoteUid.add(rUid);
        });
      },
      onUserOffline:
          (RtcConnection connection, int rUid, UserOfflineReasonType reason) {
        logSink.log(
            '[onUserOffline] connection: ${connection.toJson()}  rUid: $rUid reason: $reason');
        setState(() {
          remoteUid.removeWhere((element) => element == rUid);
        });
      },
      onLeaveChannel: (RtcConnection connection, RtcStats stats) {
        logSink.log(
            '[onLeaveChannel] connection: ${connection.toJson()} stats: ${stats.toJson()}');
        setState(() {
          isJoined = false;
          remoteUid.clear();
        });
      },
    ));

    await _engine.enableVideo();
    await _engine.startPreview();

    setState(() {
      _isReadyPreview = true;
    });
  }

  Future<void> _joinChannel() async {
    await _engine.joinChannelEx(
      token: config.token,
      connection: RtcConnection(channelId: _controller.text, localUid: 234),
      options: ChannelMediaOptions(
        channelProfile: _channelProfileType,
        clientRoleType: ClientRoleType.clientRoleBroadcaster,
      ),
    );
  }

  Future<void> _leaveChannel() async {
    await _engine.leaveChannel();
  }

  Future<void> _switchCamera() async {
    await _engine.switchCamera();
    setState(() {
      switchCamera = !switchCamera;
    });
  }

  @override
  Widget build(BuildContext context) {
    return ExampleActionsWidget(
      displayContentBuilder: (context, isLayoutHorizontal) {
        if (!_isReadyPreview) return Container();
        return Stack(
          children: [
            AgoraVideoView(
              controller: VideoViewController(
                rtcEngine: _engine,
                canvas: const VideoCanvas(uid: 0),
                useFlutterTexture: _isUseFlutterTexture,
              ),
            ),
            Align(
              alignment: Alignment.topLeft,
              child: SingleChildScrollView(
                scrollDirection: Axis.horizontal,
                child: Row(
                  children: List.of(remoteUid.map(
                    (e) => SizedBox(
                      width: 120,
                      height: 120,
                      child: AgoraVideoView(
                        controller: VideoViewController.remote(
                          rtcEngine: _engine,
                          canvas: VideoCanvas(uid: e),
                          connection:
                              RtcConnection(channelId: _controller.text),
                          useFlutterTexture: _isUseFlutterTexture,
                        ),
                      ),
                    ),
                  )),
                ),
              ),
            )
          ],
        );
      },
      actionsBuilder: (context, isLayoutHorizontal) {
        final channelProfileType = [
          ChannelProfileType.channelProfileLiveBroadcasting,
          ChannelProfileType.channelProfileCommunication,
        ];
        final items = channelProfileType
            .map((e) => DropdownMenuItem(
                  child: Text(
                    e.toString().split('.')[1],
                  ),
                  value: e,
                ))
            .toList();

        return Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisSize: MainAxisSize.min,
          children: [
            TextField(
              controller: _controller,
              decoration: const InputDecoration(hintText: 'Channel ID'),
            ),
            if (!kIsWeb &&
                (defaultTargetPlatform == TargetPlatform.android ||
                    defaultTargetPlatform == TargetPlatform.iOS))
              Row(
                mainAxisSize: MainAxisSize.min,
                mainAxisAlignment: MainAxisAlignment.start,
                children: [
                  const Text(
                      'Rendered by SurfaceView \n(default TextureView): '),
                  Switch(
                    value: _isUseFlutterTexture,
                    onChanged: isJoined
                        ? null
                        : (changed) {
                            setState(() {
                              _isUseFlutterTexture = changed;
                            });
                          },
                  )
                ],
              ),
            const SizedBox(
              height: 20,
            ),
            const Text('Channel Profile: '),
            DropdownButton<ChannelProfileType>(
              items: items,
              value: _channelProfileType,
              onChanged: isJoined
                  ? null
                  : (v) {
                      setState(() {
                        _channelProfileType = v!;
                      });
                    },
            ),
            const SizedBox(
              height: 20,
            ),
            Row(
              children: [
                Expanded(
                  flex: 1,
                  child: ElevatedButton(
                    onPressed: isJoined ? _leaveChannel : _joinChannel,
                    child: Text('${isJoined ? 'Leave' : 'Join'} channel'),
                  ),
                )
              ],
            ),
            if (defaultTargetPlatform == TargetPlatform.android ||
                defaultTargetPlatform == TargetPlatform.iOS) ...[
              const SizedBox(
                height: 20,
              ),
              ElevatedButton(
                onPressed: _switchCamera,
                child: Text('Camera ${switchCamera ? 'front' : 'rear'}'),
              ),
            ],
          ],
        );
      },
    );
  }
}

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[Paste your output here]
@peilinok peilinok added the bug Something isn't working label Apr 1, 2025
@peilinok peilinok self-assigned this Apr 1, 2025
@peilinok
Copy link
Contributor Author

peilinok commented Apr 1, 2025

Do not use shared rtc engine to do this, we should follow https://github.com/AgoraLibrary/flutter_agora_rtc_rawdata

@peilinok peilinok closed this as completed Apr 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant