understanding the value of consistency in ott video delivery · understanding the value of...

11
WHITE PAPER s:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://st andards/MPEG-DASH_schema_files/DASH-MPD.xsd" profiles="urn:mpeg:dash:profile:isoff-live:2011" type ggestedPresentationDelay="PT6S" availabilityStartTime="2019-03-07T06:00:04Z" publishTime="2019-03- pth="PT18.0S" minBufferTime="PT6.0S"> <ProgramInformation> </ProgramInformation> <Period start="PT pe="video" segmentAlignment="true" bitstreamSwitching="true" frameRate="30000/1001"> <Representati cs="avc1.64001f" bandwidth="2000000" width="1280" height="720" frameRate="30000/1001"> <SegmentTem on="6000000" availabilityTimeOffset="5.967" initialization="1551938403/init-stream$RepresentationI a="1551938403/chunk-stream_t_$RepresentationID$-$Number%05d$.m4s" startNumber="1"> </SegmentTempla daptationSet contentType="audio" segmentAlignment="true" bitstreamSwitching="true"> <Representatio cs="mp4a.40.2" bandwidth="96000" audioSamplingRate="48000"> <AudioChannelConfiguration schemeIdUri nfiguration:2011" value="2" /> <SegmentTemplate timescale="1000000" duration="6000000" availabilit on="1551938403/init-stream$RepresentationID$.m4s" media="1551938403/chunk-stream_t_$Representation SegmentTemplate> </Representation> </AdaptationSet> </Period> </MPD> <?xml version="1.0" encoding= w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xlink="http://www.w3.o on="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DA les="urn:mpeg:dash:profile:isoff-live:2011" type="dynamic" minimumUpdatePeriod="PT500S" suggestedP StartTime="2019-03-07T06:00:04Z" publishTime="2019-03-07T22:26:16Z" timeShiftBufferDepth="PT18.0S" tion> </ProgramInformation> <Period start="PT0.0S"> <AdaptationSet contentType="video" segmentAlig ameRate="30000/1001"> <Representation id="0" mimeType="video/mp4" codecs="avc1.64001f" bandwidth=" ate="30000/1001"> <SegmentTemplate timescale="1000000" duration="6000000" availabilityTimeOffset=" on="1551938403/init-stream$RepresentationID$.m4s" media="1551938403/chunk-stream_t_$Representation SegmentTemplate> </Representation> </AdaptationSet> <AdaptationSet contentType="audio" segmentAlig epresentation id="1" mimeType="audio/mp4" codecs="mp4a.40.2" bandwidth="96000" audioSamplingRate=" IdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2" /> <SegmentTemplate times ilityTimeOffset="5.979" initialization="1551938403/init-stream$RepresentationID$.m4s" media="15519 $-$Number%05d$.m4s" startNumber="1"> </SegmentTemplate> </Representation> </AdaptationSet> </Perio g="utf-8"?> <MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf andards/MPEG-DASH_schema_files/DASH-MPD.xsd" profiles="urn:mpeg:dash:profile:isoff-live:2011" type ggestedPresentationDelay="PT6S" availabilityStartTime="2019-03-07T06:00:04Z" publishTime="2019-03- pth="PT18.0S" minBufferTime="PT .0S"> <ProgramInformation> </ProgramInformation> <Period start="PT pe="video" segmentAlignment="true" bitstreamSwitching="true" frameRate="30000/1001"> <Representati cs="avc1.64001f" bandwidth="2000000" width="1280" height="720" frameRate="30000/1001"> <SegmentTem on="6000000" availabilityTimeOffset="5.967" initialization="1551938403/init-stream$RepresentationI a="1551938403/chunk-stream_t_$RepresentationID$-$Number%05d$.m4s" startNumber="1"> </SegmentTempla daptationSet contentType="audio" segmentAlignment="true" bitstreamSwitching="true"> <?xml version= ="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xlink="ht Location="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/M les="urn:mpeg:dash:profile:isoff-live:2011" type="dynamic" minimumUpdatePeriod="PT500S" suggestedP StartTime="2019-03-07T06:00:04Z shTime="2019-03-07T22:26:16Z" timeShiftBufferDepth="PT18.0S" on> </ProgramInformation> <Per <Adap ationSet contentType="video" segmentAlignm ameRate="30000/1001"> <Represe pe=" ideo/mp4" codecs="avc1.64001f" bandwidth=" ate="30000/1001"> <SegmentTempl 00" uration="6000000" availabilityTimeOffset=" on="1551938403/init-stream$Represent a "1551938403/chunk-stream_t_$Representation SegmentTemplate> </Representation> </AdaptationSet> <AdaptationSet contentType="audio" segmentAlig epresentation id="1" mimeType="audio/mp4" codecs="mp4a.40.2" bandwidth="96000" audioSamplingRate=" IdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2" /> <SegmentTemplate times ilityTimeOffset="5.979" initialization="1551938403/init-stream$RepresentationID$.m4s" media="15519 $-$Number%05d$.m4s" startNumber="1"> </SegmentTemplate> </Representation> </AdaptationSet> </Perio g="utf-8"?> <MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf andards/MPEG-DASH_schema_files/DASH-MPD.xsd" profiles="urn:mpeg:dash:profile:isoff-live:2011" type ggestedPresentationDelay="PT6S" availabilityStartTime="2019-03-07T06:00:04Z" publishTime="2019-03- pth="PT18.0S" minBufferTime="PT6.0S"> <ProgramInformation> </ProgramInformation> <Period start="PT gmentAlignment="true" bitstreamSwitching="true" frameRate="30000/1001"> <Representation id="0" mim ndwidth="2000000" width="1280" height="720" frameRate="30000/1001"> <SegmentTemplate timescale="10 meOffset="5.967" initialization="1551938403/init-stream$RepresentationID$.m4s" media="1551938403/c r%05d$.m4s" startNumber="1"> </SegmentTemplate> </Representation> </AdaptationSet> <AdaptationSet nt="true" bitstreamSwitching="true"> <Representation id="1" mimeType="audio/mp4" codecs="mp4a.40.2 ate="48000"> <AudioChannelConfiguration s hemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configura mescale="1000000" duration="6000000" availabilityTimeOffset="5.979" initialization="1551938403/ini a="1551938403/chunk-stream_t_$RepresentationID$-$Number%05d$.m4s" startNumber="1"> </SegmentTempla Period> </MPD><?xml version="1.0" encoding="utf-8"?> <MPD xmlns:xsi="http://www.w3.org/2001/XMLSch :mpd:2011" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD clyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd" profiles="urn:mpeg:dash:profile:isoff-l MAY 2019 Understanding the Value of Consistency in OTT Video Delivery WHITE PAPER

Upload: others

Post on 26-Mar-2020

5 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Understanding the Value of Consistency in OTT Video Delivery · Understanding the Value of Consistency in OTT Video Delivery technology to delivery via IP, the need to ensure high-quality

W H I T E PA P E R

<?xml version="1.0" encoding="utf-8"?> <MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema:mpd:2011" xm-

lns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailable-

Standards/MPEG-DASH_schema_files/DASH-MPD.xsd" profiles="urn:mpeg:dash:profile:isoff-live:2011" type="dynamic" minimumUpdatePeriod="PT500S"

suggestedPresentationDelay="PT6S" availabilityStartTime="2019-03-07T06:00:04Z" publishTime="2019-03-07T22:26:16Z" timeShiftBuffer-

Depth="PT18.0S" minBufferTime="PT6.0S"> <ProgramInformation> </ProgramInformation> <Period start="PT0.0S"> <AdaptationSet content-

Type="video" segmentAlignment="true" bitstreamSwitching="true" frameRate="30000/1001"> <Representation id="0" mimeType="video/mp4" co-

decs="avc1.64001f" bandwidth="2000000" width="1280" height="720" frameRate="30000/1001"> <SegmentTemplate timescale="1000000" dura-

tion="6000000" availabilityTimeOffset="5.967" initialization="1551938403/init-stream$RepresentationID$.m4s" me-

dia="1551938403/chunk-stream_t_$RepresentationID$-$Number%05d$.m4s" startNumber="1"> </SegmentTemplate> </Representation> </AdaptationSet>

<AdaptationSet contentType="audio" segmentAlignment="true" bitstreamSwitching="true"> <Representation id="1" mimeType="audio/mp4" co-

decs="mp4a.40.2" bandwidth="96000" audioSamplingRate="48000"> <AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_-

configuration:2011" value="2" /> <SegmentTemplate timescale="1000000" duration="6000000" availabilityTimeOffset="5.979" initializa-

tion="1551938403/init-stream$RepresentationID$.m4s" media="1551938403/chunk-stream_t_$RepresentationID$-$Number%05d$.m4s" startNumber="1">

</SegmentTemplate> </Representation> </AdaptationSet> </Period> </MPD> <?xml version="1.0" encoding="utf-8"?> <MPD xmlns:xsi="http://ww-

w.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLoca-

tion="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd" pro-

files="urn:mpeg:dash:profile:isoff-live:2011" type="dynamic" minimumUpdatePeriod="PT500S" suggestedPresentationDelay="PT6S" availabili-

tyStartTime="2019-03-07T06:00:04Z" publishTime="2019-03-07T22:26:16Z" timeShiftBufferDepth="PT18.0S" minBufferTime="PT6.0S"> <ProgramInfor-

mation> </ProgramInformation> <Period start="PT0.0S"> <AdaptationSet contentType="video" segmentAlignment="true" bitstreamSwitching="true"

frameRate="30000/1001"> <Representation id="0" mimeType="video/mp4" codecs="avc1.64001f" bandwidth="2000000" width="1280" height="720" fram-

eRate="30000/1001"> <SegmentTemplate timescale="1000000" duration="6000000" availabilityTimeOffset="5.967" initializa-

tion="1551938403/init-stream$RepresentationID$.m4s" media="1551938403/chunk-stream_t_$RepresentationID$-$Number%05d$.m4s" startNumber="1">

</SegmentTemplate> </Representation> </AdaptationSet> <AdaptationSet contentType="audio" segmentAlignment="true" bitstreamSwitching="true">

<Representation id="1" mimeType="audio/mp4" codecs="mp4a.40.2" bandwidth="96000" audioSamplingRate="48000"> <AudioChannelConfiguration sche-

meIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2" /> <SegmentTemplate timescale="1000000" duration="6000000" avail-

abilityTimeOffset="5.979" initialization="1551938403/init-stream$RepresentationID$.m4s" media="1551938403/chunk-stream_t_$Representation-

ID$-$Number%05d$.m4s" startNumber="1"> </SegmentTemplate> </Representation> </AdaptationSet> </Period> </MPD><?xml version="1.0" encod-

ing="utf-8"?> <MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xlink="http://ww-

w.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailable-

Standards/MPEG-DASH_schema_files/DASH-MPD.xsd" profiles="urn:mpeg:dash:profile:isoff-live:2011" type="dynamic" minimumUpdatePeriod="PT500S"

suggestedPresentationDelay="PT6S" availabilityStartTime="2019-03-07T06:00:04Z" publishTime="2019-03-07T22:26:16Z" timeShiftBuffer-

Depth="PT18.0S" minBufferTime="PT .0S"> <ProgramInformation> </ProgramInformation> <Period start="PT0.0S"> <AdaptationSet content-

Type="video" segmentAlignment="true" bitstreamSwitching="true" frameRate="30000/1001"> <Representation id="0" mimeType="video/mp4" co-

decs="avc1.64001f" bandwidth="2000000" width="1280" height="720" frameRate="30000/1001"> <SegmentTemplate timescale="1000000" dura-

tion="6000000" availabilityTimeOffset="5.967" initialization="1551938403/init-stream$RepresentationID$.m4s" me-

dia="1551938403/chunk-stream_t_$RepresentationID$-$Number%05d$.m4s" startNumber="1"> </SegmentTemplate> </Representation> </AdaptationSet>

<AdaptationSet contentType="audio" segmentAlignment="true" bitstreamSwitching="true"> <?xml version="1.0" encoding="utf-8"?> <MPD xmlns:x-

si="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:sche-

maLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd" pro-

files="urn:mpeg:dash:profile:isoff-live:2011" type="dynamic" minimumUpdatePeriod="PT500S" suggestedPresentationDelay="PT6S" availabili-

tyStartTime="2019-03-07T06:00:04Z shTime="2019-03-07T22:26:16Z" timeShiftBufferDepth="PT18.0S" minBufferTime="PT6.0S"> <ProgramInforma-

tion> </ProgramInformation> <Per <Adap ationSet contentType="video" segmentAlignment="true" bitstreamSwitching="true"

frameRate="30000/1001"> <Represe pe=" ideo/mp4" codecs="avc1.64001f" bandwidth="2000000" width="1280" height="720" fram-

eRate="30000/1001"> <SegmentTempl 00" uration="6000000" availabilityTimeOffset="5.967" initializa-

tion="1551938403/init-stream$Represent a "1551938403/chunk-stream_t_$RepresentationID$-$Number%05d$.m4s" startNumber="1">

</SegmentTemplate> </Representation> </AdaptationSet> <AdaptationSet contentType="audio" segmentAlignment="true" bitstreamSwitching="true">

<Representation id="1" mimeType="audio/mp4" codecs="mp4a.40.2" bandwidth="96000" audioSamplingRate="48000"> <AudioChannelConfiguration sche-

meIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2" /> <SegmentTemplate timescale="1000000" duration="6000000" avail-

abilityTimeOffset="5.979" initialization="1551938403/init-stream$RepresentationID$.m4s" media="1551938403/chunk-stream_t_$Representation-

ID$-$Number%05d$.m4s" startNumber="1"> </SegmentTemplate> </Representation> </AdaptationSet> </Period> </MPD> <?xml version="1.0" encod-

ing="utf-8"?> <MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xlink="http://ww-

w.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailable-

Standards/MPEG-DASH_schema_files/DASH-MPD.xsd" profiles="urn:mpeg:dash:profile:isoff-live:2011" type="dynamic" minimumUpdatePeriod="PT500S"

suggestedPresentationDelay="PT6S" availabilityStartTime="2019-03-07T06:00:04Z" publishTime="2019-03-07T22:26:16Z" timeShiftBuffer-

Depth="PT18.0S" minBufferTime="PT6.0S"> <ProgramInformation> </ProgramInformation> <Period start="PT0.0S"> <AdaptationSet contentType="video"

segmentAlignment="true" bitstreamSwitching="true" frameRate="30000/1001"> <Representation id="0" mimeType="video/mp4" codecs="avc1.64001f"

bandwidth="2000000" width="1280" height="720" frameRate="30000/1001"> <SegmentTemplate timescale="1000000" duration="6000000" availability-

TimeOffset="5.967" initialization="1551938403/init-stream$RepresentationID$.m4s" media="1551938403/chunk-stream_t_$RepresentationID$-$Num-

ber%05d$.m4s" startNumber="1"> </SegmentTemplate> </Representation> </AdaptationSet> <AdaptationSet contentType="audio" segmentAlign-

ment="true" bitstreamSwitching="true"> <Representation id="1" mimeType="audio/mp4" codecs="mp4a.40.2" bandwidth="96000" audioSamplin-

gRate="48000"> <AudioChannelConfiguration s hemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2" /> <SegmentTemplate

timescale="1000000" duration="6000000" availabilityTimeOffset="5.979" initialization="1551938403/init-stream$RepresentationID$.m4s" me-

dia="1551938403/chunk-stream_t_$RepresentationID$-$Number%05d$.m4s" startNumber="1"> </SegmentTemplate> </Representation> </AdaptationSet>

</Period> </MPD><?xml version="1.0" encoding="utf-8"?> <MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:sche-

ma:mpd:2011" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/Pub-

liclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd" profiles="urn:mpeg:dash:profile:isoff-live:2011" type="dynamic" minimumUpdatePe-

riod="PT500S" suggestedPresentationDelay="PT6S" availabilityStartTime="2019-03-07T06:00:04Z" publishTime="2019-03-07T22:26:16Z" timeShift-

BufferDepth="PT18.0S" minBufferTime="PT6.0S"> <ProgramInformation> </ProgramInformation> <Period start="PT0.0S"> <AdaptationSet content-

Type="video" segmentAlignment="true" bitstreamSwitching="true" frameRate="30000/1001"> <Representation id="0" mimeType="video/mp4" co-

decs="avc1.64001f" bandwidth="2000000" width="1280" height="720" frameRate="30000/1001"> <SegmentTemplate timescale="1000000" dura-

tion="6000000" availabilityTimeOffset="5.967" initialization="1551938403/init-stream$RepresentationID$.m4s" me-

dia="1551938403/chunk-stream_t_$RepresentationID$-$Number%05d$.m4s" startNumber="1"> </SegmentTemplate> </Representation> </AdaptationSet>

<AdaptationSet contentType="audio" segmentAlignment="true" bitstreamSwitching="true">

MAY 2019

Understanding the Value of Consistencyin OTT Video Delivery

WHITE PAPER

Page 2: Understanding the Value of Consistency in OTT Video Delivery · Understanding the Value of Consistency in OTT Video Delivery technology to delivery via IP, the need to ensure high-quality

log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http";

"strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func

main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPoll-

Channel := make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for

{ select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChan-

nel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan:

workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool)

{http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read

this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err

:= strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; };

msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control

message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Han-

dleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

<- timeout: fmt.Fprint(w, "TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); };log.Fa-

tal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http"; "str-

conv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func main() {

controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPollChannel :=

make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for { select {

case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChannel: worker-

Active = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan: workerActive

= status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool) {http.Handle-

Func("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read this stuff?

They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err := str-

conv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; }; msg

:= ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control mes-

sage issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Handle-

Func("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

Quality. Insight. Innovation.20 Years at the Edge.

1

Understanding the Value of Consistency in OTT Video Delivery

Executive Summary

As the video industry continues its transition from broadcast

technology to delivery via IP, the need to ensure high-quality

video streams, without interruption and at scale, is a priority.

For all companies delivering content to consumers, failure to

do so can have a demonstrable impact on revenues from both

advertising and subscriptions.

OTT service providers face a challenge, however, in meeting

growing expectations from viewers for a high-quality video

experience on any device. Research confirms viewers expect

their video to start promptly and play smoothly. Rebuffering

decreases viewer engagement with the content and can result

in them abandoning a stream or even a service.

Yet the video delivery supply chain is complex, and it’s difficult

to find the root cause of buffering. OTT service providers must

ensure they work with technology partners who can help them

deliver a stable and consistent video experience.

A poor-quality video experience impacts the bottom line. Akamai

estimates that an average of one instance of rebuffering per play

could result, for one leading U.S. network, in a loss of advertising

revenue worth more than $80,000. It can also cause subscribers to

leave a service. On the other hand, one SVOD provider reduced

churn by 90% by improving video quality.

A number of obstacles face OTT services looking to improve

the quality of the video experience, including old delivery

infrastructure and the growth of live streaming, and making

the case for further investment.

However, improving OTT video quality is not optional. It is a

necessity if businesses are to retain and grow their revenues

from subscribers and advertisers.

OTT has the potential to deliver video, at scale, and at an even higher quality than current broadcast technology allows.

Page 3: Understanding the Value of Consistency in OTT Video Delivery · Understanding the Value of Consistency in OTT Video Delivery technology to delivery via IP, the need to ensure high-quality

log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http";

"strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func

main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPoll-

Channel := make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for

{ select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChan-

nel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan:

workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool)

{http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read

this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err

:= strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; };

msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control

message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Han-

dleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

<- timeout: fmt.Fprint(w, "TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); };log.Fa-

tal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http"; "str-

conv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func main() {

controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPollChannel :=

make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for { select {

case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChannel: worker-

Active = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan: workerActive

= status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool) {http.Handle-

Func("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read this stuff?

They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err := str-

conv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; }; msg

:= ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control mes-

sage issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Handle-

Func("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

Quality. Insight. Innovation.20 Years at the Edge.

2

Understanding the Value of Consistency in OTT Video Delivery

Introduction: Video Quality Matters Akamai, the intelligent edge platform for securing and delivering digital experiences, asked MTM, a research and

strategy consultancy specializing in media and technology, to investigate industry perspectives around the quality

of IP-delivered video streams. What do industry executives see as the challenges of measuring and delivering high-

quality video streams? And what is the impact of video quality on their bottom line?

MTM spoke to senior executives from a range of premium video distributors to better understand current trends

and dynamics, and to explore their views on the importance of video quality to commercial success.

The TV industry is undergoing a huge transformation in the way it delivers content to viewers. Traditional broadcast

technology still dominates the current delivery infrastructure (and industry revenues), but the success of OTT services

delivering both on-demand and live linear content has accelerated the transition towards IP delivery. This shift is im-

pacting new and traditional businesses, including pay-TV platforms, content brands, and traditional broadcasters.

A key driver of this transition is consumer expectation about how, when, and where they access the content they want

to see, and the quality of the video stream they receive. Premium on-demand services such as Netflix and Hulu have

changed consumer expectations, even if the majority (70%) of viewing of Netflix content is now on a TV, according to

the company’s own research1. Consumers now expect the video experience delivered via IP to match or even exceed

that delivered by traditional broadcast technology.

Therein lies the difficulty for companies providing content services. Meeting expectations around video quality places

significant demands on a complex video-supply chain, and the stakes are high for businesses: Consumers’ tolerance

for degradations in video quality is low, and the risk of users going elsewhere is high.

Acquiring customers (and content) is an expensive business, so retention is top-of-mind for the technical teams

responsible for ensuring that viewers receive the best possible video experience. But how is that video quality

measured? What are the important metrics when measuring quality, and what is the impact on the business of

not delivering consistent quality? How are audiences’ expectations of video quality evolving, and what technical

and commercial challenges do those expectations create for the delivery of premium video services?

This paper identifies some of the key current industry trends and dynamics around the issue of video quality in the

evolving U.S. market for premium OTT video services. Akamai would like to thank all participants in the interview

program, and welcomes further discussion and input from clients and industry stakeholders.

1 Recode (2018), “You can watch Netflix on any screen you want . . .” available online here

“ You only have one or two times to get someone’s attention.

If you don’t, you lose that potential audience since there are so

many more entertainment opportunities for consumers to find.”

— VP, Strategy and Innovation, Multichannel broadcaster

Page 4: Understanding the Value of Consistency in OTT Video Delivery · Understanding the Value of Consistency in OTT Video Delivery technology to delivery via IP, the need to ensure high-quality

log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http";

"strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func

main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPoll-

Channel := make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for

{ select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChan-

nel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan:

workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool)

{http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read

this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err

:= strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; };

msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control

message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Han-

dleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

<- timeout: fmt.Fprint(w, "TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); };log.Fa-

tal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http"; "str-

conv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func main() {

controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPollChannel :=

make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for { select {

case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChannel: worker-

Active = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan: workerActive

= status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool) {http.Handle-

Func("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read this stuff?

They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err := str-

conv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; }; msg

:= ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control mes-

sage issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Handle-

Func("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

Quality. Insight. Innovation.20 Years at the Edge.

3

Understanding the Value of Consistency in OTT Video Delivery

The last of these is increasingly mandatory:

However, while the importance of delivering a high-

quality video experience is increasingly recognized,

defining or measuring it remains problematic. For now,

there is no definitive industry standard that evaluates the

quality of the video stream experienced by the end user.

There is one KPI, though, which is the predominant

measure of quality from a UX perspective: buffering, or

more specifically rebuffering, when a viewer has been

receiving a video stream, but it then stops, and the video

has to catch up. “Rebuffering ratio [the ratio between

the rebuffering duration and the actual duration of video

that is played] is the one we get measured against, and

the one we report to our CEO on a monthly basis.” — Senior Manager, Major broadcaster.

“ If the content is produced by a professional

publisher, there is an expectation — like that

set by Netflix, with video being delivered at 10

to 15 Mbps — that it should be of high quality,

wherever it is viewed.”

— VP, Strategy and Innovation, Multichannel broadcaster

OTT Service Providers Must Meet Growing Expectations Around Video QualityIn an increasingly competitive marketplace,

premium video providers are looking at various ways

to differentiate their OTT services. Their options

include investing in content, offering aggressive

pricing, enhancing the user experience (UX), and

focusing on consistent streaming quality. However,

Page 5: Understanding the Value of Consistency in OTT Video Delivery · Understanding the Value of Consistency in OTT Video Delivery technology to delivery via IP, the need to ensure high-quality

log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http";

"strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func

main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPoll-

Channel := make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for

{ select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChan-

nel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan:

workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool)

{http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read

this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err

:= strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; };

msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control

message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Han-

dleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

<- timeout: fmt.Fprint(w, "TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); };log.Fa-

tal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http"; "str-

conv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func main() {

controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPollChannel :=

make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for { select {

case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChannel: worker-

Active = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan: workerActive

= status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool) {http.Handle-

Func("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read this stuff?

They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err := str-

conv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; }; msg

:= ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control mes-

sage issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Handle-

Func("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

Quality. Insight. Innovation.20 Years at the Edge.

4

Understanding the Value of Consistency in OTT Video Delivery

Avoiding or minimizing rebuffering is the priority for most service providers, who recognize the direct relationship

between the incidences of rebuffering and viewer engagement. Viewers have little patience for rebuffering and are

likely to abandon a stream or even a service altogether if the issue persists.

This connection between rebuffering and viewer engagement was evidenced further in a 2017 study conducted

for Akamai by Sensum2. This demonstrated — using a variety of sophisticated experiential research techniques —

how viewers watching higher-fidelity video streams that are uninterrupted by rebuffering are more engaged

emotionally with storylines and spend longer watching that content. Moreover, a higher-quality video experience —

with minimal rebuffering — has positive impacts on brand perception and the propensity for viewers to recommend

a particular service.

Video start time is another area that very obviously impacts the viewer’s experience. It reflects a common theme —

that consumers are looking for a consistency of the video stream rather than prioritizing peak quality. “I do think

consumer expectations have changed in the time it takes to get to your first frame, but not in terms of the actual

image. The page loads fast, the time to start, and the ads — quality in those three domains matters — and they are

all linked to first frame.” — CTO, Major TV network.

Other KPIs — such as the number of times the bitrate shifts during a video stream — are seen as less important than

ensuring viewers receive as few interruptions to their service as possible. Achieving this requires an investment in

people and technology that executives now consider critical for any premium OTT service.

Akamai then conducted a further study, to understand what “good” actually looks like in terms of streamed video.

It commissioned Eurofins to test how Perceptual Quality (PQ) — the video fidelity actually seen by the viewer — is

impacted by a series of factors, including the video device or player, the content genre viewed, the encode profile,

and the network conditions typically experienced by users.

The results showed significant variation in the bitrate required to deliver a high-definition image to different devices,

even when the content and the network conditions were the same. In summary, the research demonstrated the need

to understand the relationship between the device, the consistency of video throughput, and the content genre —as

well as network conditions — to ensure viewers receive the best possible video experience.

2 Akamai (2017), “New study: Quality of OTT video streaming experiences directly tied to viewer loyalty, service provider success,” available online here

“ Any business pays a cost for acquisition — it’s the same thing

for streaming. You have an obligation to make that experience

as clear and smooth as possible”

— VP, Strategy and Innovation, Multichannel broadcaster

“Where we see customer engagement drop is if they’re seeing that spinning wheel more than a couple of times. Our target is to keep that below 0.5%. When rebuffering is less than 0.5, 90% of sessions are completed. As soon as you get to 0.5-1% then the number starts to drop — 80%. As soon as you hit 1% you see the rate drop down to 50%.”

— Senior Manager, Major broadcaster.

Page 6: Understanding the Value of Consistency in OTT Video Delivery · Understanding the Value of Consistency in OTT Video Delivery technology to delivery via IP, the need to ensure high-quality

log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http";

"strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func

main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPoll-

Channel := make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for

{ select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChan-

nel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan:

workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool)

{http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read

this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err

:= strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; };

msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control

message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Han-

dleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

<- timeout: fmt.Fprint(w, "TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); };log.Fa-

tal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http"; "str-

conv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func main() {

controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPollChannel :=

make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for { select {

case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChannel: worker-

Active = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan: workerActive

= status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool) {http.Handle-

Func("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read this stuff?

They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err := str-

conv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; }; msg

:= ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control mes-

sage issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Handle-

Func("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

Quality. Insight. Innovation.20 Years at the Edge.

5

Understanding the Value of Consistency in OTT Video Delivery

“ When you’re watching TV, you don’t see a gap between

TV and ad, so the next big step for us is to make OTT

more natural.

— Senior Manager, Major broadcaster

Multiple Technical Factors Can Impact Video Quality

While the impact of buffering on a user’s experience is now increasingly

understood, finding the cause can be a technical puzzle. The root of the

problem could be any part of a long and complex video supply chain that

includes the ISP, the CDN, a user’s connected device and browser, their Wi-

Fi setup, available bandwidth, network traffic, or the temporal and spatial

qualities of the content itself. “Delivering video is a complicated process,

with many loopholes and rabbit holes that can happen with that process.”

— VP, Strategy and Innovation, Multichannel broadcaster

For OTT services that include advertising, the addition of ads into a video

stream, especially via client-side ad insertion, can also create issues around

video restarts that impact the user experience.

It is essential for OTT service providers to respond quickly to a range of

issues across the video delivery ecosystem. To that end, they should identify

technology partners who have the resources, experience, and ability to help

them deliver a stable and consistent video experience.

As audiences become more sophisticated, content distributors should also

make sure they engage with tech-savvy users, especially when there are any

issues. When customers care deeply about video quality, SVOD providers

must ensure they are transparent and communicative, through blog posts and

engagement with their user community. “We actually had an incident where

we were playing with our transcoding and users got upset because it affected

the bit rates. We apologized and emphasized that it wasn’t an attempt to

reduce quality or save on costs … We have a particularly tuned audience,

who debate with our engineering staff. You have no choice but to engage.”

— CEO, Video aggregator.

Page 7: Understanding the Value of Consistency in OTT Video Delivery · Understanding the Value of Consistency in OTT Video Delivery technology to delivery via IP, the need to ensure high-quality

log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http";

"strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func

main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPoll-

Channel := make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for

{ select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChan-

nel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan:

workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool)

{http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read

this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err

:= strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; };

msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control

message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Han-

dleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

<- timeout: fmt.Fprint(w, "TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); };log.Fa-

tal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http"; "str-

conv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func main() {

controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPollChannel :=

make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for { select {

case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChannel: worker-

Active = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan: workerActive

= status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool) {http.Handle-

Func("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read this stuff?

They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err := str-

conv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; }; msg

:= ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control mes-

sage issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Handle-

Func("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

Quality. Insight. Innovation.20 Years at the Edge.

6

Understanding the Value of Consistency in OTT Video Delivery

Video Quality Impacts The Bottom LineTo try and quantify the business impact of rebuffering, Akamai modeled the potential loss of advertising revenue

resulting from one additional instance of rebuffering. It looked at the volume of online video traffic of a leading U.S.

network, which amounted to 370 million plays between June 2017 and June 2018. Based on the vast repository of

video QoE data captured by its Media Analytics tool, Akamai has observed a direct relationship between rebuffering

events and abandonment. Assuming — based on Akamai’s data — that each instance of rebuffering results in a 1%

abandonment rate, and with an average play duration of just over 8 minutes, that a single rebuffer would result in

496,417 hours lost, or the equivalent of 10.7 million ad impressions (assuming 11 minutes of ad time per hour, and

average ad length of 30 seconds). Assuming a CPM (Cost Per Mille) of $8, a single instance of rebuffering could

therefore result in lost revenue of $85,500.

Rebuffering can negatively impact subscription revenues, too. Executives working for premium video services see

a clear link between video quality and user retention. For subscription-based services in a competitive market, the

ability to deliver a consistent level of video quality has a demonstrable impact not just on user engagement but on

the business’s bottom line. Companies who have invested in improving video quality are seeing tangible business

benefits now.

Additional Revenue Lost

+$85,800

Additional Ad Impressions Lost

+10,700,000

Additional Viewing

Hours Lost

+496,417

Additional Plays Abandoned

+3,700,000

EXHIBIT: Modeling the impact of rebuffering on a broadcaster’s ad revenue

SOURCE: Akamai estimates, using data from Media Analytics, Nielsen, eMarketer

“ Is there a link between quality and user retention? Provably so.

When we first started, the No. 1 reason for churn, according

to customers, was because of video quality and buffering. That

has now improved by 90%. As we improved, it went from the

No. 1 problem to immaterial.”

— COO, SVOD service

What is the revenue impact of an instance of rebuffering

on a U.S. network that had 370,000,000 plays (6/15/2017 to 6/15/2018)?

Avg Rebuffers per Play

+1

Rebuffer Abandonment

Rate +1%

Page 8: Understanding the Value of Consistency in OTT Video Delivery · Understanding the Value of Consistency in OTT Video Delivery technology to delivery via IP, the need to ensure high-quality

log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http";

"strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func

main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPoll-

Channel := make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for

{ select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChan-

nel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan:

workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool)

{http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read

this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err

:= strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; };

msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control

message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Han-

dleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

<- timeout: fmt.Fprint(w, "TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); };log.Fa-

tal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http"; "str-

conv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func main() {

controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPollChannel :=

make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for { select {

case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChannel: worker-

Active = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan: workerActive

= status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool) {http.Handle-

Func("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read this stuff?

They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err := str-

conv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; }; msg

:= ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control mes-

sage issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Handle-

Func("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

Quality. Insight. Innovation.20 Years at the Edge.

7

Understanding the Value of Consistency in OTT Video Delivery

Understanding audiences’

preferences and their attitudes

about technology is essential to

monetizing OTT video services

effectively. Audiences make a

trade-off between price, flexibility,

choice, and experience, with

subscribers to high-value pay-TV

bundles the least tolerant of

quality issues.

Audiences currently make

allowances in terms of video quality

for cheaper (or free) services, or

for user-generated video. But

such tolerance is unlikely to be

sustained in the long term.

Delivering the same level of

quality to multiple viewers

remains a challenge given the

diversity of devices and bandwidth

scenarios among viewers. One

distributor of premium content

has responded by segmenting

customers into price- and quality-

sensitive categories. It offers

different versions of the service,

differentiated on quality.

Creating a tiered proposition, with

different price points for different

levels of video quality, is not an

option for all publishers. It creates

operational complexity and

requires a sophisticated marketing

strategy. But it is relevant for

services who can identify a large

enough segment of potentially

valuable subscribers. For those

viewers who invest significantly in,

for example, 4K-enabled TV sets,

quality is likely more important

than price.

“ The delivery process for video is more complex than

ever, while audiences are expecting TV-like quality.”

— VP, Strategy and Innovation, Multichannel broadcaster

“ Buffering on a $150 monthly package is clearly

unacceptable. On a $9.99 OTT service it’s a nuisance,

but people are more likely to accept it.”

— Senior Video Technology Consultant

“ Users who can afford a screen with high quality

might not be price sensitive, but they are very

sensitive to quality.”

— COO, SVOD servicer

“ We offer price plans based on the video quality

delivered to the viewer. We accommodate across all the

different markets — nascent 4K to limited bandwidth.”

— COO, SVOD servicer

Page 9: Understanding the Value of Consistency in OTT Video Delivery · Understanding the Value of Consistency in OTT Video Delivery technology to delivery via IP, the need to ensure high-quality

log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http";

"strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func

main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPoll-

Channel := make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for

{ select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChan-

nel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan:

workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool)

{http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read

this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err

:= strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; };

msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control

message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Han-

dleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

<- timeout: fmt.Fprint(w, "TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); };log.Fa-

tal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http"; "str-

conv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func main() {

controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPollChannel :=

make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for { select {

case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChannel: worker-

Active = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan: workerActive

= status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool) {http.Handle-

Func("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read this stuff?

They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err := str-

conv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; }; msg

:= ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control mes-

sage issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Handle-

Func("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

Quality. Insight. Innovation.20 Years at the Edge.

8

Understanding the Value of Consistency in OTT Video Delivery

Key Challenges Include The Delivery Infrastructure, The Shift To Live, And Investment There is an audience appetite for receiving high-quality video streams (e.g., those delivered in 4K), and a willingness

by distributors to provide it. But executives identified a significant barrier to U.S. audiences enjoying the full potential

of IP-delivered video.

Services that are able to deliver 4K video to users in Asia are unable to do so in the US, given the current constraints.

Growth in the demand for live OTT video creates further potential challenges for those distributing and monetizing

that content. The association of OTT content with on-demand viewing is becoming outmoded with the rise of live and

linear IP-delivered content, such as news and sports.

High-profile live events, delivered via IP, have become more commonplace as a showcase for OTT services. The stakes

are high given the value of both that content and the ad inventory around it; there is often only a small window of

opportunity to get it right. In such instances, service providers need premium-level support from their technology

partners behind the scenes to ensure an optimal experience for the end user.

“ At the end the bottleneck is the delivery infrastructure. In the U.S. market, the

average Internet speed grows less than 1% a year. We have the oldest fiber optic

infrastructure. In Asia, the speed doubles every year. Only 30% of our customers

have the bandwidth to enjoy full 4K quality — and that’s not the fault of CDNs,

it’s the fault of the delivery infrastructure.”

— COO, SVOD service

“ It’s an impact on our business because our genre of films lends itself to this

4K quality. But we are lacking that last mile in terms of getting the image to

the consumer’s screen. It’s much easier catering to a non-U.S. audience —

the delivery dilemmas are completely off the table because bandwidth is

better and more prevalent.”

— COO, SVOD service

Page 10: Understanding the Value of Consistency in OTT Video Delivery · Understanding the Value of Consistency in OTT Video Delivery technology to delivery via IP, the need to ensure high-quality

log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http";

"strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func

main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPoll-

Channel := make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for

{ select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChan-

nel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan:

workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool)

{http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read

this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err

:= strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; };

msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control

message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Han-

dleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

<- timeout: fmt.Fprint(w, "TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); };log.Fa-

tal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http"; "str-

conv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func main() {

controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPollChannel :=

make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for { select {

case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChannel: worker-

Active = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan: workerActive

= status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool) {http.Handle-

Func("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read this stuff?

They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err := str-

conv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; }; msg

:= ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control mes-

sage issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Handle-

Func("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

Quality. Insight. Innovation.20 Years at the Edge.

9

Understanding the Value of Consistency in OTT Video Delivery

Scaling up video delivery to a large, live audience remains a key part of

the challenge. For episodic content, the numbers watching that stream are

generally likely to be small, but with sports and news the numbers start to

add up, with all of them expecting a consistent, high-quality experience.

“It’s difficult when everyone clicks start at the same time, for a news event

or an NHL deadline. A million customers pressing play at the same time —

that’s where the trouble is.” — Senior Manager, Major broadcaster

Yet while many video distributors understand the importance of consistency

to the business model, they still have to make a case for investment in

the infrastructure. Amid pressure to spend more on exclusive and original

content, the case for investing in the technology to deliver that content at

the highest-possible quality also needs to be made.

For companies who also offer traditional pay TV, the IP-delivered service

must compete for resources with the linear broadcast side, which can cause

difficulties: “The justification [for lack of investment] has always been that

we don’t make as much money from the digital side as the linear. And that’s

just because the linear has been around for a longer period of time.”

— VP, Strategy and Innovation, Multichannel broadcaster

“ For a major college basketball

tournament we’re on 22

platforms, and each one of

those platforms has to be a

good experience, and you

have to create software sets to

accommodate all the streaming

and stats. With such a finite

amount of time, you have two

days where you broadcast all

teams. For those two days you

make sure that all the ads are

right and the streaming is OK,

and if you don’t then you lose

a lot of revenue.”

— VP, Strategy and Innovation, Multichannel broadcaster

Page 11: Understanding the Value of Consistency in OTT Video Delivery · Understanding the Value of Consistency in OTT Video Delivery technology to delivery via IP, the need to ensure high-quality

log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http";

"strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func

main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPoll-

Channel := make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for

{ select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChan-

nel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan:

workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool)

{http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read

this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err

:= strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; };

msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control

message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Han-

dleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

<- timeout: fmt.Fprint(w, "TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); };log.Fa-

tal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html"; "log"; "net/http"; "str-

conv"; "strings"; "time" ); type ControlMessage struct { Target string; Count int64; }; func main() {

controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan bool); statusPollChannel :=

make(chan chan bool); workerActive := false;go admin(controlChannel, statusPollChannel); for { select {

case respChan := <- statusPollChannel: respChan <- workerActive; case msg := <-controlChannel: worker-

Active = true; go doStuff(msg, workerCompleteChan); case status := <- workerCompleteChan: workerActive

= status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan chan bool) {http.Handle-

Func("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone actually read this stuff?

They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm(); count, err := str-

conv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Error()); return; }; msg

:= ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.Fprintf(w, "Control mes-

sage issued for Target %s, count %d", html.EscapeString(r.FormValue("target")), count); }); http.Handle-

Func("/status",func(w http.ResponseWriter, r *http.Request) { reqChan := make(chan bool); statusPoll-

Channel <- reqChan;timeout := time.After(time.Second); select { case result := <- reqChan: if result {

fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case <- timeout: fmt.Fprint(w,

"TIMEOUT");}}); log.Fatal(http.ListenAndServe(":1337", nil)); }; package main; import ( "fmt"; "html";

"log"; "net/http"; "strconv"; "strings"; "time" ); type ControlMessage struct { Target string; Count

int64; }; func main() { controlChannel := make(chan ControlMessage);workerCompleteChan := make(chan

bool); statusPollChannel := make(chan chan bool); workerActive := false;go admin(controlChannel, status-

PollChannel); for { select { case respChan := <- statusPollChannel: respChan <- workerActive; case msg

:= <-controlChannel: workerActive = true; go doStuff(msg, workerCompleteChan); case status := <- work-

erCompleteChan: workerActive = status; }}}; func admin(cc chan ControlMessage, statusPollChannel chan

chan bool) {http.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { /* Does anyone ac-

tually read this stuff? They probably should. */ hostTokens := strings.Split(r.Host, ":"); r.ParseForm();

count, err := strconv.ParseInt(r.FormValue("count"), 10, 64); if err != nil { fmt.Fprintf(w, err.Er-

ror()); return; }; msg := ControlMessage{Target: r.FormValue("target"), Count: count}; cc <- msg; fmt.F-

printf(w, "Control message issued for Target %s, count %d", html.EscapeString(r.FormValue("target")),

count); }); http.HandleFunc("/status",func(w http.ResponseWriter, r *http.Request) { reqChan :=

make(chan bool); statusPollChannel <- reqChan;timeout := time.After(time.Second); select { case result

:= <- reqChan: if result { fmt.Fprint(w, "ACTIVE"); } else { fmt.Fprint(w, "INACTIVE"); }; return; case

Quality. Insight. Innovation.20 Years at the Edge.

Understanding the Value of Consistency in OTT Video Delivery

Akamai secures and delivers digital experiences for the world’s largest companies. Akamai’s intelligent edge platform surrounds everything, from the

enterprise to the cloud, so customers and their businesses can be fast, smart, and secure. Top brands globally rely on Akamai to help them realize

competitive advantage through agile solutions that extend the power of their multi-cloud architectures. Akamai keeps decisions, apps, and experiences

closer to users than anyone — and attacks and threats far away. Akamai’s portfolio of edge security, web and mobile performance, enterprise access,

and video delivery solutions is supported by unmatched customer service, analytics, and 24/7/365 monitoring. To learn why the world’s top brands trust

Akamai, visit www.akamai.com, blogs.akamai.com, or @Akamai on Twitter. You can find our global contact information at www.akamai.com/locations.

Published 05/19.

MTM is an international research and strategy consulting firm, specializing in media, technology, and advertising. MTM helps clients around the world

understand and respond to digitally driven change, providing award-winning consumer research, industry analysis, strategic advice, and support for new

ventures, business development, and organizational change and transformation. For more information, please visit www.mtmlondon.com

“ Often we’ll do a screening in raw

4K quality — and it looks absolutely

stunning. Our ambition is to deliver

this to U.S. consumers.”

— COO, SVOD service

“ You look at the consumption

patterns and the relationships

viewers have with [OTT] brands and

you can see there is an insatiable

appetite. My biggest advice would

be to do whatever you can to hire

the right people and make the

right investment.”

— VP, Strategy and Innovation, Multichannel broadcaster

“ 100% of delivery in the next 5-10

years will be over IP. Every operator

in the US has been working to shift

it all over to be 100% IP-based.”

— Senior Video Technology Consultant

But As The Industry Transitions To IP, Improving OTT Video Quality Is Not Optional Traditional broadcast technology still dominates pay-TV

revenues, and can deliver a high-quality video experience

efficiently and at scale. But industry executives are now

clear that the transition to IP delivery of video is under

way. Legacy publishers are starting to adjust their mindset

to this new reality.

Executives acknowledge that currently, the video

experience for IP-delivered content is not perfect,

especially for live sports and high-speed action content,

where the refresh rate is an issue. But they believe it will

get better as the infrastructure improves. Indeed, they

argue that in time, OTT has the potential to deliver video,

at scale, at an even higher quality than current broadcast

technology allows. OTT services, where the infrastructure

allows it, can pull from a purer image than that of

encoded broadcast.

A failure to invest in improving video quality — in terms

of consistency as well as peak quality — will impact the

ability of video distributors to generate revenues from

subscribers and advertisers. In addition, the direction

of travel of the industry, not least in terms of consumer

demand, indicates where the future opportunities lie.