Skip to content

Commit

Permalink
Merge pull request globocom#86 from Eyevinn/feature-cue-envivio
Browse files Browse the repository at this point in the history
Support for Envivio flavoured SCTE35 cue markers
  • Loading branch information
leandromoreira committed Oct 5, 2016
2 parents bbc2dd3 + e130c0f commit 498cef0
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 8 deletions.
25 changes: 21 additions & 4 deletions m3u8/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ def parse(content, strict=False):
state['cue_out'] = True
state['cue_start'] = True

elif line.startswith(protocol.ext_x_cue_span):
state['cue_out'] = True
state['cue_start'] = True

elif line.startswith(protocol.ext_x_version):
_parse_simple_parameter(line, data)

Expand Down Expand Up @@ -280,15 +284,28 @@ def _parse_cueout(line, state):
state['current_cue_out_duration'] = res.group(1)
state['current_cue_out_scte35'] = res.group(2)


def _parse_cueout_start(line, state, prevline):
def _cueout_elemental(line, state, prevline):
param, value = line.split(':', 1)
state['current_cue_out_duration'] = value
res = re.match('.*EXT-OATCLS-SCTE35:(.*)$', prevline)
if res:
state['current_cue_out_scte35'] = res.group(1)
return (res.group(1), value)
else:
return None

def _cueout_envivio(line, state, prevline):
param, value = line.split(':', 1)
res = re.match('.*DURATION=(.*),.*,CUE="(.*)"', value)
if res:
return (res.group(2), res.group(1))
else:
return None

def _parse_cueout_start(line, state, prevline):
_cueout_state = _cueout_elemental(line, state, prevline) or _cueout_envivio(line, state, prevline)
if _cueout_state:
state['current_cue_out_scte35'] = _cueout_state[0]
state['current_cue_out_duration'] = _cueout_state[1]

def string_to_lines(string):
return string.strip().replace('\r\n', '\n').split('\n')

Expand Down
1 change: 1 addition & 0 deletions m3u8/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@
ext_x_scte35 = '#EXT-OATCLS-SCTE35'
ext_x_cue_start = '#EXT-X-CUE-OUT'
ext_x_cue_end = '#EXT-X-CUE-IN'
ext_x_cue_span = '#EXT-X-CUE-SPAN'
30 changes: 29 additions & 1 deletion tests/playlists.py
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@
'''

CUE_OUT_WITH_SCTE35_PLAYLIST = '''
CUE_OUT_ELEMENTAL_PLAYLIST = '''
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
Expand Down Expand Up @@ -511,6 +511,34 @@
master2500_47233.ts
'''

CUE_OUT_ENVIVIO_PLAYLIST = '''
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:11
#EXT-X-MEDIA-SEQUENCE:399703
#EXTINF:10.0000,
20160914T080055-master804-199/1703.ts
#EXTINF:10.0000,
20160914T080055-master804-199/1704.ts
#EXTINF:5.1200,
20160914T080055-master804-199/1705.ts
#EXT-X-CUE-OUT:DURATION=366,ID=16777323,CUE="/DAlAAAENOOQAP/wFAUBAABrf+//N25XDf4B9p/gAAEBAQAAxKni9A=="
#EXTINF:10.0000,
20160914T080055-master804-199/1706.ts
#EXT-X-CUE-SPAN:TIMEFROMSIGNAL=PT10S,ID=16777323
#EXTINF:10.0000,
20160914T080055-master804-199/1707.ts
#EXT-X-CUE-SPAN:TIMEFROMSIGNAL=PT20S,ID=16777323
#EXTINF:10.0000,
20160914T080055-master804-199/1708.ts
#EXT-X-CUE-SPAN:TIMEFROMSIGNAL=PT30S,ID=16777323
#EXTINF:10.0000,
20160914T080055-master804-199/1709.ts
#EXT-X-CUE-IN:ID=16777323
#EXTINF:10.0000,
20160914T080055-master804-199/1710.ts
'''

MULTI_MEDIA_PLAYLIST = '''#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA:URI="chinese/ed.ttml",TYPE=SUBTITLES,GROUP-ID="subs",LANGUAGE="zho",NAME="Chinese",AUTOSELECT=YES,FORCED=NO
Expand Down
11 changes: 9 additions & 2 deletions tests/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,20 @@ def test_segment_cue_out_attribute():
assert segments[3].cue_out == False


def test_segment_scte35_attribute():
obj = m3u8.M3U8(playlists.CUE_OUT_WITH_SCTE35_PLAYLIST)
def test_segment_elemental_scte35_attribute():
obj = m3u8.M3U8(playlists.CUE_OUT_ELEMENTAL_PLAYLIST)
segments = obj.segments
assert segments[4].cue_out == True
assert segments[9].cue_out == False
assert segments[4].scte35 == '/DAlAAAAAAAAAP/wFAUAAAABf+//wpiQkv4ARKogAAEBAQAAQ6sodg=='

def test_segment_envivio_scte35_attribute():
obj = m3u8.M3U8(playlists.CUE_OUT_ENVIVIO_PLAYLIST)
segments = obj.segments
assert segments[3].cue_out == True
assert segments[4].scte35 == '/DAlAAAENOOQAP/wFAUBAABrf+//N25XDf4B9p/gAAEBAQAAxKni9A=='
assert segments[5].scte35 == '/DAlAAAENOOQAP/wFAUBAABrf+//N25XDf4B9p/gAAEBAQAAxKni9A=='
assert segments[7].cue_out == False

def test_keys_on_clear_playlist():
obj = m3u8.M3U8(playlists.SIMPLE_PLAYLIST)
Expand Down
12 changes: 11 additions & 1 deletion tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,13 +189,23 @@ def test_should_parse_program_date_time_from_playlist():
assert cast_date_time('2014-08-13T13:36:33+00:00') == data['program_date_time']

def test_should_parse_scte35_from_playlist():
data = m3u8.parse(playlists.CUE_OUT_WITH_SCTE35_PLAYLIST)
data = m3u8.parse(playlists.CUE_OUT_ELEMENTAL_PLAYLIST)
assert not data['segments'][2]['cue_out']
assert data['segments'][3]['scte35']
assert data['segments'][3]['cue_out']
assert '/DAlAAAAAAAAAP/wFAUAAAABf+//wpiQkv4ARKogAAEBAQAAQ6sodg==' == data['segments'][4]['scte35']
assert '50' == data['segments'][4]['scte35_duration']

def test_should_parse_envivio_cue_playlist():
data = m3u8.parse(playlists.CUE_OUT_ENVIVIO_PLAYLIST)
assert data['segments'][3]['scte35']
assert data['segments'][3]['cue_out']
assert '/DAlAAAENOOQAP/wFAUBAABrf+//N25XDf4B9p/gAAEBAQAAxKni9A==' == data['segments'][3]['scte35']
assert '366' == data['segments'][3]['scte35_duration']
assert data['segments'][4]['cue_out']
assert '/DAlAAAENOOQAP/wFAUBAABrf+//N25XDf4B9p/gAAEBAQAAxKni9A==' == data['segments'][4]['scte35']
assert '/DAlAAAENOOQAP/wFAUBAABrf+//N25XDf4B9p/gAAEBAQAAxKni9A==' == data['segments'][5]['scte35']

def test_parse_simple_playlist_messy():
data = m3u8.parse(playlists.SIMPLE_PLAYLIST_MESSY)
assert 5220 == data['targetduration']
Expand Down

0 comments on commit 498cef0

Please sign in to comment.