Chapter 16. Multimedia

The current version of iOS brings some changes to multimedia playback and functionality, especially the AVFoundation framework. In this chapter, we will have a look at those additions and some of the changes.

Note

Make sure that you have imported the AVFoundation framework in your app before running the code in this chapter.

16.1 Reading Out Text with the Default Siri Alex Voice

Problem

You want to use the default Siri Alex voice on a device to speak some text.

Solution

Instantiate AVSpeechSynthesisVoice with the identifier initializer and pass the value of AVSpeechSynthesisVoiceIdentifierAlex to it.

Discussion

Let’s create an example out of this. Create your UI so that it looks like Figure 16-1. Place a text view on the screen and a bar button item in your navigation bar. When the button is pressed, you will ask Siri to speak out the text inside the text view.

Figure 16-1. Text view and button in the UI

I’ve linked the text view to a property in my view controller called textView:

  @IBOutlet var textView: UITextView!

When the read button is pressed, check first whether Alex is available:

guard let voice = AVSpeechSynthesisVoice(identifier:
  AVSpeechSynthesisVoiceIdentifierAlex) else{
    print("Alex is not available")
    return
}
            

Instances of AVSpeechSynthesisVoice have properties such as identifier, quality, and name. The identifier can be used later to reconstruct another speech object. If all you know is the identifier, then you can re-create the speech object using that. The quality property is of type AVSpeechSynthesisVoiceQuality and can be equal to values such as default or enhanced. Let’s print these values to the console:

print("id = (voice.identifier)")
print("quality = (voice.quality)")
print("name = (voice.name)")
            

Then create the voice object (of type AVSpeechUtterance) with your text view’s text:

let toSay = AVSpeechUtterance(string: textView.text)
toSay.voice = voice
            

Last but not least, instantiate the voice synthesizer of type AVSpeechSynthesizer and ask it to speak out the voice object:

let alex = AVSpeechSynthesizer()
alex.delegate = self
alex.speak(toSay)
            

16.2 Downloading and Preparing Remote Media for Playback

Problem

You have some remote assets, such as sound files, and would like to download them, even if in the background. Along the way, you want to provide real-time feedback of the download process.

Solution

Follow these steps:

  1. Create an instance of AVURLAsset with the URL to your asset.
  2. Use the background(withIdentifier:) class method on URLSessionConfiguration to create a background session configuration.
  3. Create a session of type AVAssetDownloadURLSession and pass your configuration to it.
  4. Construct the URL where your asset has to be downloaded onto the disk.
  5. Use the makeAssetDownloadTask(asset:destinationURL:options) method of your session to create a download task of type AVAssetDownloadTask.
  6. Call the resume() method on your task to start the task.
  7. Conform to the AVAssetDownloadDelegate protocol to get events from your task.
Note

All the classes I discussed whose names start with “AV” are in the AVFoundation framework, so make sure to import it.

Discussion

Let’s imagine that you have an .mp4 file that you want to download and play back in your app. First set up your view controller:

import UIKit
import AVFoundation

class ViewController: UIViewController, AVAssetDownloadDelegate {
  
  let url = URL(string: "http://localhost:8888/video.mp4")!
  let sessionId = "com.mycompany.background"
  let queue = OperationQueue()
  var task: AVAssetDownloadTask?
  var session: AVAssetDownloadURLSession?
  
  ...
            
Note

I am using MAMP to start a local server on my machine and host the file video.mp4 on my own computer, hence the URL that you are seeing. You can and probably should change this URL to a valid media file that AVFoundation can handle, like mov or mp4.

Now define some of the delegate methods defined in AVAssetDownloadDelegate and URLSessionTaskDelegate:

func urlSession(_ session: URLSession, task: URLSessionTask,
                didCompleteWithError error: Error?) {
  // code this
}

func urlSession(_ session: URLSession,
                assetDownloadTask: AVAssetDownloadTask,
                didLoad timeRange: CMTimeRange,
                totalTimeRangesLoaded loadedTimeRanges: [NSValue],
                timeRangeExpectedToLoad: CMTimeRange) {
  // code this
}

func urlSession(_ session: URLSession,
                assetDownloadTask: AVAssetDownloadTask,
                didResolve resolvedMediaSelection: AVMediaSelection) {
  
}
            

Next, create an asset by its URL. At the same time, tell the system that you don’t want cross-site references to be resolved using a dictionary with a key equal to AVURLAssetReferenceRestrictionsKey and value of AVAssetReferenceRestrictions.forbidCrossSiteReference:

let options = [AVURLAssetReferenceRestrictionsKey :
  AVAssetReferenceRestrictions.forbidCrossSiteReference.rawValue]

let asset = AVURLAsset(url: url, options: options)
            

Now it’s time to create the configuration object of type URLSessionConfiguration:

let config = URLSessionConfiguration
  .background(withIdentifier: sessionId)
            

Create the session of type AVAssetDownloadURLSession:

let session = AVAssetDownloadURLSession(
  configuration: config,
  assetDownloadDelegate: self, delegateQueue: queue)
    
self.session = session
            
Note

You must have noticed that I keep a reference to the session and the task that we are going to create soon. This is so we can refer to them later and cancel or reuse them if necessary.

And last but not least, construct the task and start it:

guard let task = session.makeAssetDownloadTask(
  asset: asset,
  assetTitle: "Asset title",
  assetArtworkData: nil,
  options: nil) else {
    print("Could not create the task")
    return
}

self.task = task

task.resume()
            

16.3 Enabling Spoken Audio Sessions

Problem

You have an ebook reading app (or similar app) and would like to enable a specific audio session that allows your app’s audio to be paused—but another app is playing back voice on top of yours (such as an app that provides navigation information with voice).

Solution

Follow these steps:

  1. First, you will need to go through the available audio session categories inside the availableCategories property of your audio session and find AVAudioSessionCategoryPlayback.
  2. Then go through values inside the availableModes property of your audio session (of type AVAudioSession). If you cannot find AVAudioSessionModeSpokenAudio, exit gracefully.
  3. After you find the AVAudioSessionModeSpokenAudio mode, set your audio category to AVAudioSessionCategoryPlayback using the setCategory(_:with:) method of the audio session.
  4. Activate your session with the setActive(_:with:) method of your audio session.

Discussion

Suppose you are developing an ebook app and have a “Read” button in the UI that the user presses to ask the app to read the contents of the book out loud. For this you can use the AVAudioSessionModeSpokenAudio audio session mode, but you have to check first whether that mode exists. To find out, use the availableModes property of your audio session.

Let’s work on an example. Let’s find the AVAudioSessionCategoryPlayback category and the AVAudioSessionModeSpokenAudio mode:

guard session.availableCategories.filter(
  {$0 == AVAudioSessionCategoryPlayback}).count == 1 &&
  session.availableModes.filter(
    {$0 == AVAudioSessionModeSpokenAudio}).count == 1 else{
      print("Could not find the category or the mode")
      return
}
            

After you confirm that the category and mode are available, set the category and mode and then activate your audio session:

do{
  try session.setCategory(AVAudioSessionCategoryPlayback,
                          with:
    AVAudioSessionCategoryOptions.interruptSpokenAudioAndMixWithOthers)
  
  try session.setMode(AVAudioSessionModeSpokenAudio)
  
  try session.setActive(true, with:
    AVAudioSessionSetActiveOptions.notifyOthersOnDeactivation)
  
} catch let err{
  print("Error = (err)")
}
            
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset