Screen Mirroring

With Connect SDK integrated in the mobile app, it can cast the screen and sound into the TV screen. This allows you to extend the screen of a mobile app to a larger TV screen and share it with your family. Screen mirroring is a way to display the entire app screen to the TV.

Note

This feature is only supported on webOS TV 22.

Requirements

Including the Connect SDK using CocoaPods and setting up for screen mirroring

Add pod "ConnectSDK" to your Podfile, and run pod install. Open the workspace file and run your project.

Note that screen mirroring runs on iOS 12 and higher. In case of Broadcast Upload Extension for Screen Mirroring, set the APPLICATION_EXTENSION_API_ONLY value to NO. Refer to the Podfile example below.

platform :ios, '12.0'

def app_pods
    pod 'ConnectSDK/Core', :git => 'https://github.com/ConnectSDK/Connect-SDK-iOS.git', :branch => 'master', :submodules => true
end

target 'ScreenMirroring-Sampler' do
  use_frameworks!
  app_pods

end

target 'ScreenMirroring-Extension-Sampler' do
  use_frameworks!
  app_pods

    post_install do |installer|
      installer.pods_project.targets.each do |target|
        target.build_configurations.each do |config|
          config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'No'
        end
      end
    end
end

ReplayKit - Broadcast Upload Extension

To capture iPhone screen, you need to implement Broadcast Upload Extension using Replay Kit. Refer to the link below.

How to Use Screen Mirroring

To use screen mirroring, follow these steps.

1. Search Devices

Search for devices (TVs) connected to your home network. You can set the filter to only search for TVs that support the screen mirroring function. Since the search for TVs takes some time, it should be started as soon as the app is running.

- (void)startDiscoveryTV {
    _isDiscoveringTV = YES;

    if (_discoveryManager == nil) {
        _discoveryManager = [DiscoveryManager sharedManager];
    }

    // Sets a device search filter (Screen Mirroring Capability) for devices that support screen mirroring
    NSArray *capabilities = @[
        kScreenMirroringControlScreenMirroring
    ];

    CapabilityFilter *filter = [CapabilityFilter filterWithCapabilities:capabilities];
    [_discoveryManager setCapabilityFilters:@[filter]];
    [_discoveryManager setPairingLevel:DeviceServicePairingLevelOn];
    [_discoveryManager registerDeviceService:[WebOSTVService class] withDiscovery:[SSDPDiscoveryProvider class]];
    [_discoveryManager startDiscovery];
}

2. Select a TV

Select the TV to run the screen mirroring on by using the Picker.

_discoveryManager.devicePicker.delegate = self;
[_discoveryManager.devicePicker showPicker:nil];

Once the user has selected a device, the application needs to store that device identifier to find it. This sample code uses NSUserDefaults to store its device identifier.

// MARK: DevicePickerDelegate
- (void)devicePicker:(DevicePicker *)picker didSelectDevice:(ConnectableDevice *)device {
        NSString *groupId = @"YOUR APP GROUP ID";
        NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:groupId];
        [sharedDefaults setObject:device.address forKey:kConnectableDeviceIpAddressKey];
        [sharedDefaults synchronize];
}

3. Start Screen Mirroring

Now you can run the screen mirroring. Start capturing the screen by creating an RPSystemBroadcastPickerView.

if (@available(iOS 12.0, *)) {
    RPSystemBroadcastPickerView *rpPickerView = [[RPSystemBroadcastPickerView alloc] initWithFrame:_rpPickerView.bounds];
    rpPickerView.preferredExtension = @"YOUR EXTENSION BUNDLE ID";
    rpPickerView.showsMicrophoneButton = NO;
    UIButton *button = rpPickerView.subviews.firstObject;
    button.imageView.tintColor = UIColor.whiteColor;
    [_rpPickerView addSubview:rpPickerView];
} else {
    /* UNAVAILABLE */
}

After the screen capture starts, you need to search once again with the information of selected TV device stored in the application.

- (instancetype)init {
        self = [super init];

    _discoveryManager = [DiscoveryManager sharedManager];

        NSString *groupId = @"YOUR APP GROUP ID";
        NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:groupId];
        _deviceAddress = [sharedDefaults stringForKey:kConnectableDeviceIpAddressKey];

    NSArray *capabilities = @[ kScreenMirroringControlScreenMirroring ];
    CapabilityFilter *filter = [CapabilityFilter filterWithCapabilities:capabilities];
        [_discoveryManager setCapabilityFilters:@[filter]];
    [_discoveryManager setPairingLevel:DeviceServicePairingLevelOn];
        [_discoveryManager registerDeviceService:[WebOSTVService class] withDiscovery:[SSDPDiscoveryProvider class]];
        [_discoveryManager startDiscovery];
    [_discoveryManager setDelegate:self];

        return self;
}

If you find your TV again, get a ScreenMirroringControl object to use the screen mirroring API. And then, you should immediately call the startScreenMirroring method.

// MARK: DiscoveryManagerDelegate
- (void)discoveryManager:(DiscoveryManager *)manager didFindDevice:(ConnectableDevice *)device {
    if ([device.address caseInsensitiveCompare:_deviceAddress] != NSOrderedSame) {
        return;
    }

    _device = device;
    _screenMirroringControl = [_device screenMirroringControl];

    if (_screenMirroringControl != nil) {
        [_screenMirroringControl startScreenMirroring];
        [_screenMirroringControl setScreenMirroringDelegate:self];
    }

    [_discoveryManager stopDiscovery];
}

Handle Runtime Errors

The following runtime errors might occur while the screen mirroring is running.

  • When the network connection is terminated
  • When the TV is turned off
  • When the screen mirroring is terminated on the TV
  • When the mobile device’s notification terminates the screen mirroring
  • When other exceptions occurred

For these errors, it is necessary to receive the error in real-time through the listener and respond appropriately.

// MARK: ScreenMirroringControlDelegate
- (void)screenMirroringDidStart:(BOOL)result {
    NSLog(@"screenMirroringDidStart %d", result);
}

- (void)screenMirroringDidStop:(BOOL)result {
    NSLog(@"screenMirroringDidStop %d", result);
}

- (void)screenMirroringErrorDidOccur:(ScreenMirroringError)error {
    NSLog(@"screenMirroringErrorDidOccur %d", error);
    [self finishBroadcastWithError:NULL];
}

4. Broadcast Upload Extension Handling

You can get CMSampleBufferRef and RPSampleBufferType via SampleHandler’s processSampleBuffer:withType:. It must be delivered to the screen mirroring API.

- (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType {
    // Handle video sample buffer and audio sample buffer for app
    if (_screenMirroringControl != nil) {
        [_screenMirroringControl pushSampleBuffer:sampleBuffer with:sampleBufferType];
    }
}

5. Stop Screen Mirroring

When you want to stop mirroring, call stopScreenMirroring.

- (void)broadcastFinished {
// User has requested to finish the broadcast.
if (_screenMirroringControl != nil) {
    [_screenMirroringControl stopScreenMirroring];
}