WWDC2014 Session 505
Transcript
[ Silence ] >> Hello, good afternoon, welcome to session 505, [ Silence ] "Harnessing Metadata in Audiovisual Media". [ Silence ] I've heard we are competing with the "Intro to Swift" talk [ Silence ] so get intimate with your neighbors here. [ Silence ] My name is Adam Sonnanstine. [ Silence ] I'm an engineer on the AVFoundation team [ Silence ] and today we are going to talk about, of course, metadata. [ Silence ] So what do I mean by metadata? [ Silence ] Well, for the purposes of this talk we're going [ Silence ] to define metadata to mean any data that is stored [ Silence ] in movie files, streaming presentations, any other sort [ Silence ] of audiovisual presentation that describes the primary data, [ Silence ] like the audio and video that we think about when we think [ Silence ] of those sorts of presentations. [ Silence ] Some examples are always helpful. [ Silence ] One you should be familiar is iTunes metadata, [ Silence ] when you have this sort of the song names and the artists [ Silence ] and the album artwork in your iTunes library. [ Silence ] All these things are stored as the sort of metadata [ Silence ] that I'm talking about today in the files [ Silence ] in your iTunes library. [ Silence ] Besides iTunes metadata, we also have things [ Silence ] like location information. [ Silence ] If you have a movie that you took with your iPhone [ Silence ] or some other location-enabled device and you played it [ Silence ] in QuickTime player, that will show up in the info window [ Silence ] to tell you where you were when you took that movie. [ Silence ] That's also stored as the kind of metadata [ Silence ] that we are talking about today. [ Silence ] Some new features, we know that you're not always standing still [ Silence ] when you are taking your videos. [ Silence ] So new in iOS 8 and OS X Yosemite, we have features [ Silence ] that support things like dynamic location, that's a location [ Silence ] that changes over time. [ Silence ] So these are some new features that we are going to be talking [ Silence ] about later on that we are pretty excited about and, [ Silence ] in addition to location, this really applies to any sort [ Silence ] of metadata that you might want to add that changes [ Silence ] over time in your movie. [ Silence ] This is a screen shot of a demo app we'll show you later [ Silence ] but the circle and the annotation text that's all [ Silence ] stored as the same sort of timed metadata as a timed location. [ Silence ] So hopefully that whets your appetite a little bit. [ Silence ] We'll talk about what we're going to cover today, [ Silence ] we're going to start, I'm going to give you an intro to metadata [ Silence ] and AVFoundation, some of the classes that have been [ Silence ] around for a while for describing all sorts [ Silence ] of metadata, how to inspect that and how to author it. [ Silence ] We're going to talk more [ Silence ] about those new timed metadata features [ Silence ] and then I'll give you some best practices including some privacy [ Silence ] things to keep in mind and some other best practices. [ Silence ] So our first topic: metadata in AVFoundation..What kind [ Silence ] of classes are we going to be using to describe our metadata? [ Silence ] Well our primary model objects that we use [ Silence ] to describe both movie files or HLS streams is AVAsset. [ Silence ] AVAsset can contain any number of AV metadata objects [ Silence ] and each AVMetadataItem instance represents a single piece [ Silence ] of metadata, either your track name or your album mark, [ Silence ] even your location stuff that's going to be separate pieces [ Silence ] of metadata in our runtime environment. [ Silence ] So a closer look at AVMetadataItem: at its core, [ Silence ] it has two properties. [ Silence ] The first is identifier, which is actually a new property [ Silence ] and that is going to describe the kind [ Silence ] of metadata that you have. [ Silence ] In this example we have the song name and it's represented [ Silence ] by this long symbol name, [ Silence ] AVMetadataIdentifieriTunes MetadataSongName, [ Silence ] and then you have the value [ Silence ] which is the actual payload of the metadata item. [ Silence ] So for song name, it's the name of the song as a string. [ Silence ] As an example for cover art, [ Silence ] you can see that the value doesn't have [ Silence ] to be a string it can be an image or any other object [ Silence ] that supports both the NSObject and NSCopying protocols. [ Silence ] Now if you've used AVMetadataItem [ Silence ] in the past you might be familiar with the key [ Silence ] and key space properties. [ Silence ] Well the identifier I mentioned that was new, [ Silence ] it's new because it is a combination [ Silence ] of the old properties, key and key space. [ Silence ] So I'm not going to talking much about key and key space today [ Silence ] but mostly going to be talking about identifier going forward [ Silence ] as the way to describe your metadata. [ Silence ] Take a look at some of the built-in identifiers we have; [ Silence ] this is just a tiny sampling. [ Silence ] There's a lot of them. [ Silence ] You can find them [ Silence ] in AVMetadataIdentifiers.h. I've arranged them here according [ Silence ] roughly to the old notion of key space so that's just sort [ Silence ] of a sampling of the kind of metadata that we already know [ Silence ] that you might want to represent. [ Silence ] Going back to the metadata item itself, [ Silence ] we have a property that's also new called dataType, [ Silence ] which describes the native data type [ Silence ] that your metadata is representing. [ Silence ] So for the case of our song name it's stored as a string [ Silence ] so we see that the data type is a UTF8 string; [ Silence ] and these string constants [ Silence ] that represent the different data types are all defined [ Silence ] in CMMetadata.h. And besides the data type property, [ Silence ] we also have several type coercion properties [ Silence ] that you can use if you know you want to get your payload [ Silence ] in the form of a certain type of Objective C object. [ Silence ] So you have string value, number value, date value and data value [ Silence ] and those are going to give you exactly what you'd expect. [ Silence ] For the case of where our native payload is a string, [ Silence ] only string value is going to give you an interesting answer. [ Silence ] The rest will give you NULL. [ Silence ] For our artwork example where the payload is a JPEG image, [ Silence ] the top three are going to give you NULL [ Silence ] and the data value is going to give you the NSData [ Silence ] that you're looking for to grab the bytes of the JPEG image. [ Silence ] There are examples where you can have more [ Silence ] than one non-nil tech coercion method. [ Silence ] And one is creation date if you have the date represented [ Silence ] as a standard string format for dates. [ Silence ] You can either get the actual string that was stored that way [ Silence ] or you can ask the metadata item to give you an instance [ Silence ] of NSDate that describes the same thing [ Silence ] in a more convenient representation. [ Silence ] So that's your brief intro to AVMetadataItem, [ Silence ] we're going to be talking a lot about it throughout the talk. [ Silence ] Let's go back to AVAsset [ Silence ] so we can see how we actually get these metadata items. [ Silence ] So the easiest way is just to ask for all of the metadata [ Silence ] that applies to the entire asset. [ Silence ] There are types of metadata that apply to just parts of the asset [ Silence ] but this is how you get the metadata to, like, the location [ Silence ] and the song title that applies to the entire asset. [ Silence ] There's also a way to get just a subset of the metadata. [ Silence ] We have this notion of metadata format but I'm not going [ Silence ] to be talking about too much today but you can use it [ Silence ] to get just that subset of the metadata. [ Silence ] So for our example, when we are getting iTunes metadata, [ Silence ] we're going to use that AVMetadataFormatiTunesMetadata [ Silence ] and grab all of that using the metadataForFormat method. [ Silence ] And then from there we can use this filtering method, [ Silence ] metadataItemsFromArray, filtered by an identifier [ Silence ] to get just the items that correspond [ Silence ] to the song name identifier. [ Silence ] You might be wondering why you can have more [ Silence ] than one song name in a single asset? [ Silence ] We'll get back to that in just a little bit but first I want [ Silence ] to talk about how you load the payload of your metadata items. [ Silence ] AVMetadataItem conforms [ Silence ] to the AVAsynchronousKeyValueLoading.h [ Silence ] protocol. [ Silence ] This is a protocol we define ourselves in AVFoundation [ Silence ] and a lot of our core model objects conform to it [ Silence ] because a lot of times when you get an AVAsset [ Silence ] or an AVMetadataItem we haven't actually loaded the data behind [ Silence ] it yet. [ Silence ] So you can use this method, load values asynchronously for keys [ Silence ] to load the specific values you want and they'll do [ Silence ] that asynchronously so you're not blocking your main thread [ Silence ] with some sort of synchronous I/O or something like that. [ Silence ] So for this case we have our metadata item. [ Silence ] We're looking for the value so we just load the key value and, [ Silence ] when we get our completion handler, [ Silence ] we're going to check the status to make sure [ Silence ] that that loading succeeded and, assuming that we did, [ Silence ] we can then just go ahead and grab the value [ Silence ] and use it however we see fit. [ Silence ] So back to that whole multiple titles in one asset string. [ Silence ] Well, one reason we might have that is [ Silence ] if we have the asset localized in multiple languages. [ Silence ] So an asset can have the same metadata items [ Silence ] in multiple languages. [ Silence ] The example that we're going to talk [ Silence ] about is QuickTimeUserDataFullName [ Silence ] dat identifier. [ Silence ] If you use this identifier in your files, [ Silence ] then QuickTime Player, for example, can pick up the title [ Silence ] and display it in the title bar. [ Silence ] So this is just yet another example [ Silence ] of how metadata is used in our applications. [ Silence ] This particular example [ Silence ] of the movie actually has the title available in both English [ Silence ] and Spanish so here we have the English as the system language [ Silence ] so QuickTime Player picks that up, picks up the English title [ Silence ] but if we set our system language to Spanish it will pick [ Silence ] up the Spanish localization of that title instead. [ Silence ] These are represented as two distinct pieces of metadata [ Silence ] within the file and the way that you distinguish between them is [ Silence ] that they'll have different values [ Silence ] for these final two properties of AVMetadataItem locale [ Silence ] and extendedLanguageTag. [ Silence ] ExtendedLanguageTag is new in this release. [ Silence ] It's a BCP 47 language tag and it's particularly useful [ Silence ] when you want to distinguish written languages. [ Silence ] So that's one reason why you might have more [ Silence ] than one metadata item with the same identifier. [ Silence ] So I mentioned before that not all metadata applies [ Silence ] to the entire asset, well one example of that is metadata [ Silence ] that only applies to a particular track, [ Silence ] so for this example we have a special label attached [ Silence ] to our subtitle track called SDH, that stands for Subtitles [ Silence ] for the Deaf or Hard of Hearing [ Silence ] and that's basically just a more rich form of subtitles [ Silence ] that includes things like labeling who's talking [ Silence ] and mentioning sound effects [ Silence ] that are vital to the understanding. [ Silence ] We talked a little bit more about SDH and accessibility [ Silence ] in general last year in our "Preparing and Presenting Media [ Silence ] for Accessibility" talk so check that one out for more details. [ Silence ] For the purposes of this talk, [ Silence ] just know that to get this SDH label here, [ Silence ] it involves setting track-specific metadata. [ Silence ] So let's talk about how you actually find [ Silence ] out if your track has this metadata in it, [ Silence ] well you're going to use AVAsset track [ Silence ] and it has pretty much the exact same API [ Silence ] as AVAsset for reading metadata. [ Silence ] You have your metadata property; [ Silence ] you have your metadataForFormat method and so if we want [ Silence ] to find all the tagged characteristics that are [ Silence ] in an asset track, we're going to ask the track [ Silence ] for its metadata for the FormatQuickTimeUserData. [ Silence ] Once we have that we use [ Silence ] that same filtering method we saw before in order to get all [ Silence ] of the items that have the identifier, [ Silence ] QuickTimeUserDataTagged Characteristic. [ Silence ] So this is one example of tagged characteristics is the SDH [ Silence ] that I just talked about and it's the payload [ Silence ] of the metadata items that tells you what kind [ Silence ] of tagged characteristic you're dealing with. [ Silence ] We'll talk a little bit more detail about SDH [ Silence ] and how you author it in just a little bit. [ Silence ] So going back to our list [ Silence ] of identifiers you might have noticed some patterns [ Silence ] if you were looking closely. [ Silence ] Each of these groups has their own version of a title [ Silence ] or a song name or something like that. [ Silence ] We noticed that and come up with our own special kind [ Silence ] of identifier called a CommonIdentifier [ Silence ] which can be used when you want to look up, say, [ Silence ] for this example a title without caring exactly how it's stored [ Silence ] in your file. [ Silence ] Same for copyright here; we also have a common identifier [ Silence ] that represents copyright. [ Silence ] These are not the only common identifiers; [ Silence ] there's a whole list of them but these are just two examples. [ Silence ] So if we go back to our example where we're looking [ Silence ] for our iTunes song name, if we don't actually care [ Silence ] that the title of our asset is stored as iTunes metadata [ Silence ] and we just want a title so we can display it somewhere, [ Silence ] you can ask the asset for its array of commonMetadata [ Silence ] and this is all the metadata items [ Silence ] that can be represented using a common identifier. [ Silence ] Then you use that same filtering method we've been using [ Silence ] to filter down to just the ones [ Silence ] that have the CommonIdentifierTitle [ Silence ] and you can go from there with your title. [ Silence ] Also worth noting is [ Silence ] that AVAssetTrack has the same property, commonMetadata, [ Silence ] so you can do the same thing over there as well. [ Silence ] So that is your brief introduction [ Silence ] to inspecting metadata with AVFoundation. [ Silence ] Let's talk a little bit about authoring. [ Silence ] If you want to make your own files that have say location [ Silence ] or iTunes metadata in them, we have several different classes [ Silence ] that can write movie files, AVAssetExportSession, [ Silence ] AVAssetWriter and the capture movie and audio files outputs [ Silence ] and these all have the exact same redirect property called [ Silence ] simply, metadata. [ Silence ] So you give an array of metadata items and then [ Silence ] that will be written out to the file. [ Silence ] Similarly for track-specific metadata [ Silence ] like those tagged characteristics, [ Silence ] you can use an AVAssetWriter input [ Silence ] which also has the exact same property. [ Silence ] Now you are not limited to just writing out metadata [ Silence ] that you got from somewhere else, like another file [ Silence ] through the APIs we've been looking at. [ Silence ] You can also create your own metadata items [ Silence ] with a mutable subclass of metadataItem. [ Silence ] And as you might expect, this just has read/write properties [ Silence ] for all of the properties in AVMetadataItem. [ Silence ] So if we use an example of writing a subtitle track [ Silence ] that is marked as SDH, well it's actually two different tag [ Silence ] characteristics that you have to use [ Silence ] and so we'll create two different metadata items, [ Silence ] set both of their identifiers to the identifier we just saw, [ Silence ] the QuickTimeUserData tag characteristic, [ Silence ] but one of them will set the value [ Silence ] to TranscribesSpokenDialogue ForAccessibility [ Silence ] and the other will be DescribesMusicAndSound [ Silence ] ForAccessibility. [ Silence ] Then we get the subtitle AssetWriterInputthat's going [ Silence ] to write our subtitle track and set that array of the two items [ Silence ] on our asset writer input. [ Silence ] So that's how you would author a subtitle track [ Silence ] that is marked as SDH. [ Silence ] Just one example of using tag characteristics [ Silence ] in AVMutableMetadataItem. [ Silence ] Special note about AVAssetExportSession: [ Silence ] by default the ExportSession is actually going to take any [ Silence ] of the metadata that's in the source asset [ Silence ] that you're exporting. [ Silence ] It's going to copy that over to the output file. [ Silence ] Now that's not the case if you set metadata [ Silence ] on its metadata property. [ Silence ] That will be the signal to tell the ExportSession [ Silence ] to ignore the metadata in the source file [ Silence ] and instead write just what you put on the property. [ Silence ] So if you want to do augmentation of the metadata [ Silence ] or some other sort of modification you'll want to grab [ Silence ] that array of metadata, make a mutable copy [ Silence ] and do any adjustments that you want and then set [ Silence ] that on the metadata property. [ Silence ] So that's just a quick note about ExportSession. [ Silence ] The last note about authoring metadata is HTTP Live Streaming, [ Silence ] this is actually a new feature in iOS 8, OS X Yosemite [ Silence ] and you can use a new tag called session-data tag [ Silence ] in your playlist, which has two required fields: [ Silence ] the data ID which is a lot like our identifiers [ Silence ] that we're talking about, a URI which can point to the payload [ Silence ] or a value which directly specifies the payload and, [ Silence ] optionally, some language information. [ Silence ] So here's an example that shows very similar [ Silence ] to what we saw before with the titles [ Silence ] in two different languages but this is the markup you'd use [ Silence ] for HTTP Live Streaming. [ Silence ] So for more information on reading [ Silence ] and writing metadata we do have some sample code, [ Silence ] it's called AVmetadataeditor and for more information [ Silence ] about the details of writing HTTP Live Streaming metadata see [ Silence ] the documents at this URL. [ Silence ] All right so that is your crash course in metadata [ Silence ] in AVFoundation, our next topic is timed metadata. [ Silence ] So timed metadata, although I mentioned we have new features, [ Silence ] it is not a new concept. [ Silence ] We supported the notion of chapters for quite some time [ Silence ] and conceptually chapters are just an example [ Silence ] of times metadata. [ Silence ] Each of these chapter markers is just a piece of metadata [ Silence ] that is describing a particular range [ Silence ] of the timeline of the movie. [ Silence ] That's all that timed metadata is, [ Silence ] it's just metadata associated with a range of time. [ Silence ] So similarly, with our dynamic location example, [ Silence ] we have the path that's drawn here that's really just composed [ Silence ] of a number of pieces [ Silence ] of metadata indicating the current location, [ Silence ] each one of them associated with a particular time [ Silence ] in the movie's timeline. [ Silence ] So to demonstrate QuickTime Player's features [ Silence ] with dynamic location in Yosemite, [ Silence ] I want to bring my colleague, Shalini, [ Silence ] up to the stage for a demo. [ Silence ] >> Hi, I'm here to demonstrate how to read [ Silence ] and play back metadata using QuickTime Player. [ Silence ] Here I have a movie file which has both audio and video [ Silence ] and timed locations data stored in a different track. [ Silence ] So now if I bring this up in QuickTime Player, [ Silence ] this is the usual UI for audio and video. [ Silence ] New in OS X Yosemite: [ Silence ] in the Movie Inspector you can see a map view [ Silence ] if your movie file has location data. [ Silence ] Your map view is presented along with the route [ Silence ] where you have recorded this video. [ Silence ] So here the blue line indicates the path [ Silence ] where we recorded the video and the red pin is an indication [ Silence ] of the current location or the location [ Silence ] on the timeline of the movie. [ Silence ] So if I zoom in a little bit and start play, [ Silence ] you can see as the movie progresses the pin's location is [ Silence ] being updated to be in sync with the video. [ Silence ] I can drag the scrubber around [ Silence ] and you can see the pin moving back and forth. [ Silence ] I can also go and click at any point in the map [ Silence ] and you see the video seek to that location to present [ Silence ] where your video was when you were at that location. [ Silence ] This is map view in QuickTime Player on OS X Yosemite. [ Silence ] >> Thank you, Shalini. [ Silence ] So let's talk about what we just saw there. [ Silence ] So that location information was stored as timed metadata [ Silence ] in the file and in order to have QuickTime Player draw [ Silence ] that information on the map, we use AVAssetReader to read all [ Silence ] of the location information from that asset. [ Silence ] And because timed metadata is stored in its own track, [ Silence ] we use an AVAssetReaderTrackOutput to read [ Silence ] that data and we use a new class called AVAssetReaderOutput [ Silence ] MetadataAdaptor that knows how to give us that data in the [ Silence ] from of a class called AVTimedMetadataGroup. [ Silence ] Then from there we can grab each location [ Silence ] and draw that path on the map. [ Silence ] So AVTimedMetadataGroup is a very simple class. [ Silence ] It's really just these two properties: [ Silence ] an array of metadata items combined with a time range [ Silence ] that describes where in the movie that data applies. [ Silence ] So to see a little bit of code for using AssetReader [ Silence ] for this purpose, the first thing you want [ Silence ] to do is find the track [ Silence ] that contains your location information and we'll talk more [ Silence ] about how to do that in just a second. [ Silence ] Then you use that track to create an AssetReaderTrackOutput [ Silence ] and you use nil output settings [ Silence ] and then you'll create your metadataAdaptor [ Silence ] with that trackOutput. [ Silence ] And then, in a loop we just take your metadataAdaptor [ Silence ] and call the nextTimedMetadataGroup method [ Silence ] over and over again, doing something with each piece [ Silence ] of data, like drawing it on the map [ Silence ] until that method returns nil. [ Silence ] Then you know there's no more data to draw. [ Silence ] So in terms of finding the right track to read, [ Silence ] the way you're going to do that is by examining the tracks [ Silence ] in your asset and looking through the format description [ Silence ] of each track to find the identifiers you're looking for. [ Silence ] So you first start by getting the tracks [ Silence ] with the MediaTypeMetadata and then for each [ Silence ] of those tracks you're going to loop through all [ Silence ] of its format descriptions, usually there's only one [ Silence ] and for each format description you're going to grab its list [ Silence ] of identifiers using this function and check whether [ Silence ] that identifier array contains the identifier you're [ Silence ] looking for. [ Silence ] In this case we're looking [ Silence ] for the location ISO 6709 identifier. [ Silence ] So once we've found it we're good to go and we can resume [ Silence ] with the code on the previous slide. [ Silence ] So that's how QuickTime Player is drawing the map [ Silence ] or drawing the path on the map before you start playback. [ Silence ] The other thing that QuickTime Player does, as you saw, [ Silence ] is it can update the current location while you're doing [ Silence ] playback or even scrubbing around and the way it does [ Silence ] that while it's already playing the asset using an AVPlayerItem [ Silence ] and we're going to use a new class called [ Silence ] AVPlayerItemMetadataOutput that you attach to your PlayerItem, [ Silence ] which also notes how to vend this data in the form [ Silence ] of TimedMetadataGroups. [ Silence ] But unlike the asset reader, instead of getting all the data [ Silence ] up front you're going to be getting it piece [ Silence ] by piece as the movie plays. [ Silence ] So a little bit of code, [ Silence ] you first create your metadata output using the [ Silence ] initWithIdentifiers method [ Silence ] and in this case we're only interested in metadata that has [ Silence ] that location identifier so that's all we're going to get [ Silence ] by opting into this way. [ Silence ] Then you create a delegate that you define [ Silence ] and that's what's going [ Silence ] to receive the metadata during playback and you set [ Silence ] that delegate on your output [ Silence ] and tell us what cue you want us to send the data on. [ Silence ] Then you create or grab your AVPlayerItem and call addOutput [ Silence ] to attach your output, to attach your output to the playerItem [ Silence ] and finally make your player and associate your item [ Silence ] with the player as the current item and start playback. [ Silence ] It's important to get the smoothest playback experience [ Silence ] possible, we highly recommend that you do all of this sort [ Silence ] of setup work before you start playback [ Silence ] or even attach the item to the player. [ Silence ] So a little bit of look at what your delegate method might [ Silence ] look like. [ Silence ] There's only one delegate method; it's the metadataOutput, [ Silence ] didOutputTimedMetadataGroups, fromPlayerItemTrack method. [ Silence ] And the first thing you want to do is grab an item [ Silence ] that you can get your payload data from. [ Silence ] In this case, to keep things simple, [ Silence ] I'm just grabbing the first item from the first group but keep [ Silence ] in mind there could be multiple items, [ Silence ] there could even be multiple groups. [ Silence ] One reason there could be multiple groups given [ Silence ] to this method is that the metadata output will keep track [ Silence ] of whether the metadata is coming faster [ Silence ] than you're processing it and, if it is, it will start to batch [ Silence ] that up and give you the metadata in batches [ Silence ] when you're done with the previous batch of metadata. [ Silence ] So moving on with your item; you're going [ Silence ] to do this LoadValueAsynchronouslyForKeys [ Silence ] dance that we talked about before. [ Silence ] In this case, we're interested in the value [ Silence ] and data type properties so we're going [ Silence ] to load both of those. [ Silence ] I've admitted the error checking for brevity here [ Silence ] which you'll probably want to do that error checking [ Silence ] like we had in the other slide. [ Silence ] And once we have the completion handler we can ask the item [ Silence ] for its data type and make sure that's the data type we're [ Silence ] prepared to handle, in this case my code only knows how [ Silence ] to handle location information in ISO 6709 format so we got [ Silence ] to make sure that's the right data type and from there we go [ Silence ] and dispatch our code to the main thread [ Silence ] that will update our UI. [ Silence ] So that's how QuickTime Player is updating the location [ Silence ] metadata during playback. [ Silence ] Of course this is not the first API that we have offered [ Silence ] for reading timed metadata during playback. [ Silence ] There is an existing property called timedMetadata [ Silence ] on AVPlayerItem but I'm here to say [ Silence ] that the AVPlayerItemMetadataOutput [ Silence ] replaces that property for all of these use cases. [ Silence ] Now we're not deprecating the property yet [ Silence ] but we do recommend, if you're new to timed metadata, [ Silence ] just adopt the metadataOutput [ Silence ] and not worry about the property. [ Silence ] If you're already using the property version we do recommend [ Silence ] that you move over but just you know that you should make sure [ Silence ] that your code is working properly after that transition, [ Silence ] in particular I'll point [ Silence ] out that the metadataOutput will give you, for certain kinds [ Silence ] of HLS content, will give more specific identifiers [ Silence ] than the old property did. [ Silence ] So just make sure your code is prepared to handle that. [ Silence ] The last topic on reading timed metadata is Chapters. [ Silence ] Chapters, like I said, have been supported for some time; [ Silence ] they even have their own API: [ Silence ] chapterMetadataGroupsBest MatchingPreferredLanguages. [ Silence ] This is on AVAsset. [ Silence ] This will give you an array of timed metadata groups [ Silence ] that contain items with the identifier, [ Silence ] QuickTimeUserDataChapter, and we've supported this [ Silence ] for some time for QuickTime movie files and M4Vs and, [ Silence ] new in iOS 8 is the-and OS X Yosemite-is support for chapters [ Silence ] in HTTP Live Streams as well as MP3 files. [ Silence ] And I'll tell you more about how [ Silence ] to author those HLS chapters in just a little bit. [ Silence ] So for more information, we have some sample code [ Silence ] that does approximately what QuickTime Player is doing, [ Silence ] where it can show your location during play back. [ Silence ] We also have a previous session about AssetReader that goes [ Silence ] into much more detail than I did here, called "Working with Media [ Silence ] in AVFoundation" from 2011. [ Silence ] So that's how you read and play back timed metadata. [ Silence ] Our next timed metadata topic is how you can create your own [ Silence ] movies that contain timed metadata. [ Silence ] We saw the screenshot before and I mentioned [ Silence ] that these annotations are stored as timed metadata and, [ Silence ] to show you this demo app, I'd like to invite Shalini back [ Silence ] up on stage to demo it. [ Silence ] >> This time let's look at an app on how [ Silence ] to author your own custom metadata movie files. [ Silence ] Here I have a video and if I would like to share some notes [ Silence ] with my friend, who is good at fixing colors in a movie, [ Silence ] I can now do that within the app. [ Silence ] To add annotations, I use a two-finger gesture, [ Silence ] I can use a pinch gesture to resize and then add a comment [ Silence ] which is enough for my whoever looks at the video later [ Silence ] to fix the colors there and then I begin playback. [ Silence ] And as playback progresses, I track the circle [ Silence ] to where I want this to be fixed. [ Silence ] And now that I have this annotation and I can write it [ Silence ] out along with the audio and video to do that, I hit "export" [ Silence ] and now we see an AV player view controller [ Silence ] which shows the exported movie along with the metadata [ Silence ] which was written to it. [ Silence ] So if I start playback you see the annotation is moving along [ Silence ] the timeline in the part in which I traced. [ Silence ] So if I scrub back in time you can see the annotation moving. [ Silence ] You might wonder that the annotation is baked [ Silence ] into the video frame; it is not. [ Silence ] It is being rendered real-time using AVPlayerItemMetadataOutput [ Silence ] and you can change the color or the font of the annotation. [ Silence ] So if I begin playback, you see the rendering is happening [ Silence ] in real time. [ Silence ] That's AVTimedAnnotationWriter, we have this available [ Silence ] as a sample code as well, thank you. [ Silence ] >> So that was a great demonstration [ Silence ] of not only the playback part of it but also how to write [ Silence ] that data into the file, so let's take a look at how [ Silence ] that was accomplished. [ Silence ] So we're going to use an AVAssetWriter to write the file [ Silence ] and we're going to use an AVAssetWriterInput in order [ Silence ] to write that metadata track to the file. [ Silence ] Just like the reader side, [ Silence ] the writer has a new class that's a metadataAdaptor [ Silence ] and that class knows how to interpret instances [ Silence ] of AVTimedMetadataGroup and write that into the file. [ Silence ] See a little bit of code; first thing we're going [ Silence ] to do is create our AssetWriter Input. [ Silence ] We're going to use the media type AVMediaTypeMetadata, [ Silence ] once again nil outputSettings and we're going to have [ Silence ] to provide a clue to the source format, well, [ Silence ] the format of the data that we're going to be appending. [ Silence ] We'll talk more about this [ Silence ] and why it's required on the next slide. [ Silence ] Then you simply create your metadataAdaptor [ Silence ] with the reference to that input and, as you generate [ Silence ] or receive your timed metadata groups, [ Silence ] you simply use the appendTimedMetadataGroup method [ Silence ] to continue to append those and write them to the file. [ Silence ] So what's the deal with that source format thing. [ Silence ] Well, it turns out in order for AVAssetWriter to be able [ Silence ] to write your metadata in the most efficient way possible, [ Silence ] it needs to know up front exactly what kind [ Silence ] of metadata it is going to be writing. [ Silence ] This will result in the most lowest storage overhead in terms [ Silence ] of the number of bytes your file takes up [ Silence ] and it also has a effect on how efficient it is [ Silence ] to play back this kind of contents. [ Silence ] You don't want to be using too much power [ Silence ] when you're playing this kind of content back. [ Silence ] So you do have some options in terms [ Silence ] of how you actually construct one of these format hits. [ Silence ] If you're reading [ Silence ] from AVAssetReader you can actually ask the track [ Silence ] that you are reading from to give you its list [ Silence ] of format descriptions and use one of those. [ Silence ] If you're creating the metadata group yourself or getting it [ Silence ] from some other source then you can use a new method called [ Silence ] copyFormatDescription that will give you back an instance [ Silence ] of CM format description that will do this job for you. [ Silence ] It's important to note that if you go this route you need [ Silence ] to make sure that the contents [ Silence ] of your metadata group are comprehensive in terms [ Silence ] of it containing every combination [ Silence ] of identifier data type and language tag [ Silence ] that you are going to be appending. [ Silence ] That is, it contains an item with each of those combinations. [ Silence ] Of course, since the CM format description is a CF type, [ Silence ] you'll need a CFRelease app when you're done. [ Silence ] Of course, there's one more way you can do this: [ Silence ] you can create the format description directly using [ Silence ] CoreMedia APIs. [ Silence ] And here you use this long name CMMetadataFormatDescription [ Silence ] CreateWith MetadataSpecifications function. [ Silence ] You're going to pass in the metadataType box. [ Silence ] That's the sort of metadata we've been talking [ Silence ] about this whole time with timed metadata. [ Silence ] And these metadata specifications it's just an [ Silence ] array of dictionaries. [ Silence ] Each dictionary contains those combinations I was talking [ Silence ] about before. [ Silence ] The identifier dataType and optionally extended language tag [ Silence ] so you want to make one of these metadata specifications [ Silence ] dictionaries for each combination you plan to append. [ Silence ] So the one thing that was not obvious about that demo is [ Silence ] that we're actually writing metadata timed metadata [ Silence ] that describes one particular other track. [ Silence ] So for the example of these annotations, [ Silence ] we're really just talking about the video track of the movie [ Silence ] and not the sound or anything else like that. [ Silence ] So just like we had a way of making track-specific metadata [ Silence ] that applied to the entire track, [ Silence ] with those tagged characteristics [ Silence ] that we saw before, you also have the ability [ Silence ] to formerly mark your metadata track [ Silence ] as describing one particular other track. [ Silence ] You do that with the addTrackAssociationWith [ Silence ] TrackOfInput method using as the parameter the AssetWriterInput [ Silence ] that you are using to write your video track. [ Silence ] And your receiver is the input that you are using [ Silence ] to write your metadata track. [ Silence ] You use the AssociationTypeMetadataReferent. [ Silence ] So that's how your create metadata that's timed [ Silence ] but also specific to a particular track. [ Silence ] The next thing we did that was interesting in that demo is [ Silence ] that we actually used our own custom identifiers. [ Silence ] So we had that big list of built in identifiers. [ Silence ] Well, you don't have to use those; [ Silence ] you can actually build your own [ Silence ] and as I mentioned before an identifier is just a combination [ Silence ] of key space and key and it has a particular format: [ Silence ] it's just a string but it is in a particular format [ Silence ] so to help you make your own custom identifiers, [ Silence ] we have this method, identifierFor Key, and keySpace, [ Silence ] it's a class method on AVMetadataItem. [ Silence ] There are some rules to follow: your key space needs [ Silence ] to be four characters long if you want to use it [ Silence ] for timed metadata [ Silence ] so we actually recommend you use our built-in key space, [ Silence ] the QuickTimeMetadata keySpace. [ Silence ] We also highly recommend you use reverse DNS notation [ Silence ] for your custom keys to avoid collisions [ Silence ] with other kinds of metadata. [ Silence ] So a brief code snippet you can see you can simply use this [ Silence ] method to make your custom identifier and then set [ Silence ] that on the identifier property of your mutableMetadataItem. [ Silence ] So in addition to custom identifiers you can also create [ Silence ] your own custom data types. [ Silence ] So we're all familiar by now, through this presentation, [ Silence ] with some of the built-in data types that we defined; [ Silence ] there's a lot more than these [ Silence ] but we've been using these quite heavily already. [ Silence ] These are really useful [ Silence ] but sometimes you want your data type information [ Silence ] to express more, maybe about the domain you're working in, [ Silence ] so if you are doing a serial number or a bar code kind [ Silence ] of thing you might want [ Silence ] to define a data type that's this sort of serial number [ Silence ] as string data type or barcode image as JPEG data type [ Silence ] so you have more specific information [ Silence ] about what your metadata actually contains. [ Silence ] The way that this works is, you have to tell us exactly how [ Silence ] to serialize that custom data type and the way you do [ Silence ] that is you tell us that your custom data type conforms to one [ Silence ] of our built-in data types. [ Silence ] So in this case the serial number conforms [ Silence ] to the UTF8 data type so under the hood it's UTF8 string, [ Silence ] but we know that it really represents a serial number [ Silence ] and the same with the barcode image. [ Silence ] The way that you do this is you register your data type using [ Silence ] the CMMetadataDataTypeRegistry RegisterDataType function that's [ Silence ] defined in Core Media. [ Silence ] You can't create your own custom base types [ Silence ] but you can create your own custom type that conforms [ Silence ] to our raw data built-in type [ Silence ] if your data type really is just a custom sequence of bytes. [ Silence ] So there are some rules to using AVAssetWriter [ Silence ] for writing timed metadata. [ Silence ] Most importantly, every metadata item that you append has [ Silence ] to have non-nil values for identifier, data type and value. [ Silence ] Your identifier has to conform to the format that we specify, [ Silence ] so we highly recommend using that utility method [ Silence ] that we just talked about. [ Silence ] The value has to be compatible with the data type [ Silence ] so you can tell us that your NSString value is an UTF8 string [ Silence ] but don't try telling us [ Silence ] that your custom class is a UTF8 string because we won't know how [ Silence ] to serialize that properly and the AssetWriter will fail. [ Silence ] As I mentioned before, you have to create your AssetWriterInput [ Silence ] with a format hint and that must be comprehensive [ Silence ] and we described that before. [ Silence ] So the last topic about AssetWriter [ Silence ] and timed metadata is a recipe for creating your own movies [ Silence ] that have the same sort of dynamic location [ Silence ] that we've seen a couple of times already. [ Silence ] To do this, you can use AVCapture audio [ Silence ] and video data outputs and target that data [ Silence ] at twin instances of AssetWriterInput and, [ Silence ] at the same time, grab information from Core Location [ Silence ] that represents the location information and write [ Silence ] that to its own AssetWriterInput. [ Silence ] For more detail about how to do that we've actually implemented [ Silence ] that and made it available as sample code, [ Silence ] so see AVCaptureLocation if you want to make your own movies [ Silence ] that contain dynamic location. [ Silence ] We also have sample code as Shalini mentioned [ Silence ] for the demo we just showed you, [ Silence ] that's called AVTimedAnnotationWriter. [ Silence ] And, of course, for more information about AssetWriter [ Silence ] in general, see that same talk I referenced earlier: [ Silence ] "Working with Media in AVFoundation". [ Silence ] Last two quick topics about timed metadata: ExportSession. [ Silence ] Just like we've said the asset ExportSession will [ Silence ] by default pass through any of your metadata that applies [ Silence ] to the entire asset or entire track, it will pass [ Silence ] that through, copy it to the output file. [ Silence ] It will do the same thing with timed metadata that exists [ Silence ] in the source file provided [ Silence ] that your destination file type is QuickTime Movie. [ Silence ] We'll talk more about file types in just a little bit [ Silence ] but basically ExportSession behaves exactly [ Silence ] as you would expect. [ Silence ] In our last timed metadata authoring topic is HTTP Live [ Silence ] Streaming chapters so if you want to author chapters [ Silence ] in your HLS stream, you can use the session-data tag we talked [ Silence ] about earlier and the special data ID, com.apple.hls.chapters. [ Silence ] Your URL should point to a JSON file [ Silence ] that describes the chapter information for that stream and, [ Silence ] of course for more detail on this, see that same link [ Silence ] that I referenced earlier for HTTP Live Streaming. [ Silence ] All right, so that is timed metadata, [ Silence ] our next topic is privacy. [ Silence ] Why is privacy important in this context? [ Silence ] Well, any time that you are writing your users data [ Silence ] to a file you need to be at least considerate [ Silence ] about their privacy and be aware that the metadata that you write [ Silence ] out to these movie files can contain user identifiable [ Silence ] information, the most obvious example of that is location. [ Silence ] And so because movie files can be distributed and we want [ Silence ] to protect the privacy of our users, [ Silence ] for our built-in sharing services, we do our best [ Silence ] to strip out any potentially user identifiable information, [ Silence ] such as this location and we recommend that you do the same. [ Silence ] So we've given you a utility [ Silence ] for that called AVMetadataItemFilter. [ Silence ] Right now there is only one filter that we make available [ Silence ] but it is geared towards privacy, [ Silence ] it is the metadata item filter for sharing and that will strip [ Silence ] out any of this sort of user identifying information [ Silence ] that we're talking about; location is only one example. [ Silence ] But it will also strip out anything it doesn't recognize, [ Silence ] because it doesn't know whether [ Silence ] that might contain user identifiable information. [ Silence ] So that includes any metadata that uses identifiers [ Silence ] that you define yourself. [ Silence ] It will leave in some things like metadata that's important [ Silence ] to the structure of the movie [ Silence ] and chapters are the best example of that, [ Silence ] and also any commercial related data like your Apple ID. [ Silence ] So to use the MetadataItemFilter you're going to first [ Silence ] of all create your filter and feed it your original array [ Silence ] of metadata items using this metadataItemsFromArray [ Silence ] filteredByMetadataItemFilter method. [ Silence ] This is a companion to that other filtering method based [ Silence ] on identifiers we've been using all day [ Silence ] and then once you have your filtered array [ Silence ] of metadata items just set that on your AssetWriter [ Silence ] or ExportSession as you normally would. [ Silence ] Well actually I mentioned ExportSession [ Silence ] but things can be simple if you're using the ExportSession [ Silence ] and only want to copy the metadata [ Silence ] from the source asset and not add your own. [ Silence ] You just set the filter on the ExportSession [ Silence ] and it will actually do the filtering for you, [ Silence ] this will filter both static and timed metadata [ Silence ] but it will only filter the metadata from the source asset. [ Silence ] If you set your own metadata on the metadata property, [ Silence ] it won't filter that for you; you'll need to do the process [ Silence ] that I just described of doing the filtering yourself. [ Silence ] The only other thing to keep in mind is [ Silence ] that the export may take more time [ Silence ] when the filter is being used because it has to go through [ Silence ] and examine all of the metadata items. [ Silence ] So that's privacy. [ Silence ] Our last section of the talk today is some assorted best [ Silence ] practices when you are writing your own files [ Silence ] that contain metadata. [ Silence ] First up, what if you're writing timed metadata [ Silence ] and you have multiple streams of metadata [ Silence ] that use different identifiers. [ Silence ] How do you get those into the same file? [ Silence ] Well, we actually have the situation in the demo app, [ Silence ] we have that circle is comprised of two different pieces [ Silence ] of information, the position and the radius. [ Silence ] So we're representing these [ Silence ] and the demo app is two distinct streams of metadata. [ Silence ] And so the most obvious way I can think of to get this [ Silence ] into a file is to use two different AVAssetWriterInputs, [ Silence ] which result in having two metadata tracks [ Silence ] in the output file, pretty simple. [ Silence ] But there is another way you can do it, [ Silence ] you could instead combine those two different types of metadata [ Silence ] into one timed metadata group and write [ Silence ] that to a single AssetWriterInput [ Silence ] and that will result in only one metadata track [ Silence ] in the output file that contains multiple different kinds [ Silence ] of identifiers. [ Silence ] There are some advantages to this approach, not the least [ Silence ] of which is it can result in lower storage overhead [ Silence ] and therefore as we always see more efficient playback. [ Silence ] But there are of course pros and cons to everything. [ Silence ] So you'll definitely want to consider combining [ Silence ] into one track your different metadata [ Silence ] if they are used together during playback [ Silence ] and they have identical timing. [ Silence ] This is definitely the case with the example we just saw [ Silence ] with the circle center and the circle radius. [ Silence ] If these are not true then you might not want to combine. [ Silence ] And in fact one instance where you definitely do not want [ Silence ] to combine, is if you have one type [ Silence ] of metadata that's associated with another track in the file, [ Silence ] so that's like our annotations are associated [ Silence ] with the video track, but then you have another type [ Silence ] of metadata like location that is associated [ Silence ] with the entire asset. [ Silence ] You don't want to combine those into one track, [ Silence ] otherwise your location in that example will become mistakenly [ Silence ] associated with just the video track, [ Silence ] and that's not what you want. [ Silence ] So that's how to deal [ Silence ] with multiple streams of timed metadata. [ Silence ] Next topic is duration of your timed metadata groups, [ Silence ] when you get a timed metadata group [ Silence ] from AVFoundation it's always going [ Silence ] to have a fully formed time range. [ Silence ] So that means it will have a start time and a duration. [ Silence ] We actually recommend when you make your own timed metadata [ Silence ] groups for a pending with the AVAssetWriter [ Silence ] that you don't bother giving us a duration. [ Silence ] And to see how that works, here's an example of a group [ Silence ] that starts at time 0 but it doesn't have a duration [ Silence ] so how do we know when it ends? [ Silence ] Well, of course we'll wait until you append the next one [ Silence ] and then we'll say that, "Okay, the end time [ Silence ] of the first group is the same [ Silence ] as the start time of the next one." [ Silence ] So this ensures that your metadata track is going [ Silence ] to have a continuous stream of contiguous metadata and we think [ Silence ] that for most cases this is the best way to store your metadata. [ Silence ] The way you accomplish this is, [ Silence ] when you're making your time range, [ Silence ] you just use KCMTimeInvalid for your duration [ Silence ] and we'll take care of the rest. [ Silence ] We do recognize that there are cases where you might not want [ Silence ] to have contiguous metadata, you might want [ Silence ] to author an explicit gap into your metadata stream and so, [ Silence ] for that, our recommendation is that you give us [ Silence ] in the middle there a group that contains zero items. [ Silence ] This is the best way to author a gap in the metadata. [ Silence ] And you can see we just do that by presenting an empty array [ Silence ] when we're creating our timed metadata group. [ Silence ] Notice that we're still using KCMTimeInvalid [ Silence ] for our duration here. [ Silence ] Just tell us when the beginning of the metadata silence, [ Silence ] so to speak, is and we'll figure out how long it lasts based [ Silence ] on when you append your next non-empty group. [ Silence ] So that's how you write gaps in your metadata. [ Silence ] Our last best practice, [ Silence ] I mentioned output file type before [ Silence ] and here's the longer explanation. [ Silence ] Well, AssetWriter [ Silence ] and AssetExportSessions support writing [ Silence ] to a wide variety of file types. [ Silence ] You've got QuickTime movie, MPEG4, and all sorts [ Silence ] of other kind of file types [ Silence ] and those file types can carry different kinds of metadata; [ Silence ] some have more restrictions than others about what kind [ Silence ] of metadata can go into that file type. [ Silence ] So the easiest situation, say if you have an ExportSession [ Silence ] and you're going from one, from the same file type [ Silence ] as your source to the output, [ Silence ] so for this example they're both QuickTime movie files. [ Silence ] This is the easiest way to ensure that all [ Silence ] of that data is actually going to make it into the output file. [ Silence ] If instead you're using a different output file type, [ Silence ] like MPEG4 in this example, [ Silence ] then some different things are going to have to happen. [ Silence ] You notice those last few items didn't quite make it [ Silence ] into the output file; [ Silence ] it's because they have no equivalent representation [ Silence ] that works with an MPEG4 file. [ Silence ] If you're looking closely you'll also notice [ Silence ] that those top two items have changed, [ Silence ] although they sound very similar they are slightly different [ Silence ] identifiers because that's the kind of identifier [ Silence ] that works with MPEG4. [ Silence ] So both AssetExportSession and AssetWriter will do the sort [ Silence ] of three step process. [ Silence ] First, they'll try to pass that data through directly [ Silence ] if possible and, if not, they'll try to convert the identifier [ Silence ] into an equivalent representation [ Silence ] in the output file type. [ Silence ] If neither of those work, we have no choice but to just drop [ Silence ] that piece of metadata on the floor. [ Silence ] So in terms of guidance on how to choose an output file type, [ Silence ] well, my two recommendations are, [ Silence ] if you are using say an ExportSession to copy all [ Silence ] of the metadata, timed or otherwise, from the source asset [ Silence ] to your destination file, the best way is to try [ Silence ] and use the same file type that you started with, [ Silence ] and if you don't know what the file type is you can use the [ Silence ] NSURLTypeIdentifierKey to find out. [ Silence ] You can also always use the QuickTime Movie file [ Silence ] because that is going to have the greatest chance [ Silence ] of supporting your metadata no matter where it came from. [ Silence ] If AVFoundation supports it, there's a good chance [ Silence ] that it will be supported by the QuickTime movie file. [ Silence ] Of course, this is the only way [ Silence ] if you're writing timed metadata, [ Silence ] to get your timed metadata into a file is [ Silence ] to use QuickTime Movie file; it's the only file form [ Silence ] that supports it right now. [ Silence ] Of course good advice is always [ Silence ] to check the results, no matter what. [ Silence ] Check that your output files contain the kind of metadata [ Silence ] that you expect, all the metadata that you expect [ Silence ] and you can choose to use some of the APIs [ Silence ] that we've already talked about if you want [ Silence ] to do that at runtime. [ Silence ] Some guidance if that doesn't end up being the case: [ Silence ] if you don't get all the metadata that you expect, well, [ Silence ] you can try to do the conversion yourself. [ Silence ] Especially if you have a custom identifier and are going [ Silence ] to a file type that doesn't support your custom identifier, [ Silence ] take a look at that long list of built-in identifiers we have [ Silence ] and see if there is something that's roughly equivalent [ Silence ] to what you're trying to store and you can do [ Silence ] that conversion yourself. [ Silence ] One particular example I want to call [ Silence ] out that involves only built-in identifiers is [ Silence ] when you're trying to go from ID3 to iTunes, [ Silence ] well AVFoundation currently isn't going to do [ Silence ] that conversion for you. [ Silence ] But there's no reason you couldn't do that yourself, [ Silence ] so once again just take a look at our long list of identifiers [ Silence ] and match them up and do the conversion in your own code. [ Silence ] So that is the end of the talk. [ Silence ] See what we covered: we talked obviously a lot about metadata [ Silence ] in AVFoundation, we talked about all [ Silence ] of the different classes you can use for inspection, [ Silence ] we talked about AVAsset and AVMetadataItem [ Silence ] and how those work together and also authoring, [ Silence ] we talked about the AssetWriter, [ Silence ] the AssetExportSession even briefly on the capture audio [ Silence ] and movie file outputs. [ Silence ] We dove into timed metadata, including all the new features [ Silence ] that enable things like the dynamic location [ Silence ] and your own timed metadata like the annotation demo. [ Silence ] We also talked about privacy considerations [ Silence ] and some best practices like how to choose the right file type. [ Silence ] So for more information, you can contact our evangelism team [ Silence ] or see our programing guide, [ Silence ] there are some other related sessions you might be [ Silence ] interested in. [ Silence ] If you missed this morning's presentation [ Silence ] on "Modern Media Playback", you can catch [ Silence ] that on the video recording. [ Silence ] Tomorrow there is also a camera capture talk focusing [ Silence ] on manual controls. [ Silence ] And on Thursday we'll have a talk about direct access [ Silence ] to video encoding and decoding, which I'm sure a lot [ Silence ] of you will be interested in. [ Silence ]