Twitch Mobile Stream API

I recently figured out how to access the mobile devices streams that Twitch.tv puts out for their Android (and presumably iPhone) apps. Due to the way in which the stream works, I couldn’t think of anything cool to do with it myself, but maybe someone else can benefit from this information.

Everything starts with a token

Seems like they finally let someone with half a brain design one of their APIs. Say CHANNEL is your twitch.tv channel.

Then you’d call this URL to grab an access token:

http://usher.justin.tv/stream/iphone_token/CHANNEL.json

As the filename hints, it comes as a JSON response:

[{
token: "0d0e4bf90927f5f243d602bfa4ea0cedb470d059:{"expiration": 1378233511.2046671, "channel": "wcs_europe", "user_agent": ".*"}",
sleep_time: 0
}]

The path to the actual token data being: data[0]->token. We’ll call this token TOKEN.

To use this token in the following API calls, we need to calculate an SHA-1 HMAC. These are created by applying SHA-1 to a text using a given key. This key for Twitch.tv’s API is currently Wd75Yj9sS26Lmhve. PHP for exmaple lets you calculate this hash like this:

hash_hmac("sha1", $TOKEN, "Wd75Yj9sS26Lmhve");

We’ll call this hash HASHTOKEN.

Now, let’s get a playlist of playlists

Now that we have a bunch of tokens, let’s get to the next step: getting a playlist that contains URLs to playlists of  distinct stream qualities. Assume that all of the variables we defined beforehand are URLESCAPEd.

We poll the URL

http://usher.justin.tv/stream/multi_playlist/CHANNEL.m3u8?token=HASHTOKEN:TOKEN

Which gives us a m3u8 playlist. This can be read with a text editor and easily parsed. It contains a set of URLs pointing to the available qualities, e.g. high, medium and low:

#EXTM3U
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="0",NAME="high",AUTOSELECT=YES,DEFAULT=YES
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2160000,VIDEO="0"
http://video8.iad02.hls.justin.tv/hls1/wcs_europe_6716557760_16590544/high/index.m3u8?token=id=9187680844069979664,bid=6716557760,exp=1378320212,node=video8-1.iad02.hls.justin.tv,nname=video8.iad02,fmt=high&sig=387397d8454c9e544e4dd41334f2d375710c6882&
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="1",NAME="medium",AUTOSELECT=YES,DEFAULT=YES
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=596000,VIDEO="1"
http://video8.iad02.hls.justin.tv/hls1/wcs_europe_6716557760_16590544/medium/index.m3u8?token=id=9187680844069979664,bid=6716557760,exp=1378320212,node=video8-1.iad02.hls.justin.tv,nname=video8.iad02,fmt=medium&sig=299579f49e2835236dbb2e67808e3d2f6e77a056&
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="2",NAME="low",AUTOSELECT=YES,DEFAULT=YES
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=164000,VIDEO="2"
http://video8.iad02.hls.justin.tv/hls1/wcs_europe_6716557760_16590544/low/index.m3u8?token=id=9187680844069979664,bid=6716557760,exp=1378320212,node=video8-1.iad02.hls.justin.tv,nname=video8.iad02,fmt=low&sig=d8070cdfe3fc8bb1252e977c2a87abe9e4a98595&

Note that the non-functional metadata (starting with #) always precedes a playlist URL and gives information about bandwidth and quality, among other things.

Getting an actual playlist

Let us first take note of the path of one of these URLs (ignoring the file/query parts):

http://video8.iad02.hls.justin.tv/hls1/wcs_europe_6716557760_16590544/low/

Let’s call this string PATH.

When we grab any of these second level, quality-specific playlists, they contain either the complete URLs, or just the filenames (FILENAME) of several .ts files, e.g.

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:8
#EXT-X-MEDIA-SEQUENCE:2062
#EXTINF:6,
http://twitch.map.fastly.net/hls1/wcs_europe_6716557760_16590544/high/index2062.ts
#EXTINF:8,
http://twitch.map.fastly.net/hls1/wcs_europe_6716557760_16590544/high/index2063.ts
#EXTINF:6,
http://twitch.map.fastly.net/hls1/wcs_europe_6716557760_16590544/high/index2064.ts
#EXTINF:6,
http://twitch.map.fastly.net/hls1/wcs_europe_6716557760_16590544/high/index2065.ts
#EXTINF:6,
http://twitch.map.fastly.net/hls1/wcs_europe_6716557760_16590544/high/index2066.ts

If they contain only the filename, then concatenating PATH and FILENAME gives you the complete URL to that particular .ts file.

Wait, what? Multiple files for one stream?

Yeah, that’s where it gets less interesting. Each of the .ts files contained within the last playlist is only approximately 6 seconds long. All of them appear to be under the same base URL (i.e. same “folder”). Each of them has an incremented-from-the-previous index number in the filename, and when you take a look at their video content, they all seem to be H264/AAC in an MPEGTS container. The “high” quality in a sample check was 720p at 30fps and approximately 2.5mbit/s.

The problem with this being used in a productive way is the segmentation. While there are good ways of using the regular flash player API to watch a stream via desktop applications as well as integrated devices, it requires rather specialized RTMP software (RTMPDUMP or its libraries) to be on the destination system. This is often not feasible for integrated systems. Sadly, it’s probably pretty hard apparently not that hard (see last paragraph) to find a regular player that plays an ever-increasing queue of consecutive video files without doing costly post-processing, e.g. remuxing these files into a single container on the fly.

On the other hand, this little examination produced proof that, contrary to what you see on the Android app, not just a handful of streams with the most viewers are exposed via this API. Instead, you can also access less popular ones with it.

Update: It has come to my attention that you can in fact use these kinds of streams after all with VLC or recent (nightly) versions of MPC-HC: You can paste the URLs that link to a quality-playlist (second level playlist) into VLC’s “Open network stream” or MPC-HC’s “Open file” dialogs.

Additional hints:

  • MPC-HC: In the options, go to Playback/Output, see that “Enhanced Video Renderer” is selected and change the EVR Buffers in the bottom to a higher number, say 30. That’ll let it buffer 30 frames before it starts rendering.
  • VLC: In the open network stream dialog, show the settings and set network cache to something high, e.g. 2000 ms.

5 responses to “Twitch Mobile Stream API

  1. I also had a look at this, this is how you can get the m3u8 playlists like the iPhone App does (I captured the network traffic with Fiddler):

    First, get a token from this URL (you don’t need to be logged in obviously): http://api.twitch.tv/api/channels/CHANNEL/access_token

    You get back something like this:
    {
    token: “{“privileged”:false,”chansub”:{“view_until”:1924905600,”restricted_bitrates”:[]},”channel”:”spamfish”,”expires”:1379082465,”user_id”:null}”,
    sig: “e5717b11ded7776f65ba30e63d5793641a6dbfb8”
    }

    You get the token and the signature of this.

    Then you just request
    http://usher.twitch.tv/api/channel/hls/CHANNEL.m3u8?token=TOKEN&sig=SIG

    And you have the m3u8 playlist 🙂

    • Hey, thanks for your post!

      Curiously enough, these are two distinct APIs, seemingly leading to the same HLS content. Why they would try to secure one of them and not the other is beyond me (I guess I can strike the first line in the second paragraph).

      • I did some experimenting just now, and apparently Android now gets the playlist the way Dennis described as well. Checked it by decompiling the apk.

  2. Pingback: Twitch Mobile Stream API /2 | julian's techblog

Leave a comment