Video hosted by Apple at devstreaming-cdn.apple.com

Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2012-502
$eventId
ID of event: wwdc2012
$eventContentId
ID of session without event part: 502
$eventShortId
Shortened ID of event: wwdc12
$year
Year of session: 2012
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2012] [Session 502] Effective H...

WWDC12 • Session 502

Effective HTTP Live Streaming

Graphics, Media, and Games • iOS, OS X • 41:03

Designed for mobility, HTTP live streaming dynamically adjusts playback quality to match the available speed of wired or wireless networks. Gain a practical understanding of how HTTP live streams are made. Learn best practices for constructing and testing your HTTP live streams.

Speakers: Roger Pantos, Eryk Vershen

Unlisted on Apple Developer site

Downloads from Apple

HD Video (125.6 MB)

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Good morning, everyone. My name is Roger Panthos, and welcome to the first of two sessions this week on HTTP live streaming. You know, it's actually really remarkable when you think about the last three years. I was chatting with a few other folks who work on streaming back at Apple.

And we were talking about how in our collective experience, no media technology that we've ever seen has been adopted as quickly as HTTP live streaming. And certainly, no new device has acquired so much new content as quickly as the iPad has. And so I think all of us in this room can take a lot of credit for that. We, of course, at Apple, built some great devices and we built a solid platform for you.

And everyone who's working on tools to produce the content and, of course, most importantly, all you folks who work on applications, the last mile, you're going to be able to get the content you want. Roger Pantoz, Eryk Vershen You know, it's a great experience. And I think, I think, it's a great experience. And I think, I think, it's a great experience.

between the network and the user, what the user sees, that's so vitally important. And so I think we can all be very proud of what we've accomplished. Now, having said that, once you get over the initial enthusiasm of "I've got prime time TV on my phone, this is awesome," once you get over that sort of initial thing, and you start looking at the applications and you start looking at the streams with a bit of a critical eye, you realize that, you know what, there are actually a few things that we can do that could make these experiences even better. And so that's what we're going to talk about today.

And so to take us through that, we actually have an old hand with us today at this. He is our media technologies evangelist. He is our go-to guy when you folks send us crazy, puzzling questions about what's wrong with your apps. He's contributed to some of the sections in the spec, the Internet Directive, and some of the other sections in the web. And so he's been a great friend graph, defines HTTP live streaming. And many of you have actually already met him, either in person, or over e-mail, or on the developer forums. And so, ladies and gentlemen, I'd like to introduce to you Eric Verschen.

ERIC VERSCHEN: Good morning. Thank you all for coming. It's a -- I hope you're not tired already. You still got another three days So as Roger mentioned, I'm the media technologies evangelist. I'm one of the engineers on the evangelism team. And the talk today is effective HTTP live streaming.

So I want to start out by asking, what makes a great streaming app? Well, high performance of course. Fast startup, fast seeking, no stalls, and You also want great navigation. You want to be able to seek fluidly. You want to be able to fast forward and rewind. It's also important that your client be getting the right stream, that is the most appropriate stream for the bit rate that they can currently stay in, the network that they're currently on. And lastly, we believe you should have your content localized.

Now, I want to emphasize that everything that you need is in iOS 5 already. Now, I assume you were at the keynote yesterday, so you will have noticed that the adoption rates for iOS 5 are pretty terrific. And even with a combination of iOS 5 and iOS 4, you're pretty much covering almost all of the devices. So there's not really any point to having the backward support to older versions.

So before I get started, I want to do a little bit of a review about HTTP live streaming. Now, those of you who are familiar with HTTP live streaming already, please bear with me. This is only going to be a couple of minutes. Those of you who are new to HTTP live streaming, this might be a little bit fast for you, but I think you'll still get something out of the talk. And we have a lot of really great material in terms of documentation and earlier years WWDC presentations.

So, HTTP live streaming starts out with a really simple idea, a really simple concept, and that's we want to deliver video over HTTP, and we want to do it with an ordinary web server. We don't want any magic in the web server. And the mechanism is pretty simple itself.

You take your video, you break it into small, roughly equal-sized chunks, you have a list of those pieces, and in the case of live video, you have to update that list periodically. Now, in order to have that list, which we call a playlist, we actually have two kinds of playlists, two types. A media playlist, which is a list of media files or segments. And a master playlist, which is a list of other media playlists.

So let's talk about the first one, your media playlist. So the media playlist is, at its heart, just a list of those files, those segments. So that's file sequence A, B, C, D that you see here. And remember that the names aren't important. We could be calling those Bob and Jane and Ted. What's important is the order.

Now the other lines that you see in this file, the ones that start with a hash, are tags, and those are the way we convey more information to the client. That this is an M3U8 file, a playlist file, what version we're using, what the maximum duration for a segment should be, and so on.

Now this particular playlist is a video-on-demand playlist, VOD. So it has a playlist type of VOD, and it has an endless tag, indicating that you're at the end. Now if this was a live playlist, we wouldn't have the playlist type, and we wouldn't have the endless tag. And instead of being a static list, it would be a list that changes. The server would be updating it. Based on the target duration, we'll be updating it periodically so the client could fetch it and get a view of the newly produced content.

Now the other kind of playlist that you have is a master playlist. So master playlist is again a list of things, but in this case it's a list of other playlists, of media playlists. And the other important thing in there is it's a list of bit rates. That bandwidth attribute tells the client how many bits per second this thing is going to take.

And this is essential so that the client can make the decision about switching between different bit rates. So we have this simple mechanism. Now how do you take that mechanism and turn it into something that gives you high performance? Well, I want to start with asking the question, what do we mean by high performance? So fast startup, obviously, fast seeking, and absolutely no playback stalls. And you also want to be able to switch between streams easily.

So there's a lot that goes into Fast Startup and Seek. The first thing is you want to make a good initial choice of variant, that is, your master playlist that first Enter the master playlist. Entry in that master playlist, that's the one the client's going to start with.

So you want the bit rate of that to be something that most of your clients are going to be able to sustain. Now, when I'm talking about getting the streams to the right device, I'll talk about how you can arrange to have different master playlists delivered to different devices so that you can tune that initial choice. Now, the other thing you want to do is you want to serve your playlists with Gzip.

Playlists can have, in the case of VOD, hundreds of entries. And even in a live playlist, if you're delivering a large window of content, you can again have hundreds of entries. And Gzip reduces the size dramatically, and it's very easy to add to your server. It's typically a one-line fix in your server configuration. Now, in order to have fast startup and seek, you need to have an IDR frame. Now, those of you who aren't that familiar with H.264 might not be familiar with IDR frames. It's an instantaneous decoder refresh, and it's a special kind of iframe.

And what it indicates to the decoder is no frame that occurs after this frame depends on any frame that occurred before. So it means that essentially I can reset the... I can reset the decoder. Now, you want that IDR frame at the beginning of your segment because if we're seeking or if this is a live stream and we're starting out partway through, we need an IDR frame to get started. And so if you've put your IDR frames partway into the segment, you're just wasting our time until we get to that IDR frame.

Now, in MPEG transport streams, there's a certain amount of overhead that's in there. And there's some padding that occurs. And depending on how your transport stream is getting created, you might have more overhead than in other cases. Now, we've done a lot of work with our media file segmenter and stream segmenter to try and make sure that we don't have too much overhead. And typically, we have less than 10%, and in fact, in most cases, less than 5% overhead.

Now, in looking at streams that come from other encoders, we've seen overhead as high as 45%. Now, to make that a little more concrete for you, let's say you had a stream that was nominally 100 kilobits per second. Now, if you've got 45% overhead, then you've only got 55 kilobits of actual video data that's going through. If you've got 5% overhead, you've got 95 kilobits. Well, obviously, I can do a much better stream if I've got 95 kilobits for video data than 55.

So you want to minimize that overhead if you can. If you're working with a different vendor, I suggest that what you do is take your original content, encode it with our media stream segmenter, and see what kind of overhead you're getting and use that as a negotiating point with your vendor.

Now, the target duration that is the size of your segments does have some effect on startup, but I want to emphasize over and over again that 10-second target duration is what we recommend. It's what we recommended at the beginning. It's what we still recommend. We believe that 10-second target durations is the best choice. If you go with a smaller target duration, you're increasing the likelihood that you're going to get a stall.

Now, some of that's coming out of two different reasons. If you're delivering your content, if it's live content, and you're going through a CDN, you're going to have propagation delays for that new content to make it all the way out to the edge nodes on the CDN, and that's going to be variable. And also, if the client is fetching the stuff over a cellular network, they've got higher latencies, and both of those things can make you much more likely to encounter a stall if you have a small target duration.

Now, the other thing that you want to do to prevent stalls is you want to make sure that you're not under-reporting your bandwidth in your master playlist. So if I've declared a particular variant, one of my choices as being 200 kilobits per second, then please, its maximum bit rate should be 200 kilobits per second.

If you don't do that, let's say that I'm running at something lower and I want to go up to that 200 kilobit per second stream. If that thing actually peaks at 300 kilobits, I might not have that much headroom, and I may stall because you lied to me about how much bandwidth I needed.

Now the other piece that comes in here has to do with ads. And this is another thing that we've recommended over and over and we continue to emphasize. People who are coming from some other environment will say, "Oh, well, you know, hey, I want to put in ad content. I'll just have a separate player.

And when I need to do my ad, I'll just click on the separate player." Well, when you're doing things with HLS, If you've got two players, two things that are fetching streams, the second one, when you start it up, he doesn't have any of the knowledge that the first one has about what bit rate he was getting.

So he has to go through the whole algorithm again. He has to start with that first bit rate, which is typically going to be lower quality, which means your ad, one of the things that your client wants to have be very good quality, is not going to be good quality.

So that's bad on their side, but the other thing that's happening is that guy's competing with the first stream. He's trying to do his prefixes to get set, and they're competing for bandwidth. So we absolutely recommend that you should have your ad content in the stream, and there are a number of techniques you can use to do that.

Now there's another point that I want to bring up about preventing stalls, and this one's a little bit complicated setup, so bear with me here. So when you're updating live playlists, so you're delivering, imagine you're delivering this content, you're going through a CDN, right? And in my little diagram here, the blue bars indicate when I've got my encode done, and that blue box is how long it takes me to get that segment out to the edge nodes.

So that's going to be variable. Remember, the segments are going to be different sizes depending on what kind of stuff I was encoding. Like I might have been on a fade to black, I was on a black screen for a while, and now I'm into something and there's like smoke or fog, so I'm ending up having to encode.

And the encoding is more difficult, I end up with a larger thing that takes me longer, as well as just delays in the CDN. So as you remember, when you're delivering a playlist, In the live environment, the segments that it refers to, they have to be on the system already. They have to be available to the user. You can't deliver that playlist for segment seven when segment seven's not available, because then you're going to get a stall as well.

So the naive view is that, well, once I've got my segment up, I'll just ship my playlist up. But because I've got a lot of variation in how long it takes the segments to go up, I can end up with a very long delay in how long it takes my playlist to come up.

So that playlist for segment seven may come up really fast because it had a short segment. And that playlist for segment eight may take a long time, at which point the time between when I picked up the playlist for segment seven and segment eight, I might be more than one and a half target durations.

Between those times, and I'm actually out of spec, and I could get a stall. So what you want to do is delay when you're uploading those playlists. You want to get those playlists to come up and be available on a pretty regular cadence. You want it like clockwork because that's what the client needs. Now, the other thing that you want to do is you want to have fast stream switching. So here, your IDR frames, they're really important. That's where we can switch.

If you think about it, when you're switching between two streams, you essentially have a discontinuity. You're potentially changing the profile and level that you're going to or the resolution. So the decoder's having to restart and needs that IDR frame. This is where you can also have a difficulty because if you have longer segments, if you say, oh, I'll just go with bigger segments.

If 10 is good, let's go to 20, let's go to 30. The problem there is when you're... When I want to do a stream switch, I always grab the segment before the segment where I believe I want to get into. Partially because I don't know. There's not an absolute requirement that there be an IDR frame at the beginning. I want to have that lead in, which means that if I'm trying to switch and I've got bigger segments, I'm having to pull down a lot more data before I can make the transition. So you've done all this work to increase your performance.

You want to use the access logs that you can get from the client to keep track of your performance. Now, these are great things, but remember this is extracting data from the user that knows something about the user, so you've got to ask the user's permission to get this stuff.

You want to look at, especially at duration watched, observed bit rate, indicated bit rate, so you can tell whether you're actually increasing your performance. So the three things I want you to take away is use 10-second durations, serve your playlist during GZIPs, it's really easy, and absolutely have at least one IDR frame per segment.

So now we've got high performance, but we also need to have great navigation. And I want to start talking about great navigation by also asking the question, what makes great navigation? A generous live content window. And I'll talk about that in a little more detail. We want seeking to work well, and you absolutely want to have fast forward and reverse playback supported.

So remember when you have live content, what you have is your playlist is a window into that content because the content is essentially infinite. I can only show you a range of it. Now, according to the spec, you only have to have three target durations worth of content.

But the larger you make that window, the more Flexibility you give to the user so they can pause or seek back. And to try and illustrate that, I've got a couple little animations here. So let's say I have a small content window. It's three segments. I can't pause playback because as I'm moving along, that arrow is the segment that I'm about to play.

I've got no room here. I can't pause. I'll fall off of the window. Now, if I have a bigger content window, I've only made it slightly bigger here because I don't have a large enough screen. In this case, as I'm moving along, I can actually pause and the stream's moving along and I can start up again later.

Now, how big should you make this window? Well, we believe that at a minimum, you should have a minute worth of content in this window. But what we really think you should do is you should have a lot more than a minute. You should have half an hour or even an hour.

You should have enough content that the -- the user can be in a situation where they get a phone call, somebody's at the door, there's a noise in the other room. They've got something that interrupts them and they want to pause but get back to where they were in the content.

Now you also want your seeking to work well. And the most important thing, is floating point durations. In the very first version of HLS, you could have integer durations, but Please, use floating point duration. There's really no point being still within integer durations. So you need this because the only information that the client has about how long these segments take is the duration that you specify in the media playlist.

If I want to seek forward, I haven't seen those segments. I don't know exactly how long they are. I only have that duration that you've told me. If you give me integer durations, I'm accumulating error. So if I attempt to seek well into the program and start up, I've accumulated an error, and I'm at a very different time. That current time that's going to report in my app is going to be very different than what I would have gotten if I'd just played forward.

Now the other part about seeking is there's actually two kinds of seek. There's fast seek or precise seek. So an AV player item, if I use seek to time, I'm getting a fast seek. And in the case of a fast seek, what we're doing is we're finding the segment where we believe that time is, picking up the first IDR frame and going ahead and playing. We're just trying to get in the general ballpark.

Now that goes pretty quick, but if I really want precise seeking, if I want to get close to the frame that I want, I use that second method, seek to time with tolerance before and tolerance after. Now, please remember that if you want precise seeking with HLS, those tolerances have to be zero.

If you don't have them as zero, it won't invoke the precise seek. And what happens in this case is we're actually going to go and pick up that segment and the one before it, and we're going to try and get, we'll pick up the IDR frame and try and start exactly where you want us to.

Now, it does take a little bit more time because we have to fetch that segment beforehand because we don't have a guarantee that you have an IDR frame at the beginning. So we might actually have to start from there, as well as the fact that there can be some minor variations in time code. So where we want to get may not be exactly in the segment that we're interested in.

Now, the other thing that you want to have in order to give the user a good experience is fast forward and reverse playback. And the way you do this is with iframe playlists. And this is really important if you're using AirPlay and Apple TV. If I move my content over to Apple TV, I don't have a scrub bar that I can touch. I've just got the fast forward button. And if I don't have these iframe playlists, the user's not going to get any feedback. You're not getting any feedback when you're doing the fast forward.

Now, the higher your iframe frequency, the better your, smoother your playback's going to be in fast forward, right? If I've got, say, four seconds between each iframe, then when I'm going at 4x, I've got one frame to show to the user per second. If it was two, if the separation's two seconds, then I'd have two frames to show.

So the higher we get this, the smoother the playback's going to be. Now your iframe playlists can reuse your existing media files. So if you've already got content, The iframe playlist is pulling out references to those iframes. So it can point at exactly the same media files that you had for your normal playback content.

and the playlist uses our byte range syntax to indicate a particular offset and length where the iframe will be in that segment. You want to have multiple iframe playlists in your master playlist. Beginning I've got my four variants here and then I've got some iframe playlists. What you can't see in this is that that low iframe M3U8 is pointing to the same media that the low audio/video M3U8 is pointing to. It's just pointing to pieces of it. Now, even though we're using the same Media.

The decision about which bitrate to choose is made independently if I'm in fast forward than from normal playback. So if in normal playback I'm playing my medium level bitstream there, when I go into fast forward, I'm not required to pick the medium range iframe. I'll pick whichever one has the most appropriate bitrate. So those are the essentials for having great navigation. Having that nice, generous live content window, floating point durations, and iframe playlists. So we've got good performance, got great navigation. So the next thing is getting the right stream to the right device.

Now, the tagline I want you to remember is the right stream to the right device. You want to choose your bit rates carefully. You want to know something about your client. What's the capabilities of this particular device? And there are some special considerations for cellular. So we have this great tech note, 2224, on bit rates. And rather than bore you by going through all of the detail in there, I thought I'd try and distill it down to some high points.

So the first thing is that adjacent bit rates should be a factor of about one and a half to two apart. The reason you want to keep them at a particular range is you don't want them too close together. If you put them too close together, you're essentially wasting bandwidth. You're just going to pick one.

It's not really going to make much difference if I've got 150 and 180. It's like, what's the point? But you don't want them too far apart, because if they're too far apart, you may get in a situation where a client could actually have gotten a better stream, but you don't have one available because you're going from 100 kilobits to 300 kilobits. It's too big of a jump. Now, keyframes, IDR frames, we, in all of our recommendations, they're no more than three seconds apart. In fact, in some cases, they're less.

And the first bit rate that you have in your playlist should be the one that most of the clients can sustain. Now, choosing bit rates, you do have some considerations. And the problem here is that I can't tell you the absolute right answer because there is no absolute right answer. You have to figure it out for yourself.

You have constraints on yourself. You're using some particular encoding hardware. You may have a limitation on the number of various bit rates that you can produce. It may be that with your particular set of hardware, it's like, oh, well, you can only produce five. You can't produce six. So you've got to pick five that work for you.

Also in the case of live, if you're delivering through a CDN, you've got to worry about how much time does it take, how much bandwidth do I need into my CDN to get all of these streams up at the same time. Because I'm not going through the big fat pipe where I've got a whole bunch of different edge nodes to talk to. I'm trying to get this in from the back end. I may have a budget. In fact, I may even have financial constraints on that side.

And then lastly, you want to think about the things I talked about before. You know, what's my ability to switch? What's the distance between these various bit rates? Once you've picked your bit rates, once you've deployed all this stuff, you want to verify your assumptions. So just like when you were measuring your performance, you want to measure this stuff. You want to track the client performance in the field. You've got access logs and error logs. And once again, don't forget to get the user's permission to grab this data.

Now we've got a lot of fields in the access log. So you want to look at, you know, what streams am I actually getting? How long are they playing for? Where am I stream switching? Am I getting stalls? You want to also know about your clients. Devices have different capabilities. They have different screen resolutions.

They have different versions of H.264 that they can handle, different profiles and levels. And you might in some cases want to provide a different playlist to different models, like a particular playlist to iPads versus iPhones. and you want to use the capabilities you have on the client side to find out about your network.

So the first thing you can do is you can add things into the playlist that allows the client to select based on resolution. So different devices have different resolutions that they can handle. Let's imagine that we have a playlist like this. I've got a 640x360, a 720p, and a 1080p. So if I'm delivering to an old device, a 3GS, the 3GS can't play 720p or 1080p. So it's going to pick that 640x360. Whereas the new iPad will pick the 1080p if it can handle the bitrate.

But you should remember that if you've got an app and you actually have a smaller window, you're not delivering your video to the whole screen. So let's say I had on my iPad and I had a window that was 640 by 360. Then we'd pull the 640 by 360 stream because there's no point getting the 1080p and downshifting it. If you're only showing that stuff in a small window, why would we waste the user's bandwidth? But if you then go to full screen, we're going to switch up to the 1080p if we can.

Now, you can also add things into your playlist that allows the client to filter based on the codec, the particular profile and level that you're encoding. Now, these codecs tags are somewhat obscure, I'll admit, and we have some documentation available that will help you navigate to the correct one. That's why I've added the comment, and by the way, that's not a comment, you can't put comments in your playlist, so don't try and use that slash slash, it won't work.

So the first one's baseline, second one's main, the third one's high. So once again, if I'm going to something like the 3GS, it doesn't handle main or high, so it's going to take that baseline profile, it's going to ignore the other two variants. Whereas, say the iPhone 4 would take the main profile.

Now, you can also select based on your device model by sniffing the user agent string. So this is something you do on the server side rather than the client side. You can sniff the user agent string and decide, you know, oh, iPhone versus iPad, I'll deliver you a different playlist.

The first two here are if you're coming over Safari. The last two are if you're in an app. And if you'd like to go to the Stumpy Experts, what modern phone can that third one not be from? It's a fairly obscure question. So you also want to know about your network, right? You've got the reachability API available to you, SC Network Reachability.

And this allows you to find out whether you're on cellular or Wi-Fi. And so doing this, you can either go to a different URL or add this as extra information in your request so that the server can tell you, and you can get a different playlist. This is particularly important so that you can have that first-order. That first item in the playlist be appropriate for the network that you're on.

Now, there's some special considerations for cellular. And this is something that we've repeated over and over again, but it bears repeating again. And that's if you're delivering this content over a cellular network, you absolutely have to provide a stream that is no more than 64 kilobits per second, or you will be rejected.

And as I was reminded by someone this morning, when you're doing that, when you submit to the App Store, Tell us a URL to your cellular stream, the cellular playlist, so that we can test it. Because otherwise what we have to do is try and sniff the network to find out what it is.

And if we can't find what it is, we'll reject your app and you'll have to go through another cycle. The other thing that you need to remember is... Some people have been grabbing media in some other format, bringing it into their app and having a local web server to serve that as HLS. You can't do that on cellular. Don't do that.

So delivering the right stream to the right device is about choosing your bit rates carefully, using the access logs to verify your assumptions, and customizing your master playlist. So we've talked about great performance, great navigation, getting the right stream. So there's one other piece, which is localizing your content.

You're probably delivering your app to the world, but you might even be delivering it just to a particular country. And even in that case, many countries have significant minorities that speak some other language. So you probably want to get your audio into multiple languages. And the way you do this is with alternate audio playlists.

So here's a conceptual diagram of a master playlist. It's got a bunch of variants, one audio only, and then three audio/video variants at different bit rates. So when I add an audio alternates, what I'm doing is I'm adding some more playlists, one that has English language audio, one with German, one with French. And I'm grouping those audios together and then associating that group with each of the variants.

So let me walk through what the master playlist looks like. So to get one of those audio variants, you use the media tag, which is something we added in 5.0, but it's backward compatible. So what the important things in here, there's the URI that tells us where the playlist is.

The language that it's in and a name that we're going to show to the user for this alternate. So when I add the German and French in here, again I've got

[Transcript missing]

So when I add this into a playlist, you can see the rest of this playlist is a typical variant, typical master playlist with a bunch of variants.

And the connection is still that group ID. That group ID is just an arbitrary name that you can come up with. The audio attribute on the stream info tags says that this particular variant can have its audio substituted by any of the audios in this group. Those alternates that you have, they're constrained by the streams that use them.

That is, if my variant says I'm using a particular codec, a particular sample rate and bit rate, then the alternate should have the same sample rate and bit rate. And in fact, the playlist, if my variant has a particular target duration, the alternate should have the same target duration as well.

And if I have discontinuities or I have add insertions, the same things apply. These alternates have to be substitutable for the variants. And that media tag is backward compatible. So I can take that playlist and I can provide that to a client who's not on 5.0 and they'll still be able to play the variants. They just won't be able to detect the alternates.

So I'd like to try and wrap up. I want to mention a bunch more things that you can do. Along with audio alternates, you can have video alternates. So I can have an event that has multiple cameras on it, and I can provide the feeds of both of those cameras to my users. I can add timed metadata, which allows me to add metadata that's associated with particular instance in my movie.

I can add American style closed captioning. I can add real world times and dates associated with points in my stream. I can also consolidate segments together. So that same byte range syntax that we use to identify iframes in your media files, you can actually consolidate your media files into a single media file and reference the individual segments.

This allows you to have fewer files on your server. And of course, streams can be encrypted. Now, we have a lot of resources available if you're interested in HTTP live streaming. Remember this URL, developer.apple.com slash resources slash HTTP dash streaming. So we have pointers to our documentation there, including the internet draft spec. We also have pointers to all the tech notes that we've done. We have pointers to the tools. And to the to some example streams that we've recently put up.

So these example streams are really great. They show-- they're using all of the things that I've talked about today. They're using floating point durations and alternate audio and iframe playlists and more stuff, time metadata and closed captions. So these are a great way to test your player. If you're running a player, this gives you some content that you can play where you can make sure that you're picking up time metadata correctly, that you're dealing with closed captions, that you're able to switch between alternate audio. So I encourage you to take advantage of these example streams.

So, that's almost it. I'm Eryk Vershen. You can email me. The documentation, of course, the tools are available where all of our special downloads are in developer.apple.com/downloads. Just go there and search for HTTP and you'll find the tools. I also want to recommend that if you have questions about HTTP live streaming that you don't get a chance to get answered here. That you go to the dev forums. Engineers from the HLS team are on the dev forums answering questions. So another pitch for our talk tomorrow. Please do come back tomorrow and find out all the cool new stuff we've done. Thank you very much.