Flutter亚马逊Chime会议集成插件eggnstone_amazon_chime的使用

发布于 1周前 作者 bupafengyu 来自 Flutter

Flutter亚马逊Chime会议集成插件eggnstone_amazon_chime的使用

简介

eggnstone_amazon_chime 是一个用于 Amazon Chime SDK 的封装,允许通过音频和视频加入 Chime 会议。

Android 注意事项

  • Chime 不支持 Android 模拟器!
  • 确保你的设备运行在 ARM 支持的设备(真实设备)或模拟器上。目前不支持 x86 模拟器。
  • 设置应用的最低 SDK 版本为 23 或更高(因为 Chime 需要 API 级别 21,但平台视图无法在低于 API 级别 23 的版本上显示)。
  • 使用的版本:v0.17.1。

iOS 注意事项

  • 版本 2 及以上不支持 iOS
  • 需要维护者来支持 iOS 版本。
  • 版本 1 支持 iOS,但由于文件大小问题,版本 2 及以上目前不支持 iOS。
  • Chime 不支持 iOS 模拟器!

示例代码

以下是一个完整的示例 main.dart 文件,展示如何使用 eggnstone_amazon_chime 插件:

import 'dart:convert';
import 'dart:io';

import 'package:chime_example/MeetingSessionCreator.dart';
import 'package:chime_example/data/Attendee.dart';
import 'package:chime_example/data/Attendees.dart';
import 'package:device_info/device_info.dart';
import 'package:eggnstone_amazon_chime/eggnstone_amazon_chime.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:permission_handler/permission_handler.dart';

void main() {
    runApp(App());
}

class App extends StatefulWidget {
    @override
    _AppState createState() => _AppState();
}

class _AppState extends State<App> {
    String _version = 'Unknown';
    String _createMeetingSessionResult = 'CreateMeetingSession: Unknown';
    String _audioVideoStartResult = 'AudioVideo: Unknown';
    String _audioVideoStartLocalVideoResult = 'AudioVideoLocalVideo: Unknown';
    String _audioVideoStartRemoteVideoResult = 'AudioVideoRemoteVideo: Unknown';

    Attendees _attendees = Attendees();
    bool _isAndroidEmulator = false;
    bool _isIosSimulator = false;

    @override
    void initState() {
        super.initState();
        _startChime(); // 初始化 Chime
    }

    @override
    Widget build(BuildContext context) {
        var chimeViewChildren = List<Widget>.empty(growable: true);

        if (_attendees.length == 0)
            chimeViewChildren.add(Expanded(child: Center(child: Text('No attendees yet.'))));
        else
            for (int attendeeIndex = 0; attendeeIndex < _attendees.length; attendeeIndex++) {
                Attendee attendee = _attendees[attendeeIndex];
                if (attendee.videoView != null)
                    chimeViewChildren.add(
                        Expanded(
                            child: Center(
                                child: AspectRatio(
                                    aspectRatio: attendee.aspectRatio,
                                    child: attendee.videoView
                                )
                            )
                        )
                    );
            }

        var chimeViewColumn = Column(children: chimeViewChildren);

        Widget content;

        if (_isAndroidEmulator || _isIosSimulator)
            content = Padding(
                padding: const EdgeInsets.all(16),
                child: Center(
                    child: Text('Chime does not support Android/iOS emulators/simulators.\n\nIf you see the SDK version above then the connection to the SDK works though.')
                )
            );
        else
            content = Column(
                children: [
                    Text(_createMeetingSessionResult),
                    SizedBox(height: 8),
                    Row(
                        mainAxisAlignment: MainAxisAlignment.spaceAround,
                        children: [
                            Text('Audio/Video:'),
                            ElevatedButton(
                                child: Text('Start'),
                                onPressed: () => _audioVideoStart()
                            ),
                            ElevatedButton(
                                child: Text('Stop'),
                                onPressed: () => _audioVideoStop()
                            )
                        ]
                    ),
                    Text(_audioVideoStartResult),
                    SizedBox(height: 8),
                    Row(
                        mainAxisAlignment: MainAxisAlignment.spaceAround,
                        children: [
                            Text('Local Video:'),
                            ElevatedButton(
                                child: Text('Start'),
                                onPressed: () => _audioVideoStartLocalVideo()
                            ),
                            ElevatedButton(
                                child: Text('Stop'),
                                onPressed: () => _audioVideoStopLocalVideo()
                            )
                        ]
                    ),
                    Text(_audioVideoStartLocalVideoResult),
                    SizedBox(height: 8),
                    Row(
                        mainAxisAlignment: MainAxisAlignment.spaceAround,
                        children: [
                            Text('Remote Video:'),
                            ElevatedButton(
                                child: Text('Start'),
                                onPressed: () => _audioVideoStartRemoteVideo()
                            ),
                            ElevatedButton(
                                child: Text('Stop'),
                                onPressed: () => _audioVideoStopRemoteVideo()
                            )
                        ]
                    ),
                    Text(_audioVideoStartRemoteVideoResult),
                    SizedBox(height: 8),
                    Expanded(child: chimeViewColumn)
                ]
            );

        return MaterialApp(
            home: Scaffold(
                appBar: AppBar(title: Text('ChimePlugin')),
                body: Column(
                    children: [
                        SizedBox(height: 8),
                        Text(_version),
                        SizedBox(height: 8),
                        Expanded(child: content)
                    ]
                )
            )
        );
    }

    void _startChime() async {
        await _getVersion();

        if (Platform.isAndroid) {
            DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
            AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
            if (androidInfo.isPhysicalDevice) {
                _addListener();
                await _createMeetingSession();
            } else {
                setState(() {
                    _isAndroidEmulator = true;
                });
            }
        } else if (Platform.isIOS) {
            DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
            IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
            if (iosInfo.isPhysicalDevice) {
                _addListener();
                await _createMeetingSession();
            } else {
                setState(() {
                    _isIosSimulator = true;
                });
            }
        } else {
            _addListener();
            await _createMeetingSession();
        }
    }

    Future<void> _getVersion() async {
        String version;

        try {
            version = await Chime.version ?? '?';
        } on PlatformException {
            version = 'Failed to get version.';
        }

        if (mounted)
            setState(() {
                _version = version;
            });
    }

    void _addListener() {
        Chime.eventChannel.receiveBroadcastStream().listen(
            (data) async {
                dynamic event = JsonDecoder().convert(data);
                String eventName = event['Name'];
                dynamic eventArguments = event['Arguments'];
                switch (eventName) {
                    case 'OnVideoTileAdded':
                        _handleOnVideoTileAdded(eventArguments);
                        break;
                    case 'OnVideoTileRemoved':
                        _handleOnVideoTileRemoved(eventArguments);
                        break;
                    default:
                        print('Warning: Unhandled event: $eventName');
                        print('Data: $data');
                        break;
                }
            },
            onDone: () {
                print('Chime.eventChannel.receiveBroadcastStream().listen()/onDone()');
            },
            onError: (e) {
                print('Chime.eventChannel.receiveBroadcastStream().listen()/onError()');
            }
        );
    }

    Future<void> _createMeetingSession() async {
        if (await Permission.microphone.request().isGranted == false) {
            _createMeetingSessionResult = 'Need microphone permission.';
            return;
        }

        if (await Permission.camera.request().isGranted == false) {
            _createMeetingSessionResult = 'Need camera permission.';
            return;
        }

        String meetingSessionState;

        try {
            // 需要提供自己的 AWS 账户和 Chime 配置数据
            meetingSessionState = await MeetingSessionCreator().create() ?? 'OK';
        } on PlatformException catch (e) {
            meetingSessionState = 'Failed to create MeetingSession. PlatformException: $e';
        } catch (e) {
            meetingSessionState = 'Failed to create MeetingSession. Error: $e';
        }

        if (mounted)
            setState(() {
                _createMeetingSessionResult = meetingSessionState;
            });
    }

    Future<void> _audioVideoStart() async {
        String result;

        try {
            result = await Chime.audioVideoStart() ?? 'OK';
        } on PlatformException catch (e) {
            result = 'AudioVideoStart failed: PlatformException: $e';
        } catch (e) {
            result = 'AudioVideoStart failed: Error: $e';
        }

        if (mounted)
            setState(() {
                _audioVideoStartResult = result;
            });
    }

    Future<void> _audioVideoStop() async {
        String result;

        try {
            result = await Chime.audioVideoStop() ?? 'OK';
        } on PlatformException catch (e) {
            result = 'AudioVideoStop failed: PlatformException: $e';
        } catch (e) {
            result = 'AudioVideoStop failed: Error: $e';
        }

        if (mounted)
            setState(() {
                _audioVideoStartResult = result;
            });
    }

    Future<void> _audioVideoStartLocalVideo() async {
        String result;

        try {
            result = await Chime.audioVideoStartLocalVideo() ?? 'OK';
        } on PlatformException catch (e) {
            result = 'AudioVideoStartLocalVideo failed: PlatformException: $e';
        } catch (e) {
            result = 'AudioVideoStartLocalVideo failed: Error: $e';
        }

        if (mounted)
            setState(() {
                _audioVideoStartLocalVideoResult = result;
            });
    }

    Future<void> _audioVideoStopLocalVideo() async {
        String result;

        try {
            result = await Chime.audioVideoStopLocalVideo() ?? 'OK';
        } on PlatformException catch (e) {
            result = 'AudioVideoStopLocalVideo failed: PlatformException: $e';
        } catch (e) {
            result = 'AudioVideoStopLocalVideo failed: Error: $e';
        }

        if (mounted)
            setState(() {
                _audioVideoStartLocalVideoResult = result;
            });
    }

    Future<void> _audioVideoStartRemoteVideo() async {
        String result;

        try {
            result = await Chime.audioVideoStartRemoteVideo() ?? 'OK';
        } on PlatformException catch (e) {
            result = 'AudioVideoStartRemoteVideo failed: PlatformException: $e';
        } catch (e) {
            result = 'AudioVideoStartRemoteVideo failed: Error: $e';
        }

        if (mounted)
            setState(() {
                _audioVideoStartRemoteVideoResult = result;
            });
    }

    Future<void> _audioVideoStopRemoteVideo() async {
        String result;

        try {
            result = await Chime.audioVideoStopRemoteVideo() ?? 'OK';
        } on PlatformException catch (e) {
            result = 'AudioVideoStopRemoteVideo failed: PlatformException: $e';
        } catch (e) {
            result = 'AudioVideoStopRemoteVideo failed: Error: $e';
        }

        if (mounted)
            setState(() {
                _audioVideoStartRemoteVideoResult = result;
            });
    }

    void _handleOnVideoTileAdded(dynamic arguments) async {
        bool isLocalTile = arguments['IsLocalTile'];
        int tileId = arguments['TileId'];
        int videoStreamContentHeight = arguments['VideoStreamContentHeight'];
        int videoStreamContentWidth = arguments['VideoStreamContentWidth'];

        Attendee? attendee = _attendees.getByTileId(tileId);
        if (attendee != null) {
            print('_handleOnVideoTileAdded called but already mapped.');
            return;
        }

        print('_handleOnVideoTileAdded: New attendee: TileId=$tileId => creating ChimeDefaultVideoRenderView');
        attendee = Attendee(tileId, isLocalTile);
        attendee.height = videoStreamContentHeight;
        attendee.width = videoStreamContentWidth;
        _attendees.add(attendee);

        Attendee nonNullAttendee = attendee;
        setState(() {
            nonNullAttendee.setVideoView(
                ChimeDefaultVideoRenderView(
                    onPlatformViewCreated: (int viewId) async {
                        nonNullAttendee.setViewId(viewId);
                        print('ChimeDefaultVideoRenderView created. Binding...');
                        await Chime.bindVideoView(nonNullAttendee.viewId!, nonNullAttendee.tileId);
                        print('ChimeDefaultVideoRenderView created. Bound.');
                    }
                )
            );
        });
    }

    void _handleOnVideoTileRemoved(dynamic arguments) async {
        int tileId = arguments['TileId'];

        Attendee? attendee = _attendees.getByTileId(tileId);
        if (attendee == null) {
            print('Error: Could not find attendee for TileId=$tileId');
            return;
        }

        print('_handleOnVideoTileRemoved: Found attendee. Unbinding...');
        _attendees.remove(attendee);
        await Chime.unbindVideoView(tileId);
        print('_handleOnVideoTileRemoved: Found attendee. Unbound.');

        setState(() {
            // refresh
        });
    }
}

更多关于Flutter亚马逊Chime会议集成插件eggnstone_amazon_chime的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter亚马逊Chime会议集成插件eggnstone_amazon_chime的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中集成并使用eggnstone_amazon_chime插件来加入Amazon Chime会议的示例代码。请注意,为了成功运行此代码,你需要确保已经安装了Flutter和Dart环境,并且在你的项目中正确添加了eggnstone_amazon_chime插件。

首先,你需要在pubspec.yaml文件中添加eggnstone_amazon_chime依赖:

dependencies:
  flutter:
    sdk: flutter
  eggnstone_amazon_chime: ^最新版本号  # 请替换为实际发布的最新版本号

然后运行flutter pub get来安装依赖。

接下来,是一个基本的Flutter应用示例,展示了如何使用eggnstone_amazon_chime插件加入Amazon Chime会议:

import 'package:flutter/material.dart';
import 'package:eggnstone_amazon_chime/eggnstone_amazon_chime.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Amazon Chime Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  AmazonChimeClient? chimeClient;

  @override
  void initState() {
    super.initState();
    // 初始化Amazon Chime客户端
    initializeChimeClient();
  }

  void initializeChimeClient() async {
    // 配置你的Amazon Chime凭证和其他必要信息
    final credentials = AmazonChimeCredentials(
      accessKeyId: '你的AccessKeyID',
      secretAccessKey: '你的SecretAccessKey',
      sessionToken: '你的SessionToken',  // 如果使用临时凭证,需要提供
      region: '你的Region',  // 例如 'us-west-2'
    );

    // 创建Amazon Chime客户端实例
    chimeClient = AmazonChimeClient(credentials: credentials);

    // 你可以在这里添加更多初始化代码,比如创建会议、获取会议详情等
    // ...
  }

  void joinMeeting() async {
    if (chimeClient == null) {
      print('Chime client is not initialized.');
      return;
    }

    // 配置会议信息
    final meetingInfo = MeetingInfo(
      meetingId: '你的MeetingID',
      externalMeetingId: '你的ExternalMeetingID',  // 如果需要
      attendeeId: '你的AttendeeID',
      joinToken: '你的JoinToken',
    );

    try {
      // 加入会议
      await chimeClient!.startMeeting(meetingInfo: meetingInfo);
      print('Joined the meeting successfully.');
    } catch (e) {
      print('Failed to join the meeting: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Amazon Chime Example'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: joinMeeting,
          child: Text('Join Meeting'),
        ),
      ),
    );
  }
}

在这个示例中,我们定义了一个简单的Flutter应用,其中包含一个按钮用于加入Amazon Chime会议。在initializeChimeClient方法中,我们初始化了Amazon Chime客户端,并配置了必要的凭证信息。在joinMeeting方法中,我们配置了会议信息并尝试加入会议。

请注意,为了运行这个示例,你需要提供有效的Amazon Chime凭证(accessKeyId, secretAccessKey, sessionToken)和会议信息(meetingId, externalMeetingId, attendeeId, joinToken)。这些信息通常是从你的Amazon Chime后端服务获取的。

此外,由于Amazon Chime SDK for Flutter可能包含一些额外的配置步骤和依赖项,建议查阅eggnstone_amazon_chime的官方文档(如果可用)以获取更详细的信息和最新的使用指南。

回到顶部