100ms Logo

100ms

Docs

Search docs
/

Screen Share

100ms React Native Package provides support for sharing the entire screen of the device to the room.

Please note that for a peer to share their screen, their role must have screenshare enabled in the dashboard. Also select the appropriate resolution for the Screen share quality. 1080p is recommended for better text readability.

ScreenshareDashboard

Android Setup

Add HmsScreenshareActivity to manifest located at android/app/src/main/AndroidManifest.xml as follows -

<application android:name=".MainApplication" ... android:label="@string/app_name"> ... <!-- Add below activity in your "application" tag --> <activity android:name="com.reactnativehmssdk.HmsScreenshareActivity" android:label="@string/app_name" /> ... </application>

iOS Setup

You need to create an iOS broadcast upload extension. It uses Apple's ReplayKit framework to record the device screen and delivers frame samples to your broadcast extension. You can share not only your own app but also the entire device sceeen including other apps on the device.

Step 1 - Open Xcode project at the

Open your iOS Xcode project xed ios.

Step 2 - Add Broadcast Upload Extension

Click on your project in the Project Navigator to show the project settings. Press + at the bottom of the target list to add a new target.

AddExtension

Select the Broadcast Upload Extension type for your new target.

SelectExtension

Enter your new target detail in the dialog. Uncheck Include UI Extension option.

DetailExtension

In the following dialog, activate the new scheme for the new target.

ActivateExtension

Step 3 - Add App Group

Click + icon in Signing & Capabilities section. Select App Group from the list of Capabilities.

AddAppgroup

New section should be added under Signing & Capabilities named App Groups. Click + icon under that.

Appgroup

Enter App Aroup name. This must be unique to your Apple Development Account. The standard practice is to use your domain name like group.your.domain.name.

AppgroupDetail

Step 4 - Edit Podfile

In ios folder of your react project and open Podfile. Paste the following code and replace the extension name you just created:

# ENSURE TO SET THE CORRECT SCREENSHARE EXTENSION TARGET NAME BELOW target 'RNHMSExampleBroadcastUpload' do use_modular_headers! pod 'HMSBroadcastExtensionSDK' end

A sample of complete Podfile snippet after adding the Screenshare Target is as follows -

require_relative '../node_modules/react-native/scripts/react_native_pods' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' platform :ios, '13.0' # ENSURE TO SET THE CORRECT EXTENSION TARGET NAME BELOW. # Not required if starting Screenshare from iOS devices is not a feature of the app. Then Remove this Target snippet target 'RNHMSExampleBroadcastUpload' do use_modular_headers! pod 'HMSBroadcastExtensionSDK' end # ENSURE TO SET THE CORRECT MAIN APP TARGET NAME BELOW target 'RNHMSExample' do config = use_native_modules! permissions_path = '../node_modules/react-native-permissions/ios' use_react_native!( :path => config[:reactNativePath], # to enable hermes on iOS, change `false` to `true` and then install pods # :hermes_enabled => false :hermes_enabled => true ) pod 'Permission-Camera', :path => "#{permissions_path}/Camera" pod 'Permission-Microphone', :path => "#{permissions_path}/Microphone" target 'RNHMSExampleTests' do inherit! :complete # Pods for testing end # Enables Flipper. # # Note that if you have use_frameworks! enabled, Flipper will not work and # you should disable the next line. use_flipper!() # POST INSTALL SCRIPT TO DISABLE BITCODE FROM ALL PODS & EXCLUDE arm64 FROM SIMULATORS post_install do |installer| react_native_post_install(installer) installer.pods_project.build_configurations.each do |config| config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64" config.build_settings['ENABLE_BITCODE'] = 'NO' end end end

Podfile

In Terminal, change the directory to ios and run pod install command.

Step 5 - Edit SampleHandler

Open the SampleHandler.swift file. It would be available in your TARGET_NAME > ExtensionName Xcode folder. For example, RNHMSExample > ExtensionName then SampleHandler.swift file.

SampleHandler

Replace the default SampleHandler.swift file with the code below and pset the correct App Group created for your Apple Developer Account.

import ReplayKit import HMSBroadcastExtensionSDK class SampleHandler: RPBroadcastSampleHandler { // ENSURE TO SET CORRECT APP GROUP LINKED TO YOUR APPLE DEVELOPER ACCOUNT HERE let screenRenderer = HMSScreenRenderer(appGroup: "group.reactnativehms") override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) { } override func broadcastPaused() { } override func broadcastResumed() { } override func broadcastFinished() { screenRenderer.invalidate() } override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) { switch sampleBufferType { case RPSampleBufferType.video: // Handle video sample buffer if let error = screenRenderer.process(sampleBuffer) { if error.code == .noActiveMeeting { finishBroadcastWithError(NSError(domain: "ScreenShare", code: error.code.rawValue, userInfo: [NSLocalizedFailureReasonErrorKey: "You are not in a meeting."])) } } break case RPSampleBufferType.audioApp: _ = self.screenRenderer.process(audioSampleBuffer: sampleBuffer) break case RPSampleBufferType.audioMic: // Handle audio sample buffer for mic audio break @unknown default: // Handle other sample buffer types fatalError("Unknown type of sample buffer") } } }

Start Screenshare

To start screen share, app needs to call the startScreenshare method of HMSSDK instance.

To start screen share in iOS you need to pass App Group and Preferred Extension name while creating HMSSDK instance as follows:

// Create `HMSSDK` instance with `appGroup` and `preferredExtension` properties const hmsInstance = await HMSSDK.build({ appGroup: 'group.reactnativehms', // ensure to pass the correct App Group linked to your Apple Developer Account preferredExtension: 'RHHMSExampleBroadcastUpload', // ensure to pass the correct Extension Target name created in Xcode }); ... // Start Screenshare try { await hmsInstance.startScreenshare(); console.log('Start startScreenshare Success'); } catch(error) { console.log('Start startScreenshare Error: ', error); }

You can find app group and extension name in Xcode under Signing and Capabilities section under target > yourExtensionName.

Parameter

Stop Screenshare

To stop screen share, application needs to call the stopScreenshare method of HMSSDK instance.

try { await hmsInstance.stopScreenshare(); console.log('Stop Screenshare Success'); } catch (error) { console.log('Stop Screenshare Error: ', error); }

How to get Screen Share Status

Application needs to call the isScreenShared method of HMSSDK instance. This method returns true inscase Screenshare is currently active and being used, and false for inactive state.

try { await hmsInstance.isScreenShared(); console.log('Is Screenshare Success'); } catch (error) { console.log('Is Screenshare Error: ', error); }

Render Screenshare Track

You can get screenshare track in two ways:

  • from HMSUpdateListenerActions.ON_TRACK_UPDATE event, when the track is added
  • from peers' auxiliaryTracks property

Note: Screenshare track can be differentiated from normal track using tracks' source property - track.source === "SCREEN" for screenshare tracks

Code snippet to get screenshare track from HMSUpdateListenerActions.ON_TRACK_UPDATE event -

const [peerTrackNodes, setPeerTrackNodes] = useState([]); // Get screenshare track from onTrackListener const onTrackListener = ({ peer, track, type, }: { peer: HMSPeer; track: HMSTrack; type: HMSTrackUpdate; }) => { if( type === HMSTrackUpdate.TRACK_ADDED && // track is added track.source === HMSTrackSource.SCREEN && // track source is screenshare track.type === HMSTrackType.VIDEO // track type is video ) { // Saving screenshare track and peer to render it setPeerTrackNodes(prevNodes => { return [ { id: peer.peerID + track.trackId, peer, track, }, ...prevNodes ]; }); } }; ... const HmsView = hmsInstance.HmsView; <HmsView trackId={peerTrackNodes[0]?.trackId} />

Code snippet to get screenshare track from peers' auxiliaryTracks property -

const remotePeers = await hmsInstance.getRemotePeers(); const firstPeer = remotePeers[0]; // Get screenshare track from peer const firstPeerScreenshareTrack = firstPeer.auxiliaryTracks.find(auxiliaryTrack => { return ( auxiliaryTrack.source === HMSTrackSource.SCREEN && // track source is screenshare auxiliaryTrack.type === HMSTrackType.VIDEO // track type is video ); }); ... const HmsView = hmsInstance?.HmsView; <HmsView trackId={firstPeerScreenshareTrack?.trackId} />

Refer to Render Video docs to know more about HmsView component and help in rendering video.

Troubleshooting Guide

For starting Screenshare from iOS devices (iPhones or iPads) following are some common setup you should already have within your Apps -

Bitcode Disabled

Bitcode has been disabled by Apple from Xcode 14 & iOS 16 and above. So 100ms packages also have Disabled Bitcode to ensure compatibility. Ensure that in your Xcode project Bitcode is Disabled for all Targets.

Disable Bitcode in Xcode

Podfile with Bitcode Disabled

You can use the following Podfile which has post_install script to Disable Bitcode for all Pods. Ensure that you modify the target for your Main App and the Broadcast Upload Extension.

In this sample Podfile, the Target names are RNHMSExample and RNHMSExampleBroadcastUpload. Change these to the actual Target names defined in your Xcode project.

require_relative '../node_modules/react-native/scripts/react_native_pods' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' platform :ios, '13.0' # ENSURE TO SET THE CORRECT SCREENSHARE EXTENSION TARGET NAME BELOW. # Not required if starting Screenshare from iOS devices is not a feature of the app. Then Remove this Target snippet target 'RNHMSExampleBroadcastUpload' do use_modular_headers! pod 'HMSBroadcastExtensionSDK' end # ENSURE TO SET THE CORRECT MAIN APP TARGET NAME BELOW target 'RNHMSExample' do config = use_native_modules! permissions_path = '../node_modules/react-native-permissions/ios' use_react_native!( :path => config[:reactNativePath], # to enable hermes on iOS, change `false` to `true` and then install pods # :hermes_enabled => false :hermes_enabled => true ) pod 'Permission-Camera', :path => "#{permissions_path}/Camera" pod 'Permission-Microphone', :path => "#{permissions_path}/Microphone" target 'RNHMSExampleTests' do inherit! :complete # Pods for testing end # Enables Flipper. # # Note that if you have use_frameworks! enabled, Flipper will not work and # you should disable the next line. use_flipper!() # POST INSTALL SCRIPT TO DISABLE BITCODE FROM ALL PODS & EXCLUDE arm64 FROM SIMULATORS post_install do |installer| react_native_post_install(installer) installer.pods_project.build_configurations.each do |config| config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64" config.build_settings['ENABLE_BITCODE'] = 'NO' end end end

Background Modes Enabled

In majority use-cases, playing audio from Room would be required when app is in Background Mode. Mostly, if users are starting Screenshare from their iPhones/iPads they would want to continue listening to audio from the Room. So, ensure that you have Background Modes Enabled in your Xcode project.

Enable Background Modes

App Groups Enabled

Ensure that you have enabled App Groups for both your Main App Target & the newly created Broadcast Extension Target. If the same App Group is not enabled on both Targets then the App & the Screenshare Extension won't be able to communicate & starting Screenshare from your iOS device will fail. Also, ensure that there's no typo / spelling mis-matches between the App Group enabled on Main App Target & the Screenshare Broadcast Extension Target.

Same App Group for both Targets

Permission Denied EXC_BAD_ACCESS error

100ms Example Apps already have configurations for starting Screenshare on iOS devices. The values for App Group & Preferred Extension used in 100ms Example Apps cannot be reused by any other apps as they won't be linked to your Apple Developer Account.

If the 100ms values for App Group which is "group.flutterhms" & Preferred Extension which is "FlutterBroadcastUploadExtension" are resued by your apps then you will have an Xcode exception containing Permission Denied EXC_BAD_ACCESS error message similar to the one shown below -

CFMessagePort: bootstrap_register(): failed 1100 (0x44c) 'Permission denied', port = 0xbc03, name = 'group.flutterhms.88C5E70E-F40F-4C36-A48C-E65736E85CAC.audio.mach.port' See /usr/include/servers/bootstrap_defs.h for the error codes. * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x8) frame #0: 0x00000001c03ddc78 CoreFoundation_CFGetNonObjCTypeID + 92 ...

To resolve this issue ensure that you create a Unique App Group which is linked to your Apple Developer Account. If you do not have an Active Apple Developer Account or do not have permissions to create a New App Group, then you won't be able to this feature.

Running on Simulator

Starting Screenshare from an Simulator is not supported by Apple. You can start Screenshare only from an actual iOS device like an iPhone or iPad.

iOS Deployment Target Version

100ms React Native Package is supported for iOS 12 and above versions. Ensure that the Minimum iOS Deployment Target Version is set to 12.0 or above in your Xcode project & the Podfile.

Role has Screenshare Permission

Ensure that the Role used to Join the Room has Screenshare permission Enabled from the 100ms Dashboard. If the Screenshare permission is not Enabled from the Dashboard, any users joining with this Role won't be able to start Screenshare. These users would still be able to see Screenshare performed by other Peers who have Screenshare permissions.

Screen Share Permission


Have a suggestion? Recommend changes ->

Was this helpful?

1234