Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

62
Video Killed The Rolex Star Chris Adamson • @invalidname CocoaConf San Jose • November, 2015

Transcript of Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Page 1: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Video Killed The Rolex Star

Chris Adamson • @invalidname CocoaConf San Jose • November, 2015

Page 2: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Media Support in watchOS 2.0

Page 3: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

watchOS 2.0

• App Extension runs on watch, not on iPhone

• New watchOS APIs for media playback and recording

• Prepare yourself, they’re limited!

Page 4: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

From watchOS 2.0 Transition Guide

You must implement your extension using the frameworks in the watchOS SDK instead of the iOS SDK. For any features not available in the provided frameworks, you must rely on your iPhone app to perform the corresponding task.

Page 5: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

From watchOS 2.0 Transition Guide

Your extension now stores files and data on Apple Watch. Any data that is not part of your Watch app or WatchKit extension bundle must be fetched the network or from the companion iOS app running on the user’s iPhone. You cannot rely on a shared group container to exchange files with your iOS app. Fetching files involves transferring them wirelessly to Apple Watch.

Page 6: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Media files. The Watch app handles audio and video playback in your app. If your WatchKit extension downloads media files from the network or the companion iOS app, you must place those files in a shared group container that is accessible to both your Watch app and WatchKit extension. For more information about managing media-related files, see Managing Your Media

From watchOS 2.0 Transition Guide

Page 7: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Media functionality

• Video playback

• Audio playback

• Audio recording

Page 8: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Video Playback

• WKInterfaceMovie — Canned UI component for movie playback

• WKInterfaceController — A/V features provided by primary UI controller class

Page 9: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

WKInterfaceMovie

A WKInterfaceMovie object lets you play back video and audio content directly from your interface. A movie object displays a poster image with a play button on top of it. When the user taps the play button, WatchKit plays the movie in a modal interface.

Page 10: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)
Page 11: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

// WKInterfaceMovie.h // WatchKit

WK_AVAILABLE_WATCHOS_ONLY(2.0) @interface WKInterfaceMovie : WKInterfaceObject

- (void)setMovieURL:(NSURL *)URL; - (void)setVideoGravity:(WKVideoGravity)videoGravity; // default is WKVideoGravityResizeAspect - (void)setLoops:(BOOL)loops;

- (void)setPosterImage:(nullable WKImage *)posterImage;

@end

Page 12: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Video Gravity• Conceptually identical to video gravity constants in AV

Foundation

• Resize — stretch pixels to fill container

• Aspect (Fit) — honoring aspect ratio, scale to reach one set of bounds (top/bottom or right/left), then letter-/pillar-box

• Aspect Fill — honoring aspect ratio, scale to reach both sets of bounds, allowing contents to be clipped if needed

Page 13: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Original 16:9 Frame

Page 14: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

WKVideoGravity.ResizeAspect

This is the default for WKInterfaceMovie.videoGravity

Page 15: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

WKVideoGravity.ResizeAspectFill

Page 16: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

WKVideoGravity.Resize

Please never do this

Page 17: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

WKInterfaceController

• Media playback and recording methods provided by the base controller class

• Can use these to play video whenever the app decides it’s time to do so

Page 18: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

- (void)presentMediaPlayerControllerWithURL:(NSURL *)URL options:(nullable NSDictionary *)options completion:(void(^)(BOOL didPlayToEnd, NSTimeInterval endTime, NSError * __nullable error))completion WK_AVAILABLE_WATCHOS_ONLY(2.0);

- (void)dismissMediaPlayerController WK_AVAILABLE_WATCHOS_ONLY(2.0);

options dictionary of presentMediaPlayerController takes WKVideoGravity key, uses the WKVideoGravity constants

Page 19: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Video Considerations

Page 20: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

1280x720 320x180

921,600 pixels 57,600 pixels

1/16 the size!

Page 21: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Encoding Guidelines

• Video Codec: H.264 High profile

• Bitrate: 160 kbps, 30 frames/sec

• Size: 320x180 (landscape), 208x260 (portrait)

• Audio: 32 kbps

Page 22: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

160 kbps video

Page 23: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

93% smaller file size!

Page 24: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

WKInterfaceController Audio

• Playback works just like video

• Same recommendation for audio bitrate: 32 kbps

• Audio playback always uses Bluetooth headphones/speakers if paired, otherwise internal speaker

Page 25: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Audio Recording

-(void)presentAudioRecordingControllerWithOutputURL:(NSURL *)URL preset:(WKAudioRecordingPreset)preset maximumDuration:(NSTimeInterval)maximumDuration actionTitle:(nullable NSString *)actionTitle completion:(void (^)(BOOL didSave, NSError * __nullable error))completion WK_AVAILABLE_WATCHOS_ONLY(2.0);

- (void)dismissAudioRecordingController WK_AVAILABLE_WATCHOS_ONLY(2.0);

Page 26: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

presentAudioRecordingControllerWithOutputURL:• actionTitle — A string to use in an “end recording”

button once audio capture is underway

• preset — WKAudioRecording quality preset

• NarrowBandSpeech (8 kHz sampling, 24 kbps AAC)

• WideBandSpeech (16 kHz sampling, 32 kbps AAC)

• HighQualityAudio (44.1 kHz sampling, 96 kbps AAC)

Page 27: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Audio Player

• “Headless” API for playing audio programmatically

• Assumes app provides own UI, or doesn’t have one

Page 28: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

WKAudioFileAsset

+ (instancetype)assetWithURL:(NSURL *)URL;

+ (instancetype)assetWithURL:(NSURL *)URL title:(nullable NSString *)title albumTitle:(nullable NSString *)albumTitle artist:(nullable NSString *)artist;

Page 29: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

WKAudioFilePlayerItem

• status — .Unknown, .ReadyToPlay, .Failed

• Notifications — Time jumped, Played to End, Failed to Play to End

+ (WKAudioFilePlayerItem *)playerItemWithAsset:(WKAudioFileAsset *)asset;

@property (nonatomic, readonly) WKAudioFileAsset *asset; @property (nonatomic, readonly) WKAudioFilePlayerItemStatus status; @property (nonatomic, readonly, nullable) NSError *error; @property (nonatomic, readonly) NSTimeInterval currentTime;

Page 30: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

WKAudioFilePlayer+ (instancetype)playerWithPlayerItem: (WKAudioFilePlayerItem *)item;

- (void)play; - (void)pause;

- (void)replaceCurrentItemWithPlayerItem: (nullable WKAudioFilePlayerItem *)item;

@property(nonatomic, readonly, nullable) WKAudioFilePlayerItem *currentItem;

@property (nonatomic, readonly) WKAudioFilePlayerStatus status; @property (nonatomic, readonly, nullable) NSError *error;

@property (nonatomic) float rate;

@property (nonatomic, readonly) NSTimeInterval currentTime;

Page 31: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

WKAudioFileQueuePlayer@interface WKAudioFileQueuePlayer : WKAudioFilePlayer

+ (instancetype)queuePlayerWithItems: (NSArray<WKAudioFilePlayerItem *> *)items;

- (void)advanceToNextItem;

- (void)appendItem:(WKAudioFilePlayerItem *)item;

- (void)removeItem:(WKAudioFilePlayerItem *)item;

- (void)removeAllItems;

@property(nonatomic, readonly) NSArray<WKAudioFilePlayerItem *> *items;

@end

Page 32: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

And that’s it!

Page 33: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Video Killed The Rolex Star

Chris Adamson • @invalidname CocoaConf San Jose • November, 2015

Slides available at slideshare.net/invalidname Code (eventually) at github.com/invalidname

Page 34: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Now wait a darn minute!

Page 35: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)
Page 36: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

AVFoundation, Core Image, and Core Audio are huge and complex, but required for lots of app types. Will any audio, video, or image APIs be available? Will they only be possible through limited, high-level interfaces?

http://www.marco.org/2015/05/28/watch-sdk-questions

Page 37: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Available System Technologies Extensions built specifically for watchOS 2 have access to the following system frameworks:

ClockKit Contacts Core Data Core Foundation Core Graphics Core Location Core Motion EventKit Foundation

HealthKit HomeKit ImageIO MapKit Mobile Core Services PassKit Security Watch Connectivity WatchKit

Notice the absence of AV Foundation, Core Audio, Core Media, and Core Video

Page 38: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

From watchOS 2.0 Transition Guide

You must implement your extension using the frameworks in the watchOS SDK instead of the iOS SDK. For any features not available in the provided frameworks, you must rely on your iPhone app to perform the corresponding task.

Page 39: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

overcast.fm

Page 40: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)
Page 41: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

AUGraph

AUFilePlayer AURemoteIO

AVAudioEngine is conceptually similar, but can’t do a step we need later, so this is the Core Audio approach

Page 42: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

AUGraph

AUFilePlayer AURemoteIOAUNew TimePitch

Page 43: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Offline AUGraphs

AUFilePlayer AUGeneric Output

AUNew TimePitch

AudioUnit Render()

+ExtAudioFile

Write()

Page 44: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

OSStatus timeShift(NSURL *inSourceURL, NSURL *inDestinationURL, float inSpeed) { OSStatus err = noErr; // crate graph AUGraph auGraph; err = NewAUGraph(&auGraph); if (err != noErr) {goto fail;} // goto fail, go directly to fail... AudioComponentDescription compDesc = {0};

// file player NSLog (@"Making AUFilePlayer"); AUNode filePlayerNode; AudioUnit filePlayerUnit; compDesc.componentType = kAudioUnitType_Generator; compDesc.componentSubType = kAudioUnitSubType_AudioFilePlayer; compDesc.componentManufacturer = kAudioUnitManufacturer_Apple;

err = AUGraphAddNode(auGraph, &compDesc, &filePlayerNode); if (err != noErr) {goto fail;} // goto fail, go directly to fail... err = AUGraphNodeInfo(auGraph, filePlayerNode, NULL, &filePlayerUnit); if (err != noErr) {goto fail;} // goto fail, go directly to fail... // AUNewTimePitch NSLog (@"Making AUNewTimePitch"); AUNode timePitchNode; AudioUnit timePitchUnit; memset(&compDesc, 0, sizeof(compDesc)); compDesc.componentType = kAudioUnitType_FormatConverter; compDesc.componentSubType = kAudioUnitSubType_NewTimePitch; compDesc.componentManufacturer = kAudioUnitManufacturer_Apple;

// and another 100 lines or so of this!

Page 45: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

?

Page 46: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Watch ConnectivityThe Watch Connectivity framework (WatchConnectivity.framework) provides a two-way communications conduit between an iOS app and a WatchKit extension on a paired Apple Watch. Apps use this framework to pass files and data back and forth. Most transfers happen in the background when the receiving app is inactive. When the app wakes up, it is notified of any data that arrived while it was inactive. Live communication is also possible when both apps are active.

Page 47: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Demo (beginning)

Page 48: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Phone: Activate WCSession

if WCSession.isSupported() { WCSession.defaultSession().delegate = self WCSession.defaultSession().activateSession() NSLog ("iPhone WCSession ready") }

Page 49: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Watch: Activate WCSession

if WCSession.isSupported() { WCSession.defaultSession().delegate = self WCSession.defaultSession().activateSession() NSLog ("Watch WCSession ready") }

Page 50: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Phone: transfer filelet transfer = WCSession.defaultSession().transferFile(url, metadata: nil) NSLog ("transferring: \(transfer)")

//MARK: WatchConnectivity delegate func session(session: WCSession, didFinishFileTransfer fileTransfer: WCSessionFileTransfer, error: NSError?) { NSLog ("didFinishFileTransfer: \(fileTransfer), error: \(error)") }

Page 51: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Watch: receive file

func session(session: WCSession, didReceiveFile file: WCSessionFile) { NSLog ("didReceiveFile: \(file)") let docsURL = NSFileManager.defaultManager().URLsForDirectory( NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).first let storageURL = docsURL?.URLByAppendingPathComponent( file.fileURL.lastPathComponent!) do { try NSFileManager.defaultManager().copyItemAtURL(file.fileURL, toURL: storageURL!) pushControllerWithName("player", context: storageURL) } catch let error as NSError { NSLog ("copy error: \(error)") } }

file: If you want to keep the file referenced by this parameter, you must move it synchronously to a new location during your implementation of this method. If you do not move the file, the system deletes it after this method returns.

Page 52: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Demo (continued!)

Page 53: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

What about video?

Page 54: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)
Page 55: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)
Page 56: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

AVAssetExportSession

AVAssetExportSession

Page 57: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Export Preset Names for Apple DevicesYou use these export options to produce files that can be played on the specific Apple devices.DeclarationSWIFTlet AVAssetExportPresetAppleM4VCellular: String let AVAssetExportPresetAppleM4ViPod: String let AVAssetExportPresetAppleM4V480pSD: String let AVAssetExportPresetAppleM4VAppleTV: String let AVAssetExportPresetAppleM4VWiFi: String let AVAssetExportPresetAppleM4V720pHD: String let AVAssetExportPresetAppleM4V1080pHD: String let AVAssetExportPresetAppleProRes422LPCM: String

Page 58: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

AVAssetWriter

• Low-level access for writing media files

• Allows you to specify output size, encoding settings, bitrate, etc.

• Requires you to write each CMSampleBuffer individually

Page 59: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

AVAssetWriterInput

SWIFTlet AVVideoCodecKey: String let AVVideoCodecH264: String let AVVideoCodecJPEG: String let AVVideoCodecAppleProRes4444: String let AVVideoCodecAppleProRes422: String let AVVideoWidthKey: String let AVVideoHeightKey: String let AVVideoCompressionPropertiesKey: String let AVVideoAverageBitRateKey: String let AVVideoQualityKey: String let AVVideoMaxKeyFrameIntervalKey: String let AVVideoProfileLevelKey: String let AVVideoProfileLevelH264Baseline30: String let AVVideoProfileLevelH264Baseline31: String let AVVideoProfileLevelH264Baseline41: String let AVVideoProfileLevelH264Main30: String

let AVVideoProfileLevelH264Main31: String let AVVideoProfileLevelH264Main32: String let AVVideoProfileLevelH264Main41: String let AVVideoProfileLevelH264High40: String let AVVideoProfileLevelH264High41: String let AVVideoPixelAspectRatioKey: String let AVVideoPixelAspectRatioHorizontalSpacingKey: String let AVVideoPixelAspectRatioVerticalSpacingKey: String let AVVideoCleanApertureKey: String let AVVideoCleanApertureWidthKey: String let AVVideoCleanApertureHeightKey: String let AVVideoCleanApertureHorizontalOffsetKey: String let AVVideoCleanApertureVerticalOffsetKey: String

Video SettingsThese constants define dictionary keys for configuring video compression and compression settings for video assets.

- initWithMediaType:outputSettings:sourceFormatHint:

Page 60: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

AVAssetReader Output

AVAssetReader Output

AVAssetWriter Input

AVAssetWriter Input

Audio path (output settings to change bitrate)

Video path (output settings to change size, encoding, bitrate)

Page 61: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Takeaways

• Basic support for file-based audio/video playback and audio recording

• Playback files are either in your bundle or downloaded by your iOS app + extension

• Any downloading or media processing needs to be performed on your iPhone, then sent to watch extension/app via Watch Connectivity

Page 62: Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)

Video Killed The Rolex Star

Chris Adamson • @invalidname CocoaConf San Jose • November, 2015

Slides will be at slideshare.net/invalidname Code (eventually, maybe) at github.com/invalidname