Skip to content
This repository has been archived by the owner on Dec 8, 2024. It is now read-only.

Commit

Permalink
Merge pull request #40 from amagimedia/master
Browse files Browse the repository at this point in the history
Parsing SCTE35 tag from playlist
  • Loading branch information
bradleyfalzon committed Mar 17, 2016
2 parents 3cc81c6 + 8956645 commit 4a78874
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 0 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ to the project. They listed below in an alphabetical order:
- Kz26
- Makombo
- Scott Kidder <[email protected]>
- Vishal Kumar Tuniki <[email protected]>
- Zac Shenker <[email protected]>

If you want to be added to this list (or removed for any reason)
Expand Down
21 changes: 21 additions & 0 deletions reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,20 @@ func decodeLineOfMediaPlaylist(p *MediaPlaylist, wv *WV, state *decodingState, l
return fmt.Errorf("Byterange sub-range offset value parsing error: %s", err)
}
}
case !state.tagSCTE35 && strings.HasPrefix(line, "#EXT-SCTE35:"):
state.tagSCTE35 = true
state.listType = MEDIA
state.scte = new(SCTE)
for attribute, value := range decodeParamsLine(line[12:]) {
switch attribute {
case "CUE":
state.scte.Cue = value
case "ID":
state.scte.ID = value
case "TIME":
state.scte.Time, _ = strconv.ParseFloat(value, 64)
}
}
case !state.tagInf && strings.HasPrefix(line, "#EXTINF:"):
state.tagInf = true
state.listType = MEDIA
Expand Down Expand Up @@ -471,6 +485,13 @@ func decodeLineOfMediaPlaylist(p *MediaPlaylist, wv *WV, state *decodingState, l
}
state.tagRange = false
}
if state.tagSCTE35 {
state.tagSCTE35 = false
scte := *state.scte
if err = p.SetSCTE(scte.Cue, scte.ID, scte.Time); strict && err != nil {
return err
}
}
if state.tagDiscontinuity {
state.tagDiscontinuity = false
if err = p.SetDiscontinuity(); strict && err != nil {
Expand Down
48 changes: 48 additions & 0 deletions reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,51 @@ func ExampleMediaPlaylist_DurationAsInt() {
// #EXTINF:10,
// movieB.ts
}

func TestMediaPlaylistWithSCTE35Tag(t *testing.T) {
test_cases := []struct {
playlistLocation string
expectedSCTEIndex int
expectedSCTECue string
expectedSCTEID string
expectedSCTETime float64
}{
{
"sample-playlists/media-playlist-with-scte35.m3u8",
2,
"/DAIAAAAAAAAAAAQAAZ/I0VniQAQAgBDVUVJQAAAAH+cAAAAAA==",
"123",
123.12,
},
{
"sample-playlists/media-playlist-with-scte35-1.m3u8",
1,
"/DAIAAAAAAAAAAAQAAZ/I0VniQAQAgBDVUVJQAA",
"",
0,
},
}
for _, c := range test_cases {
f, _ := os.Open(c.playlistLocation)
playlist, _, _ := DecodeFrom(bufio.NewReader(f), true)
mediaPlaylist := playlist.(*MediaPlaylist)
for index, item := range mediaPlaylist.Segments {
if item == nil {
break
}
if index != c.expectedSCTEIndex && item.SCTE != nil {
t.Error("Not expecting SCTE information on this segment")
} else if index == c.expectedSCTEIndex && item.SCTE == nil {
t.Error("Expecting SCTE information on this segment")
} else if index == c.expectedSCTEIndex && item.SCTE != nil {
if (*item.SCTE).Cue != c.expectedSCTECue {
t.Error("Expected ", c.expectedSCTECue, " got ", (*item.SCTE).Cue)
} else if (*item.SCTE).ID != c.expectedSCTEID {
t.Error("Expected ", c.expectedSCTEID, " got ", (*item.SCTE).ID)
} else if (*item.SCTE).Time != c.expectedSCTETime {
t.Error("Expected ", c.expectedSCTETime, " got ", (*item.SCTE).Time)
}
}
}
}
}
11 changes: 11 additions & 0 deletions sample-playlists/media-playlist-with-scte35-1.m3u8
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.000,
media0.ts
#EXT-SCTE35: CUE="/DAIAAAAAAAAAAAQAAZ/I0VniQAQAgBDVUVJQAA"
#EXTINF:10.000,
media1.ts
#EXTINF:10.000,
media2.ts
11 changes: 11 additions & 0 deletions sample-playlists/media-playlist-with-scte35.m3u8
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.000,
media0.ts
#EXTINF:10.000,
media1.ts
#EXT-SCTE35: CUE="/DAIAAAAAAAAAAAQAAZ/I0VniQAQAgBDVUVJQAAAAH+cAAAAAA==", ID="123", TIME=123.12
#EXTINF:10.000,
media2.ts
9 changes: 9 additions & 0 deletions structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,16 @@ type MediaSegment struct {
Key *Key // EXT-X-KEY displayed before the segment and means changing of encryption key (in theory each segment may have own key)
Map *Map // EXT-X-MAP displayed before the segment
Discontinuity bool // EXT-X-DISCONTINUITY indicates an encoding discontinuity between the media segment that follows it and the one that preceded it (i.e. file format, number and type of tracks, encoding parameters, encoding sequence, timestamp sequence)
SCTE *SCTE // EXT-SCTE35 used for Ad signaling in HLS
ProgramDateTime time.Time // EXT-X-PROGRAM-DATE-TIME tag associates the first sample of a media segment with an absolute date and/or time
}

type SCTE struct {
Cue string
ID string
Time float64
}

// This structure represents information about stream encryption.
//
// Realizes EXT-X-KEY tag.
Expand Down Expand Up @@ -252,6 +259,7 @@ type decodingState struct {
tagStreamInf bool
tagIframeStreamInf bool
tagInf bool
tagSCTE35 bool
tagRange bool
tagDiscontinuity bool
tagProgramDateTime bool
Expand All @@ -264,4 +272,5 @@ type decodingState struct {
variant *Variant
xkey *Key
xmap *Map
scte *SCTE
}
8 changes: 8 additions & 0 deletions writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,14 @@ func (p *MediaPlaylist) SetRange(limit, offset int64) error {
return nil
}

func (p *MediaPlaylist) SetSCTE(cue string, id string, time float64) error {
if p.count == 0 {
return errors.New("playlist is empty")
}
p.Segments[(p.tail-1)%p.capacity].SCTE = &SCTE{cue, id, time}
return nil
}

// Set discontinuity flag for the current media segment.
// EXT-X-DISCONTINUITY indicates an encoding discontinuity between the media segment
// that follows it and the one that preceded it (i.e. file format, number and type of tracks,
Expand Down

0 comments on commit 4a78874

Please sign in to comment.