Graphics and Games • iOS, macOS, tvOS • 23:57
Learn how you can integrate ReplayKit into your Mac apps and games to easily share screen recordings or broadcast live audio and visuals online. We’ll show you how to capture screen content, audio, and microphone input inside your Mac apps, and even broadcast your video to a live audience. For more on getting started with this framework in your app, be sure to see “Live Screen Broadcast with ReplayKit” and "What's New with Screen Recording and Live Broadcast" from previous years.
Speaker: Johnny Trenh
Downloads from Apple
Transcript
Hello and welcome to WWDC. Hello and welcome to the ReplayKit for macOS session. My name is Johnny Trenh, and I'm a software engineer on the ReplayKit team. Today, I'm really excited to talk to you about ReplayKit on the Mac platform, as well as some new, exciting features for all platforms.
I've got a lot to cover, so let's get started. ReplayKit is a framework that gives you the ability to record or capture your application's screen, audio and microphone content to a video that people can edit, save and share. ReplayKit also gives you the ability to broadcast your application's screen, audio and microphone to other third-party broadcast services to be viewed all over the world. ReplayKit originally launched for iOS in 2015, and in 2016, ReplayKit for tvOS was introduced. This year, I'm pleased to announce ReplayKit is coming to the Mac.
All the great features that are available to your iOS and tvOS applications will now be made available for your Mac applications. You can now record, capture and broadcast your Mac application to everyone in the world. The main goal in bringing ReplayKit to the Mac was to ensure that you have access to all the great features that were already shipping with iOS and tvOS, along with the same HD quality, low performance impact and built-in privacy safeguards. Now, as you can imagine, applications work quite differently on the Mac when compared to iOS or tvOS, so let's take a look at the major ReplayKit features in the Mac. And I'll start with screen recording.
Screen recording gives you the ability to record your application's screen, audio and microphone into a video that people can then edit, save or share. How does screen recording work under the hood? Well, let's take a look. Your application will call into RPScreenRecorder to get the shared recorder instance.
With the shared instance, you call startRecording, at which point, ReplayKit will start to capture the screen, audio and microphone samples from your application, and start writing those samples into a movie file. When your application calls stopRecording, ReplayKit saves the recording, calls the preview and share extension, and passes that information back up through RPPreviewViewController. You then present the previewViewController, which allows people to edit, save or share that movie file.
Why don't we jump in and see how we can start in-app recording in a project? So here, I wanna talk a little bit about our project first. I'm here on our main storyboard. So, the main storyboard, we have three buttons that are gonna indicate the different functionalities that we're going to express and build out throughout this demo.
We're only gonna focus on a Start Recording button right now that you see right here on our storyboard. So this button is already hooked up to our code via IBOutlets and through the storyboard. What we're gonna look at now is the IBAction for what happens when someone presses this button. So here in our code, the IBAction is right here for recordButtonTapped.
We're gonna do two different types of things with this button. We're actually gonna have the button pull double duty for us. If we're actively recording, this button's gonna stop recording. But if we're not actively recording, this button's gonna start recording for us. So let's go ahead and take a look at what happens when we start recording. So here is our IBAction, and we're gonna look at the method startRecording. This is very easy. The first thing you wanna do is you wanna go ahead and call RPScreenRecorder and get the shared recorder instance.
From there, we're gonna go ahead and call startRecording. After that, this entire block is going to be executed right here. So, the first thing you wanna do, 'cause we're gonna give back an error, is you wanna see if we have an error. If we don't have an error, then we're properly recording, and everything has started up fine.
So here, we say if there's no error, all we're simply gonna do is we're gonna go ahead and update our recording state. What do we do when we update our recording state? Actually, what we do when we update our recording state is we simply set the button to reflect that.
Are we recording or not recording? "Start" or "stop," and that's it, and we update our state. After that, we are now up and recording. ReplayKit is now actually capturing everything and recording a video for you. So what happens when your user taps the button again? Well, here, we're gonna go ahead and call stopRecording. And let's take a look at how that works.
Going down to the stopRecording method right here, once again, we're gonna go ahead and get the shared recorder instance, and then we're gonna go ahead and call stopRecording. When we do that, the completionHandler block is then executed. Now this looks a little bit complicated, but I'll walk you through it 'cause it's actually really simple. The first thing I'm gonna do is check to see if we have an error. If we don't have an error, let's check to see if we have a previewViewController.
Now, this is where it gets really interesting. What we wanna do with the previewViewController is, like we spoke about, you wanna display it so people can edit, save or share their video. So, we get the previewViewController, we go ahead and save a reference to that here, and then this is the important part, you want to make sure that you are a delegate of the previewViewController.
And I'll talk to you about why that's important in just a little bit. Then you wanna go ahead and share the sheet. In this case, I'm using mainWindow and beginSheet. You can share the sheet or show the sheet however which way you choose using UIKit, but in this example, we're gonna go ahead and use beginSheet.
And that's it. We're gonna go ahead and show the sheet. We do some error-checking here, and if there's errors, we print them. If not, then after that, we go ahead and set the recording state because we're no longer recording. So, let's take a look at what happens after the user interacts with the previewViewController. That's why we wanna be part of the delegate. So, I'm gonna go ahead and scroll down here, and here is our delegate, previewViewControllerDidFinish.
This is gonna be called when the user is done using the previewViewController. They're gonna go ahead and click Save or exit out of it, and that's when you get notified. So the reason why this is important is because we wanna keep a reference to that previewViewController so that we can dismiss it.
And that's it. Just like that, you're up and running with ReplayKit and recording your application to a video. Now that you've seen what it looks like in code, I wanna take a moment to highlight some of the differences with RPPreviewViewController. People will be able to edit and trim the recording by tapping on the Edit button in the previewViewController.
Saving the video will be a very familiar experience for people as it follows the standard macOS file-save flow. With just two simple API calls, you're now up and running and recording with ReplayKit. With in-app screen recording, you and your application don't have access to the movie file. ReplayKit handles the creation of the movie, and people drive the sharing and saving of the recorded session. However, in the past, I've received a lot of requests from other developers about having access to the recorded movie. I think we finally have an answer for those requests.
This is how screen recording currently works. As you can see, the movie file is created and saved within ReplayKit, and your application has no access to the recorded movie itself. It is only provided with a previewViewController for people to edit and save the video. But what if instead of ReplayKit handling the video file, we give your application direct access to the video? You can now do that with our new stop API. The new stopRecording(with URL will allow you to provide a destination URL, and ReplayKit will write and save the recorded movie to that URL.
You now have direct access to the recorded movie, and you can incorporate them into your application. With it, you can create and manage ReplayKit videos in your application. You can even create a custom video editor and have it integrated right into your own app experience. These are just some of the new and exciting experiences you can create using the new stopRecording API.
But you might be thinking, "What if I don't want ReplayKit to make the movie recording for me? What if I want more control of my application's video and audio content?" And for that, you can use in-app screen capture, the next major feature we've brought to the Mac for ReplayKit.
With in-app screen capture, ReplayKit will send the audio and video samples straight to your application's process, where you'll have complete control in how to use them. Let's go ahead and take a quick look at how that works. You've already seen how in-app screen recording is done by interacting with the shared screen recorder instance, but instead of calling startRecording, when startCapture is called on the shared recorder, ReplayKit will start to capture your audio, visual and microphone samples from your application.
However, instead of ReplayKit creating and managing a movie for you as we do in recording, we send all audio and visual samples back to your application process. Let's jump back into our sample code project and see how we can get started with in-app capture for the Mac. Okay, so we're back into our project here, and once again, we're taking a look at the storyboard.
Remember, like we said before, we're building out this demo so that we can cover all three of the major functionalities in ReplayKit. We've already covered the Start Recording button. Now we're gonna go ahead and focus on the Start Capture button, and let's take a look at how that looks like inside the code.
So here we have an IBAction that's tied to the button from our storyboard. Just like with recording, our button's gonna pull double duty. If we're actively not recording or broadcasting or capturing, tapping this button's gonna start capture. Now, if we are actively capturing, tapping this button's gonna stop capture. So let's go ahead and take a look at what happens when we hit Start Capture.
Here's our code for startCapture. Now, it looks a little bit crazy, but I'll talk to you step-by-step. Here, we're gonna go ahead and call ScreenRecorder and get the shared recorder instance. From there, you're gonna call startCapture. StartCapture is gonna take in two different handlers. The first handler is going to be your sample handler, which we see here.
This block of code is executed every time ReplayKit gives you a sample, whether it be audio, video or microphone. So this block of code is run continuously. Now, the second handler is going to be the completionHandler, which we have here. Now, the completionHandler is called only once when you start capture.
This is much like the recording completionHandler where it signifies to you that capture has started, and will give you an error if there's something that happened in the process. So, let's take a look at the completionHandler first, and then we'll take a look at what we do when the samples come in.
Here in the completionHandler, just like with recording, we're gonna check to see if we have any errors. If we don't have any errors, we're gonna set the captureState much like we did with the recording. And what did we do in the recording captureState? Well, we set the button title, our active state internally, and we disabled the other buttons. And we set up the cameraView. Now, we saw this back in the recording as well. The cameraView's gonna allow you to use the camera PIP to add to your application.
So, what happens when we do have an error? We're gonna go ahead and print the error and move forward. Now let's go ahead and look at the sample block, because this is actually the most interesting bit because this gets called every time ReplayKit gives you a sample. So what you wanna do is, you wanna go ahead and take a look at all the different samples and all the different types that we give back. Then you wanna go ahead and decide what you wanna do with it. So, in this completion block, we give you back a sample. We give you back the sample type and an error.
And in this block right here, the first thing we do is we take a look to see if we have any errors. If we do, we go ahead and print them. If not, we're gonna run a switch statement on the type. Now, I'm sure in your application, you're gonna be doing a lot of really, really awesome things, but in this application, we'll keep it simple. We're gonna go ahead and go through each one of these types, and we'll process them separately. So for if it's a video type, we'll go ahead and say processAppVideoSample.
We'll do the same for audio and mike, and that's it. These samples are given to you from ReplayKit, and then you can do whatever you need to do with them. Let's take a look at what we do in this example, which is pretty simple. For all of our process methods, all we do is print.
Now, again, I'm sure you can do something far more interesting, and I'm excited to see what you do with it, but for this example, we just print, and that's it. We're getting all of these samples, and we print if we get an audio, we print if we get a video, and then we print if we get a mike.
And just like that, you're up and running with capture. With in-app screen capture, your application now has access to all the video and audio samples through ReplayKit. You can build dynamic in-app experiences that include screenshots. Now that you have access to the samples, you can add custom gaming overlays to the videos.
With this raw data, you can even implement a custom heads-up display like this for your application. The possibilities really are endless. Okay, so now we can record and we can capture. But what happens when you're ready to take things global? That's what live broadcast is for, the final major feature we've brought over to macOS for ReplayKit. With live broadcast, you give people the ability to stream their experience in your application to a third-party streaming service, allowing viewers from all over the world to watch live.
Let's take a quick look at how live broadcast works, before we jump into another example of how to set up a live broadcast. With in-app broadcast, you will call into RPBroadcastActivityController to display a picker for the available third-party streaming services. When people select a third-party streaming service, ReplayKit will set up the broadcast connection to the streaming service and give back a RPBroadcastController to your application.
The broadcastController is what you'll use to control the actual broadcast. Here is a quick overview of the three-part process that will allow you to start live-broadcasting your application. People will initiate a broadcast, and you'll present the broadcast picker. People will select a broadcast service, and you'll get back a broadcastController. Then you'll start to broadcast using that controller. Now that we have an idea of how that works, let's go ahead and take a look at how we can do that in a project.
Okay, so we're back in here in our storyboard. We've already covered the Start Recording and Start Capture. Now we're going to go ahead and focus on the Start Broadcast button. This is where things get really, really cool. All right, let's jump back into our Swift code here. And here we go. We're right here on the IBAction for the button tap. Once again, our broadcast button is gonna pull double duty for us. If we're active, the broadcast button is gonna stop broadcast, and if we're not active, it's going to start a broadcast.
Let's look at the more interesting bit, which is starting a broadcast. And you'll see how easy it is to actually get your app global and broadcasting to the world. All right. Here's our button. And we're gonna go ahead and do the presentBroadcastPicker. Like we covered in the slides, this is what happens when your application presents a picker for the available third-party broadcast so that people can choose.
Let's go ahead and jump into that code right here. And here it is. Well, first thing we're gonna do is we're gonna decide on a picker origin point. This is the origin point that is the bottom left-hand corner of the broadcast picker. Next, we're gonna go ahead and go on to the RPBroadcastActivityController and we're gonna call showBroadcastPicker at our origin point from the shared window of our application, and we're gonna pass in a nil for preferredExtension because for now, I just want to show all the available third-party streaming services. All right. When we call that, this block right here is gonna get executed.
And the first thing we want to do is check for errors. If we don't have an error, this is what we're gonna do. We're gonna go ahead and set the activityController that we give back from this call, and we're gonna keep a reference to it. You'll see why this is important later. And then you're gonna get an activity controller delegate. Now, the delegate part is a really, really important part. You're going to need to set yourself as the delegate or your application as the delegate. I will explain that in just a little bit.
If we have an error, go ahead and print it. So now you're gonna go ahead and present this picker. People are gonna go ahead and choose a broadcast that they want to do, and then you're gonna want to be notified when they're done. This is where the delegate comes in and why it's very, very important. Let's go ahead and take a look at the delegate call.
Here is the broadcastActivityController delegate... didFinishWith broadcastController. So we definitely want to go ahead and be a delegate here. And the reason why is when the user is done picking a third-party broadcast, ReplayKit will connect your application to the broadcast extension, and we're gonna give you back a broadcastController.
You need the broadcastController here... because that broadcastController is what you're gonna use to actually control the broadcast. So, in this call, we're gonna go ahead and check to see if we have an error. If we don't have an error, we're gonna go ahead and save the broadcastController because the broadcastController, again, controls your broadcast. You can pause, you can stop, start. All of those things are tied in with the broadcastController. Then we immediately use the broadcastController to call startBroadcast.
Then another block is executed. This is just like your startRecording. You call this, and we give you back an error. If there's an error, that means something went wrong, but if there isn't, you are now broadcasting to the third-party streaming service people have selected. What we do is update our broadcast state, like we did with the other ones, which is going to update our button, title, our internal state, and disable the other two ReplayKit functional buttons.
And that's it. You're now broadcasting. You are now sending audio and video to the user's selected third-party broadcast service. Now, what happens when we want to call stopBroadcast? This is interesting because when we call stopBroadcast, right here, this is why we want to save the broadcastController, because here we simply call our reference broadcastController, and we call finishBroadcast, at which point, this entire block will be executed.
Just like with stopRecording and stopCapture, it will give you an error if something has happened. First thing you want to do is check the error. If you don't have an error, set your broadcast state to false because we're no longer broadcasting, tear down your camera and anything else that you need to do. And that's it. You've just started and stopped a live broadcast with your application, and you're ready to take your application global.
I want to take a minute to go into a little bit more depth with the differences for in-app live broadcast that were made for macOS. If you're coming from iOS, you'll notice that we no longer interact with the RPBroadcastActivity- ViewController class. Instead, for macOS, that class has been replaced with RPBroadcastActivityController.
Due to macOS supporting multiple screens and windows, instead of calling loadBroadcastActivityViewController like we do in iOS, for macOS we call showBroadcastPicker with a CGPoint and a window reference. We also take in a preferredExtensionIdentifier if your application wants to stream directly to a specific broadcast service. The CGPoint passed in is a reference to the origin point of the passed-in window and represents the bottom left-hand corner for your broadcast picker. You're now global with your application using ReplayKit live broadcast. When you start to implement ReplayKit in your macOS applications, you're gonna notice something new that's been added to your menu bar.
With ReplayKit on macOS, in keeping with the goal of user privacy, you'll see a new menu-bar item shown on the menu bar every time you start an active recording, capture or broadcast session. The menu-bar icon indicates that there is an active ReplayKit session. It also serves as a way for people to stop the active ReplayKit session. You'll need to make sure that you adhere to RPScreenRecordingDelegate method call didStopRecording- WithPreviewViewController, as this will be called when people click on the menu-bar icon to stop a session.
For in-app capture and live-broadcast sessions, the RPPreviewViewController passed in will be nil. With that, you're now ready to add ReplayKit to record, capture and broadcast your applications and share them with the rest of the world. But we have one last thing we want to talk about, and that is... game controllers. I'm happy to announce that there is now ReplayKit support for the Game Controller framework in iOS, tvOS and macOS.
Game Controller framework will now have built-in ReplayKit functionality. Double-tapping the Share button on the PS4 controller or the Select button on the Xbox controller will start an in-app recording. Double-tapping again will automatically stop the in-app recording and save it to your photos for iOS and your desktop for macOS.
For applications already using ReplayKit, game controllers can start and stop recordings outside of your application's control. So, it's a good idea to make sure you're using key-value observing for both availability and recording properties on RPScreenRecorder, so that you can update your state as needed. Also be sure to follow the protocol method didStopRecordingWith- PreviewViewController on the RPScreenRecorderDelegate so that you can update your application state as needed. I'm super excited to be announcing ReplayKit support for game controllers. I hope to see it in your games very soon. Wow. We covered a lot today. Let's go ahead and take a quick recap of what we talked about.
I covered in-app screen recording, where ReplayKit will record your application's audio and video into a movie that people can edit, share and save. I covered in-app screen capture, where ReplayKit gives your application's audio and video samples right back to you in your application's process. I covered in-app screen broadcast, where ReplayKit sends your application audio and video to a third-party livestreaming service. And finally, I covered the new built-in functionality found in Game Controllers framework.
The sample code project, as well as the documentation regarding ReplayKit framework, or to revisit this session, please visit us at developer.apple.com. We look forward to seeing you add ReplayKit to your iOS, tvOS and now macOS applications. Thank you so much for watching our session on ReplayKit for macOS, and I hope you have a wonderful WWDC.