<texit info> author=Dick Snippe Dick.Snippe@tech.omroep.nl title=streamAPI specificatie keywords=On-Demand streaming Api backgroundtext=draft </texit>

Inleiding

streamAPI = On Demand API = De API van de redirector van het on-demand streaming (en later) content platform. streamAPI genereert de uiteindelijke links (dwz de aller diepste deeplinks) naar de media content. streamAPI gaat dienen als de vervanger voor het huidige streams script en de streamAPI die daarin gebruikt wordt. Deze nieuwe versie is dus in feite streamAPI versie 2.0; maar zal in de rest van het document simpelweg streamAPI genoemd worden.

Qua implementatie behelst streamAPI ongeveer het samenvoegen van de code in cachingLIB, het huidige streams script en het toevoegen van een aantal nieuwe zaken.

De uitgangspunten van streamAPI zijn:

  • bedoeld voor redirecten naar ons windowsmedia, quicktime, helix en later contentplatform en mogelijk flash-media uitspeelomgeving
  • streamAPI is een web interface, dwz commmunicatie verloopt via http
  • aanroepen gebeurt vanaf de eindgebruiker
  • zo handig mogelijk maken om tegenaan te programmeren (xml/json/javascript)
  • ondersteunt doorgeven van start/stop timecodes en evt andere platform afhankelijke parameters
  • ondersteunt onze vorm van caching (streamcache-mgr)
  • ondersteunt onze vorm van loadbalancing (PMLB = Poor Mans Load Balancer)
  • ondersteunt first-level geoip
  • ondersteunt first-level anti-deeplinking op basis van shared secret
  • ondersteunt first-level anti-deeplinking op basis van sessies
  • ondersteunt onze vorm van Content Distributie Networks (CDN)
  • genereert kort houdbare links (anti deeplink) naar de mediacontent
  • mogelijkheid voor asynchrone communicatie (“filmpje wordt uit archief opgehaald”)
  • uitbreidbaar voor als er later andere zaken nodig blijken te zijn

Aan de buitenkant is streamAPI heel simpel:

  • jij vraagt een clipje op (bv /pad/naar/file.wmv )
  • en je krijgt een deeplink naar de server die het kan uitspelen (bv mms://wm1a.omroep.nl/moeilijk/pad/naar/file.wmv?rare_parameters )

Maar er zijn wat details om rekening mee te houden:

  • hoe krijg je die deeplink terug: als http redirect / ASX / SMIL / XML / raw / anders ?
  • wat verwacht je terug te krijgen als de content niet bestaat
  • of als je geweigerd wordt op GeoIP
  • of als je geweigerd wordt op anti-deeplink
  • of als de content tijdelijk niet beschikbaar is
  • of als de content eerst even uit het archief gehaald moet worden en pas over X seconden beschikbaar is

Hierover gaan de volgende secties. De bedoeling is om een aantal smaken van de API te maken. We beginnen bij streamAPI/simple. Deze is bedoeld als een zo simpel mogelijk interface: jij vraagt iets, er komt een antwoord terug. In een later stadium volgen de XML/JSON/Javascript interfaces, om vanuit een web-applicatie makkelijker tegen streamAPI aan te kunnen praten. streamAPI/simple gaat heel erg lijken op de huidige redirector van content.omroep.nl, zie http://hosting.omroep.nl/sterretje-cluster:content-hosting voor meer informatie over het content-platform.

De verschillende uitspeel omgevingen (windowsmedia/quicktime/helix) delen op onze storage een en dezelfde documentroot (/e/ap/media) Dat gaat er straks voor zorgen dat je via de ene uitspeel omgeving kan gaan proberen content van de andere omgeving uit te spelen (http://windowsmedia.omroep.nl/pad/naar/file.m4v of http://quicktime.omroep.nl/pad/naar/file.wmv) In de huidige opzet wordt dat door het streams script voorkomen (Die stuurt je voor *.m4v altijd naar de quicktime uitspeel omgeving)

FIXME op dit moment weet ik niet wat handig is. Het zou zomaar eens zo kunnen zijn dat in de toekomst h264 content in een mpeg container zowel door windowsmedia als door quicktime uit te spelen is. Als we dan dan ook echt willen gebruiken dan is de huidige setup handig, omdat je maar 1x de h264 content op hoeft te slaan. Maar, het zou ook best eens zo kunnen zijn dat sommige uitspeel omgevingen zich ongeneeslijk verslikken in content die niet voor hen bedoeld is. En dat zou er dan weer voor pleiten om de data uit elkaar te gaan trekken in verschillende documentroots.

Voorlopig opteer ik er maar voor om per uitspeel omgeving een lijstje van valide file suffixen aan te houden (.wmv en .asf voor windowsmedia, .m4v en .mov voor quicktime e.d.) en op die manier niet valide requests tegen te houden. (noot Wietse: ik denk dat dat een goed idee is)

Normaliter is streamAPI diep verborgen in de ingewanden van hogere lagen.

Neem bijvoorbeeld ODI. Dit heeft als ingang o.a. een NEBO-afleveringsID of een What's On Product ID.. Op basis van dit ID kan ODI een padnaam en eventuele hotlink hashes bedenken. Die combitie gaat streamAPI in en streamAPI genereert uiteindelijk de redirect naar de eigenlijke content.

De communicatie is steeds vanaf de player/browser/whatever die bij de eindgebruiker draait, naar ODI, van ODI door naar streamAPI en het antwoord van streamAPI gaat terug naar de eindgebruiker. dus het schema is zo iets:

  1. eindgebruiker gaat naar website (“uitzendinggemist”)
  2. krijgt daar een ODI url (ODI level deeplink)
  3. browser/player/whatever roept ODI url aan
  4. ODI roept streamAPI aan en krijgt daar een media url (second level deeplink)
  5. media url wordt door ODI teruggegeven aan browser/player/whatever
  6. en krijgt (at last) de media content uitgespeeld

Als het gaat om zogenaamde 'webonly' content, die dus niet vanuit de backend-UG geupload wordt; heeft ODI geen weet van de bijbehorende ID's, en kan dus ook geen 'deeplink' naar de streamAPI verzinnen. In dat geval wordt de interactie:

  1. eindgebruiker gaat naar website (“xyzplayer.xrtv.nl”)
  2. krijgt daar een streamAPI url (first level deeplink)
  3. browser/player/whatever roept streamAPI url aan
  4. krijgt daar een media url (second level deeplink)
  5. browser/player/whatever roept media url aan
  6. en krijgt (at last) de media content uitgespeeld

Er zijn 2 verschillende types applicaties die bij streamAPI dingen kunnen opvragen:

  1. Publieke applicaties. Deze draaien bij het grote publiek, in hun browser: Publiek→StreamAPI
  2. Prive applicaties. Concreet is dit ODI. Dit draait bij ons ergens op een server: Publiek→ODI→StreamAPI

Het verschil tussen beide smaken is dat de er wat streamAPI betreft minder checking nodig is voor de prive applicaties (want die gebeurt al in de stap ervoor, in ODI)

Om dit te faciliteren komen er 2 versies van streamAPI: public en private. Het private interface is gelijk aan het publieke interface, met uitzondering van de volgende punten

  1. afgeschermd op bais van IP adres en digest authentication, zodat het niet van buitenaf beschikbaar is
  2. er vinden geen geoIP en anti-hotlink checks plaats
  3. het ip adres van de client (dat nodig is tbv CDN) moet doorgegeven worden
parameter omschrijving voorbeeld
ip IP adres (dotted quad IPv4 of colon separated IPv6) van de client 82.95.104.238

Vooralsnog zal er alleen een xml/json/javascript variant van streamAPI-private beschikbaar komen. Deze is te benaderen via

http://private.streamapi.omroep.nl/1.0/endpoint

Voorbeeld

http://private.streamapi.omroep.nl/1.0/endpoint?family=windowsmedia&clip=/test/wm9video.wmv&type=xml&ip=82.95.104.238

streamAPI/simple

Voor streamAPI/simple komt er voor elke uitspeelomgeving dat we draaien een eigen ingang. Deze worden

  • http://windowsmedia.omroep.nl Windows Media streaming
  • http://quicktime.omroep.nl Quicktime (darwin) streaming
  • http://realmedia.omroep.nl Realmedia (helix) streaming
  • http://flashmedia.omroep.nl Flashmedia streaming (volgt later)
  • http://content.omroep.nl Progressive download (bestaat al, wordt ooit omgebouwd)

Als voorbeeld zal ik steeds windowsmedia.omroep.nl gebruiken.

Het opvragen van een clipje gebeurt als een regulier http GET request

http://windowsmedia.omroep.nl/pad/naar/file.wmv

eventuele parameters kunnen meegegeven worden als HTTP GET parameters:

http://windowsmedia.omroep.nl/pad/naar/file?var1=val1&var2=val2&...

of als HTTP headers genaamd X-Setvar-$var:

GET /pad/naar/file
X-Setvar-var1: val1
X-Setvar-var2: val2
Host: windowsmedia.omroep.nl

HEAD requests kunnen gebruikt worden om zoals in regulier http verkeer properties van een bestand op te vragen. Niet bestaande bestandne zullen dan een HTTP 404 teruggeven. POST/PUT/anders zullen niet ondersteund gaan worden en geven een HTTP 405 Method Not Allowed status terug

De response hangt af van het gekozen platform:

  • windowsmedia genereert standaard ASX
  • quicktime genereert standaard SMIL
  • realmedia genereert raw
  • flashmedia weet ik nog niet
  • het content-platform genereert een http redirect

Default hangt het type response (ASX/SMIL/…) af van de gekozen uitspeel omgeving, maar het is mogelijk om in het request middels:

type=<asx|smil|raw|http>

een specifiek response af te dwingen. Valide responsetypes zijn

responsetype omschrijving
asx ASX 3.0
smil SMIL 2.0
raw De ruwe, niet ingepakte url
http Een HTTP 307 temporary redirect

Dit betekent dus dat er HTTP/1.1 gesproken moet worden door de client, HTTP/1.0 wordt niet meer ondersteund.

Het uitspeelprotocol is het netwerk protocol dat gebruikt wordt om de content uit te spelen. Bijvoorbeeld, quicktime wordt normaliter via rtsp uitgespeeld en flash-streaming via rtmp. Dat zie je terug in de uitspeelurls, die worden dan iets als rtsp://quicktime-server/pad/naar/file.mov Normaliter hangt het uitspeelprotocol (mms/rtsp/rtmp/http) af van de gekozen uitspeelomgeving, maar het zou in theorie kunnen voorkomen dat je dat handmatig wilt overriden. Bijvoorbeeld, bij windowsmedia worden default mms urls gegenereerd, maar mms is in de windows wereld geen echt protocol meer. Het is een hint voor de mediaplayer dat er eerst rtsp geprobeerd moet worden en als dat niet werkt een botched streaming-over-http protocol. Als je nou de rtsp stap wilt overslaan, dan zou je dus aan willen kunnen geven dat het uitspeelprotocol http moet zijn. Specificeren van uitspeelprotocol kan middels:

proto=<string>

Er zit verder geen enkele vorm van checking op de parameter die meegegeven wordt, dus bij proto=smurf wordt er een url gegenereerd van de vorm smurf://server/pad/naar/file

Omdat in de responses bijna altijd een tijdsgebonden second-level hotlink beschermings code zit zijn de responses maar kort houdbaar. Daarom zal er een Expires header gegenereed worden die aangeeft hoe lang de response houdbaar is. Er worden ook headers als Cache-control: no en Prama: no-cache gegenereerd om applicatie aan te moedigen deze content niet te cachen.

In een aantal omstandigheden kan het voorkomen dat streamAPI geen redirect naar de gevraagde content kan of wil genereren. Bijvoorbeeld als er een clipje opgevraagd wordt dat uberhaupt niet bestaat, als uit een hotlink of geoip check volgt dat het request niet vervuld mag worden, of als er een stuk van de infrastructuur down is, waardoor het request niet vervuld kan worden.

In de meer programmatische api's (json/xml/javascript) kan dat beter opgevangen worden, maar in streamAPI/simple is daar niet echt ruimte voor. Daarom worden de responses op foutcondities volgens de volgende richtlijnen gegenereerd

  • Geef op foutcondities die de vragende kant niet kan voorzien altijd het type respons dat verwacht cq expliciet gevraagd wordt. (dwz: verwacht men ASX, genereer dan ook ASX)
  • Zorg dat het media type is wat er verwacht wordt (verwacht men .wmv, geef dan .wmv, bijvoorbeeld een link naar een sorry filmpje) Indien mogelijk wordt er ook onderscheid gemaakt tussen sorry-audio en sorry-video.
  • Probeer indien mogelijk wel terug te geven dat er een foutconditie is

Concreet betekent dat als er geen onerror=… gedefinieerd wordt:

  • requests op niet bestaande content (“404's”) worden een redirect naar een standaard 404-sorry media-fragment.
  • bij requests buiten het GeoIP gebied en/of uitgetimede hotlink-protected urls wordt er gekeken of er een redirect naar een mediafragment ingesteld is. Zo ja dan wordt die gebruikt, anders een standaard sorry media-fragment. Er wordt dus geen redirect naar een html page meer gedaan
  • Bij requests die niet vervuld kunnen worden omdat een deel van de infrastructuur oid niet beschikbaar (of vol) is wordt geredirect naar een sorry media fragment
  • Bij requests die aantoonbaar fout zijn (verkeerde md5 hash) wordt een geschikte HTTP statuscode teruggegeven
  • In alle gevallen wordt er een X-Error: header gegenereerd waarin staat wat er precies aan de hand was. Het aanwezig zijn van de X-Error: header is tevens de check of alles al dan niet oke was.

Sturen van gedrag bij error condities

Bij het request kan een

onerror=<default|http>

parameter meegegeven worden waarmee aangegeven kan worden wat voor type responses er bij errors terug mogen komen. De volgende waarden worden herkend

onerror Omschrijving
default werk volgens bovenstaand gedrag
http genereer http status codes

Bij onerror=http worden de volgende http status codes gegenereerd

http foutconditie
404 Not Found clip bestaat niet
403 Forbidden GeoIP check failed
403 Forbidden Hotlink check failed (bad md5 hash)
403 Forbidden men probeert via de ene ingang content van een andere ingang op te vragen
410 Gone Hotlink check timed out
503 Service Unavailable Request kan tijdelijk niet vervuld worden
202 Accepted Wordt uit het archief opgehaald

soms is het gewenst om niet een heel bestand af te spelen, maar slechts een gedeelte ervan. Dit kan geregeld worden met Start en Stop parameters. Hiermee kan aangegeven worden op welke tijscodes moet worden begonnen en geeindig met afspelen. Er worden drie parameters herkend:

start=[[HH:[MM:]]SS
end=[[HH:[MM:]]SS
duration=[[HH:[MM:]]SS
Parameter Type Voorbeeld
start HH:MM:SS start=00:05:42 start in op 5 min en 43 sec
end HH:MM:SS end=00:55:40 stop bij 55 min en 40 sec
duration HH:MM:SS duration=00:10:20 speel gedurende 10 minuten en 20 sec af vanaf het start moment

Het is mogelijk om of een “end” danwel een “duration” parameter te gebruiken, niet allebei tegelijk. Zonder start parameter wordt vanaf het begin afgespeeld en zonder end of duration parameter wordt tot het eind afgespeeld.

Het is toegestaan om willekeurige parameters mee te geven:

paramX=valueY

Afhankelijk van het type inpakmethode dat gebruikt wordt, worden deze op de volgende manier doorgegeven:

inpakmethode manier van doorgeven
ASX Als ASX parameters: <paramX>valueY</paramX>
SMIL Als parameters aan het “video” element: paramX=valueY
raw Als parameters aan de deeplink: ?paramX=valueY
http Als parameters aan de redirect link: ?paramX=valueY

In het streams script is het mogelijk om met 1 request een lijstje van bestanden af te laten spelen. In streamAPI/simple is dat niet meer mogelijk.

GeoIP afscherming werkt analoog aan contentplatform Alleen het redirecten als een GeoIP check faalt (“sorry, u zit niet in Nederland”) is anders, nl zoals hierboven uitgelegd.

Dit wordt identiek aan contentplatform, er is een paramater md5 en een parameter t waarbij md5 een valide md5 hash en t een timestamp in hex moet bevatten.

md5=$token
t=$t_hex

Er komen dus ook twee manieren van aanroepen:

http://windowsmedia.omroep.nl/pad/naar/clip.wmv?md5=$token&t=$t_hex
http://windowsmedia.omroep.nl/secure/$token/$t_hex/pad/naar/clip.wmv

Bij deze vorm van hotlink bescherming kunnen 2 foutcondities vooorkomen

  1. De timeout is verlopen
  2. Het md5 token was onjuist

In het eerste geval moet de gebruiker netjes naar een “sorry, deze link is verlopen” pagina geredirect worden, in het tweede geval heeft er iemand zitten knoeien en kan er gewoon een HTTP 403 Forbidden teruggegeven worden.

Om het mogelijk te maken een transitie van het ene naar het andere shared secret te doen wordt het mogelijk om een voorkeurs- en een backup shared secret in te stellen. Default wordt de voorkeurs-secret gebruikt (zodat je niet te veel extra rekenkracht nodig hebt), voordat gekeken wordt of het fallback-secret gebruikt kan/mag worden.

Dit wordt ook identiek aan contentplatform Dus het opvragen van de challenge gaat middels de getchallenge parameter

getchallenge (deze behoeft geen waarde)

Dat geeft een id en een challenge terug en de eigenlijke content kan dan opgevraagd worden door de juiste id en response parameters terug te geven

id=$id
response=$token
  • http://streaming.omroep.nl/pad/naar/clip.wmv?getchallenge
  • http://streaming.omroep.nl/pad/naar/clip.wmv?id=$id&response=$response

FIXME Ideetje van Wietse: de opvrager van de challenge moet in geval dat het een eindgebruiker is, daar zelf mee aan het rekenen gaan. Dat betekent dat er ofwel een heen-en-weer-communicatie met de player-applicatie moet zijn (serverside?), of dat de logica in de client gestopt moet. In geval van bijvoorbeeld de iPhone enzo kan dat lastig zijn.

Wellicht is het een idee om, als de IP-matching aanstaat, de getchallenge vanuit een serverside script te laten komen, die dan de mogelijkheid moet hebben om het eindgebruikersIP mee te submitten naar de streamAPI.

FIXME Hier moet iets komen voor hoe te handelen als streamAPI straks gaat zegge “jahaa, de content is er wel maar die moet ik eerst uit het archief halen en dat gaat even X seconden duren”

Wat ik eigenlijk denk is dat als je niks speciaals doet dan houdt streamAPI gewoon de connectie open tot de content klaar staat.

Daarnaast komt er een parameter asyncallow en dan kan streamAPI een HTTP 202 sturen, samen met een header hoe lang het ongeveer gaat duren tot de content er is.

asyncallow=<true|false>

Als die tijd verlopen is dan kan de client het nogmaals proberen en dan krijgt ie vanzelf wel de uiteindelijke redirect terug.

streamAPI/[xml|json|javascript]

FIXME Ik zie JSON/javascript straks als het belangrijkste protocol vanuit AJAX-achtige players. Volgens mij kan het protocol heel simpel worden. We gaan iets doen met de versie van het protocol in de url, en iets simpels met “geef mij de uitspeel url voor clipje X”.

XML is primair bedoeld voor andere applicaties (evt intern) die met streamAPI moeten communiceren, maar die niet direct in een browser / op een client draaien

De requests komen binnen op een endpoint waar je aan kan kan geven wat de naam van je clipje is en hoe je het response wilt hebben (xml/json/javascript) Het voorgestelde endpoint van versie 1.0 van streamAPI is:

http://streamapi.omroep.nl/1.0/endpoint

Dit herkent de volgende streamAPI/$moeilijk specifieke parameters (dwz parameters die niet door streamAPI/simple herkend worden)

cmd=<redirect>
family=<windowsmedia|quicktime|realmedia|download|flashmedia>
type=<xml|json|javascript>
clip=<string>
parameter omschrijving voorbeeld
cmd cmd=redirect wordt een regulier redirect verzoek gedaan, analoog aan een GET request in streamAPI/simple. Later komen daar mogelijk nog uitbreidingen bij, denk aan cmd=status dat het equivalent van een HEAD request iin streamAPI/simple zou kunnen zijn. redirect
family specificeert de uitspeelomgeving windowsmedia
type in wat voor formaat moet de response opgeleverd worden json
clip het bestand waarvoor de redirect gegenereerd moet worden /test/wmvideo.wmv

En verder worden de volgende parameters van streamAPI/simple herkend

proto=<string>
md5=$token
t=$t_hex
getchallenge
id=$id
response=$token
asyncallow=<true|false>

Voorbeeld:

http://streamapi.omroep.nl/1.0/endpoint?family=windowsmedia&clip=/test/wm9video.wmv&type=json

De response is dan een structuur waar de volgende elementen inzitten

element omschrijving voorbeeld
errorcode error status, 0=goed, !0 = fout 0
errormessage string die bij errorcode!=0 de error beschrijft File not found
family de uitspeelomgeving die het uitspelen gaat verzorgen windowsmedia
protocol het uitspeel protocol mms
server de mediaserver die het uitspelen gaat verzorgen media.omroep.nl
path het complete pad op de uitspeel serverer /moeilijk/pad/naar/clip.wmv
querystring eventuele querystring wmt=4b2b5402&wmhash=5573f7ca
url complete uitspeel url mms://media.omroep.nl/moeilijk/pad/naar/clip.wmv?wmt=4b2b5402&wmhash=5573f7ca
wait hoeveel seconde je moet wachten totdat deze clip beschikbaar is (ivm uit archief halen, indien asyncallow=true) 0

In de json/javacript/xml api's is het de verantwoordelijkheid van de aanroeper om de teruggekeregen url in ASX/SMIL whatnot te verpakken. OOk gebeurt er niks met start/stop codes of andere parameters. Dat is allemaal de verantwoordelijkheid van de aanroeper om die idien nodig via ASX/SMIL door te geven aan de uitspeel omgeving.

Response:xml

De xml response is een simpele lijst van key/value paren zoals hierboven gedefinieerd. Het xml root element dat gebruikt wordt heet streamapi_response De exacte key/value paren die terugkomen hangen af van het type command dat wordt gedaan. Vooralsnog is alleen cmd=redirect“ gedefinieerd. Als er in de toekomst andere commando's bijkomen (cmd=foo'') dan kunnen er andere key/value paren voor dat commando terugkomen.

<xml version="1.0" encoding="UTF-8"/>
<streamapi_response>
  <errorcode>...</errorcode>
  <errormessage>...</errormessage>
  ...
  <url>...</url>
</streamapi_response>

Response:json

De waardes worden als een simpele lijst van key/value paren teruggegegven

{
  "errorcode" : ... ,
  "errorstring" : ... ,
  "url" : ...
}

Response:javascript

In het javascript reponse type wordt het hele json object teruggegeven als parameter aan een mee te geven functie. Voor de javascript response is dus 1 extra parameter nodig:

parameter omschrijving voorbeeld
callback De callback functie die gebruikt wordt om de data in te pakken myfunc
Request: http://streamAPI.omroep.nl/1.0/endpoint?family=windowsmedia&clip=/test/wm9video.wmv&type=javascript&callback=myfunc
Response: 
myfunc(
  {
    "errorcode" : ... ,
    "errorstring" : ....,
    ...
    "url" : ...
  }
)

IPv6 support

FIXME TBD De streamAPI en alle aanroepende elementen moeten voorbereid zijn op het gebruik van IPv6. Dat betekent dat geoIP-filtering en andere IP-afschermingsmechanismes er tegen moeten kunnen dat er een IPv6-adres gebruikt wordt, ipv een IPv4-adres.

Omdat geoIP-filtering op IPv6 op moment van schrijven nog niet uitgekristalliseerd is (de MaxMind-database is nog niet bijster goed; ik weet momenteel niet of de PHP-modules die de IPv4-geofiltering goed en snel kunnen doen hier al toe in staat zijn), zal dat later uitgewerkt worden. Te denken valt aan een overgangssituatie waarin de IP-adressen waarvan wij zeker weten dat ze zich in NL bevinden, in een snelle lookup-tabel gooien, naast de MaxMind database.

Test URLs

Er is voor streamapi een test omgeving. De urls zijn:

productie url test url opmerking
streamapi.omroep.nl test.streamapi.omroep.nl
private.streamapi.omroep.nl test.streamapi.omroep.nl zit achter een wachtwoord
windowsmedia.omroep.nl test.windowsmedia.omroep.nl
quicktime.omroep.nl test.quicktime.omroep.nl
realmedia.omroep.nl test.realmedia.omroep.nl
flashmedia.omroep.nl test.flashmedia.omroep.nl
content.omroep.nl test.download2.omroep.nl test.download is voor de huidige download redirector
  • streaming/streamapi.txt
  • Last modified: 2019/05/28 10:47
  • (external edit)