-
Notifications
You must be signed in to change notification settings - Fork 382
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
How to detect who is sharing the screen in flutter agora web #1892
Comments
You should know what the screen share uid is, in your case, it's 1001. |
Hi @littleGnAl , what about when we use one RtcEngine instance only to do this? Below is the code where we can share screen track without joining as different user (1001).
This shares the screen track and another remote user is able to view the screenshare track successfully when already joined inside that channel. But when that remote user leaves and rejoins the channel, this screenshare track does not load again. How can I solve this for remote user who joined after screenshare happened? |
When using the below video view, screenshare track does not load for remote users when joining after screen is already shared by another user.
|
The screen-sharing stream should not be published without calling |
Did you mean that we cannot join a channel first and then also, share screen later with same user without joining as another user? So far, I have always seen Agora's flutter screen sharing examples always have an extra dedicated user joined for sharing screen only. Is that the only to share screen? Join an additional "screen share only" user always? |
Without additional information, we are unfortunately not sure how to resolve this issue. We are therefore reluctantly going to close this bug for now. If you find this problem please file a new issue with the same description, what happens, logs and the output. All system setups can be slightly different so it's always better to open new issues and reference the related ones. Thanks for your contribution. |
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please raise a new issue. |
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:agora_rtc_engine_example/components/rgba_image.dart';
import 'package:agora_rtc_engine_example/components/basic_video_configuration_widget.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:agora_rtc_engine_example/components/remote_video_views_widget.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:ui' as ui;
/// ScreenSharing Example
class ScreenSharing extends StatefulWidget {
/// Construct the [ScreenSharing]
const ScreenSharing({Key? key}) : super(key: key);
@OverRide
State createState() => _State();
}
class _State extends State with KeepRemoteVideoViewsMixin {
late final RtcEngineEx _engine;
bool _isReadyPreview = false;
String channelId = config.channelId;
bool isJoined = false;
late TextEditingController _controller;
late final TextEditingController _localUidController;
late final TextEditingController _screenShareUidController;
bool _isScreenShared = false;
late final RtcEngineEventHandler _rtcEngineEventHandler;
@OverRide
void initState() {
super.initState();
_controller = TextEditingController(text: channelId);
_localUidController = TextEditingController(text: '1000');
_screenShareUidController = TextEditingController(text: '1001');
_initEngine();
}
@OverRide
void dispose() {
super.dispose();
_engine.unregisterEventHandler(_rtcEngineEventHandler);
_engine.release();
}
_initEngine() async {
_rtcEngineEventHandler = 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;
});
}, onLeaveChannel: (RtcConnection connection, RtcStats stats) {
logSink.log(
'[onLeaveChannel] connection: ${connection.toJson()} stats: ${stats.toJson()}');
setState(() {
isJoined = false;
});
}, onLocalVideoStateChanged: (VideoSourceType source,
LocalVideoStreamState state, LocalVideoStreamReason error) {
logSink.log(
'[onLocalVideoStateChanged] source: $source, state: $state, error: $error');
if (!(source == VideoSourceType.videoSourceScreen ||
source == VideoSourceType.videoSourceScreenPrimary)) {
return;
}
}
void _joinChannel() async {
final localUid = int.tryParse(_localUidController.text);
if (localUid != null) {
await _engine.joinChannelEx(
token: '',
connection:
RtcConnection(channelId: _controller.text, localUid: localUid),
options: const ChannelMediaOptions(
autoSubscribeVideo: true,
autoSubscribeAudio: true,
publishCameraTrack: true,
publishMicrophoneTrack: true,
clientRoleType: ClientRoleType.clientRoleBroadcaster,
));
}
}
Future _updateScreenShareChannelMediaOptions() async {
final shareShareUid = int.tryParse(_screenShareUidController.text);
if (shareShareUid == null) return;
await _engine.updateChannelMediaOptionsEx(
options: const ChannelMediaOptions(
publishScreenTrack: true,
publishSecondaryScreenTrack: true,
publishCameraTrack: false,
publishMicrophoneTrack: false,
publishScreenCaptureAudio: true,
publishScreenCaptureVideo: true,
clientRoleType: ClientRoleType.clientRoleBroadcaster,
),
connection:
RtcConnection(channelId: _controller.text, localUid: shareShareUid),
);
}
_leaveChannel() async {
await _engine.stopScreenCapture();
await _engine.leaveChannel();
}
@OverRide
Widget build(BuildContext context) {
return ExampleActionsWidget(
displayContentBuilder: (context, isLayoutHorizontal) {
if (!_isReadyPreview) return Container();
final children = [
Expanded(
flex: 1,
child: AspectRatio(
aspectRatio: 1,
child: AgoraVideoView(
controller: VideoViewController(
rtcEngine: _engine,
canvas: const VideoCanvas(
uid: 0,
),
)),
),
),
Expanded(
flex: 1,
child: AspectRatio(
aspectRatio: 1,
child: _isScreenShared
? AgoraVideoView(
controller: VideoViewController(
rtcEngine: _engine,
canvas: const VideoCanvas(
uid: 0,
sourceType: VideoSourceType.videoSourceScreen,
),
))
: Container(
color: Colors.grey[200],
child: const Center(
child: Text('Screen Sharing View'),
),
),
),
),
];
Widget localVideoView;
if (isLayoutHorizontal) {
localVideoView = Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: children,
);
} else {
localVideoView = Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: children,
);
}
return Stack(
children: [
localVideoView,
Align(
alignment: Alignment.topLeft,
child: RemoteVideoViewsWidget(
key: keepRemoteVideoViewsKey,
rtcEngine: _engine,
channelId: _controller.text,
connectionUid: int.tryParse(_localUidController.text),
),
)
],
);
},
actionsBuilder: (context, isLayoutHorizontal) {
if (!_isReadyPreview) return Container();
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: _controller,
decoration: const InputDecoration(hintText: 'Channel ID'),
),
TextField(
controller: _localUidController,
decoration: const InputDecoration(hintText: 'Local Uid'),
),
TextField(
controller: _screenShareUidController,
decoration: const InputDecoration(hintText: 'Screen Sharing Uid'),
),
const SizedBox(
height: 20,
),
BasicVideoConfigurationWidget(
rtcEngine: _engine,
title: 'Video Encoder Configuration',
setConfigButtonText: const Text(
'setVideoEncoderConfiguration',
style: TextStyle(fontSize: 10),
),
onConfigChanged: (width, height, frameRate, bitrate) {
_engine.setVideoEncoderConfiguration(VideoEncoderConfiguration(
dimensions: VideoDimensions(width: width, height: height),
frameRate: frameRate,
bitrate: bitrate,
));
},
),
const SizedBox(
height: 20,
),
Row(
children: [
Expanded(
flex: 1,
child: ElevatedButton(
onPressed: isJoined ? _leaveChannel : _joinChannel,
child: Text('${isJoined ? 'Leave' : 'Join'} channel'),
),
)
],
),
if (kIsWeb)
ScreenShareWeb(
rtcEngine: _engine,
isScreenShared: _isScreenShared,
onStartScreenShared: () {
if (isJoined) {
_updateScreenShareChannelMediaOptions();
}
},
onStopScreenShare: () {}),
if (!kIsWeb &&
(defaultTargetPlatform == TargetPlatform.android ||
defaultTargetPlatform == TargetPlatform.iOS))
ScreenShareMobile(
rtcEngine: _engine,
isScreenShared: _isScreenShared,
onStartScreenShared: () {
if (isJoined) {
_updateScreenShareChannelMediaOptions();
}
},
onStopScreenShare: () {}),
if (!kIsWeb &&
(defaultTargetPlatform == TargetPlatform.windows ||
defaultTargetPlatform == TargetPlatform.macOS))
ScreenShareDesktop(
rtcEngine: _engine,
isScreenShared: _isScreenShared,
onStartScreenShared: () {
if (isJoined) {
_updateScreenShareChannelMediaOptions();
}
},
onStopScreenShare: () {}),
],
);
},
);
}
}
class ScreenShareWeb extends StatefulWidget {
const ScreenShareWeb(
{Key? key,
required this.rtcEngine,
required this.isScreenShared,
required this.onStartScreenShared,
required this.onStopScreenShare})
: super(key: key);
final RtcEngine rtcEngine;
final bool isScreenShared;
final VoidCallback onStartScreenShared;
final VoidCallback onStopScreenShare;
@OverRide
State createState() => _ScreenShareWebState();
}
class _ScreenShareWebState extends State
implements ScreenShareInterface {
@OverRide
bool get isScreenShared => widget.isScreenShared;
@OverRide
void onStartScreenShared() {
widget.onStartScreenShared();
}
@OverRide
void onStopScreenShare() {
widget.onStopScreenShare();
}
@OverRide
RtcEngine get rtcEngine => widget.rtcEngine;
@OverRide
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
flex: 1,
child: ElevatedButton(
onPressed: !isScreenShared ? startScreenShare : stopScreenShare,
child: Text('${isScreenShared ? 'Stop' : 'Start'} screen share'),
),
)
],
);
}
@OverRide
void startScreenShare() async {
if (isScreenShared) return;
}
@OverRide
void stopScreenShare() async {
if (!isScreenShared) return;
}
}
class ScreenShareMobile extends StatefulWidget {
const ScreenShareMobile(
{Key? key,
required this.rtcEngine,
required this.isScreenShared,
required this.onStartScreenShared,
required this.onStopScreenShare})
: super(key: key);
final RtcEngine rtcEngine;
final bool isScreenShared;
final VoidCallback onStartScreenShared;
final VoidCallback onStopScreenShare;
@OverRide
State createState() => _ScreenShareMobileState();
}
class _ScreenShareMobileState extends State
implements ScreenShareInterface {
final MethodChannel _iosScreenShareChannel =
const MethodChannel('example_screensharing_ios');
@OverRide
bool get isScreenShared => widget.isScreenShared;
@OverRide
void onStartScreenShared() {
widget.onStartScreenShared();
}
@OverRide
void onStopScreenShare() {
widget.onStopScreenShare();
}
@OverRide
RtcEngine get rtcEngine => widget.rtcEngine;
@OverRide
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
flex: 1,
child: ElevatedButton(
onPressed: !isScreenShared ? startScreenShare : stopScreenShare,
child: Text('${isScreenShared ? 'Stop' : 'Start'} screen share'),
),
)
],
);
}
@OverRide
void startScreenShare() async {
if (isScreenShared) return;
}
@OverRide
void stopScreenShare() async {
if (!isScreenShared) return;
}
Future _showRPSystemBroadcastPickerViewIfNeed() async {
if (defaultTargetPlatform != TargetPlatform.iOS) {
return;
}
}
}
class ScreenShareDesktop extends StatefulWidget {
const ScreenShareDesktop(
{Key? key,
required this.rtcEngine,
required this.isScreenShared,
required this.onStartScreenShared,
required this.onStopScreenShare})
: super(key: key);
final RtcEngine rtcEngine;
final bool isScreenShared;
final VoidCallback onStartScreenShared;
final VoidCallback onStopScreenShare;
@OverRide
State createState() => _ScreenShareDesktopState();
}
class _ScreenShareDesktopState extends State
implements ScreenShareInterface {
List _screenCaptureSourceInfos = [];
late ScreenCaptureSourceInfo _selectedScreenCaptureSourceInfo;
@OverRide
bool get isScreenShared => widget.isScreenShared;
@OverRide
void onStartScreenShared() {
widget.onStartScreenShared();
}
@OverRide
void onStopScreenShare() {
widget.onStopScreenShare();
}
@OverRide
RtcEngine get rtcEngine => widget.rtcEngine;
Future _initScreenCaptureSourceInfos() async {
SIZE thumbSize = const SIZE(width: 50, height: 50);
SIZE iconSize = const SIZE(width: 50, height: 50);
_screenCaptureSourceInfos = await rtcEngine.getScreenCaptureSources(
thumbSize: thumbSize, iconSize: iconSize, includeScreen: true);
_selectedScreenCaptureSourceInfo = _screenCaptureSourceInfos[0];
setState(() {});
}
Widget _createDropdownButton() {
if (_screenCaptureSourceInfos.isEmpty) return Container();
ui.PixelFormat format = ui.PixelFormat.rgba8888;
if (defaultTargetPlatform == TargetPlatform.windows) {
// The native sdk return the bgra format on Windows.
format = ui.PixelFormat.bgra8888;
}
return DropdownButton(
items: _screenCaptureSourceInfos.map((info) {
Widget image;
if (info.iconImage!.width! != 0 && info.iconImage!.height! != 0) {
image = RgbaImage(
bytes: info.iconImage!.buffer!,
width: info.iconImage!.width!,
height: info.iconImage!.height!,
format: format,
);
} else if (info.thumbImage!.width! != 0 &&
info.thumbImage!.height! != 0) {
image = RgbaImage(
bytes: info.thumbImage!.buffer!,
width: info.thumbImage!.width!,
height: info.thumbImage!.height!,
format: format,
);
} else {
image = const SizedBox(
width: 50,
height: 50,
);
}
}
@OverRide
void initState() {
super.initState();
}
@OverRide
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_createDropdownButton(),
if (_screenCaptureSourceInfos.isNotEmpty)
Row(
children: [
Expanded(
flex: 1,
child: ElevatedButton(
onPressed:
!isScreenShared ? startScreenShare : stopScreenShare,
child:
Text('${isScreenShared ? 'Stop' : 'Start'} screen share'),
),
)
],
),
],
);
}
@OverRide
void startScreenShare() async {
if (isScreenShared) return;
}
@OverRide
void stopScreenShare() async {
if (!isScreenShared) return;
}
}
abstract class ScreenShareInterface {
void onStartScreenShared();
void onStopScreenShare();
bool get isScreenShared;
RtcEngine get rtcEngine;
void startScreenShare();
void stopScreenShare();
}
The text was updated successfully, but these errors were encountered: