New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixing SCTE-35 implicit signal closing rules. #63
Changes from 4 commits
5761ae9
dd56e10
3106078
de49d1f
376db19
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,4 @@ | |
.DS_Store | ||
.*.swp | ||
*.ts | ||
coverage.* |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,6 +45,7 @@ type segmentationDescriptor struct { | |
subSegsExpected uint8 | ||
spliceInfo SCTE35 | ||
eventCancelIndicator bool | ||
hasSubSegments bool | ||
} | ||
|
||
type segCloseType uint8 | ||
|
@@ -148,6 +149,7 @@ func (d *segmentationDescriptor) parseDescriptor(data []byte) error { | |
if buf.Len() > 0 && (d.typeID == 0x34 || d.typeID == 0x36) { | ||
d.subSegNum = readByte() | ||
d.subSegsExpected = readByte() | ||
d.hasSubSegments = true | ||
} | ||
} | ||
return nil | ||
|
@@ -255,19 +257,20 @@ func (d *segmentationDescriptor) CanClose(out SegmentationDescriptor) bool { | |
return true | ||
} | ||
case segCloseEventIDNotNested: | ||
// this should also consider segnum == segexpected for IN signals closing an out signal. | ||
if d.IsIn() && d.EventID() == out.EventID() && d.SegmentNumber() == d.SegmentsExpected() { | ||
return true | ||
} | ||
if d.EventID() != out.EventID() { | ||
return false | ||
} | ||
fallthrough | ||
case segCloseNotNested: | ||
if d.IsIn() { | ||
// if in, last desc is x/x | ||
if d.segNum == d.segsExpected { | ||
case segCloseNotNested: // only applies to 0x34 and 0x36 with subsegments. | ||
if d.IsOut() && (d.TypeID() == SegDescProviderPOStart || d.TypeID() == SegDescDistributorPOStart) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like this should be dictated by the closeRules map only 0x34 and 0x36 should have segCloseNotNested, the extra logic here is unneeded, and two places will have to be updated if another signal gets this close rule footnote. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good call. |
||
if d.HasSubSegments() && d.SubSegmentNumber() == d.SubSegmentsExpected() { | ||
return true | ||
} | ||
} else if d.IsOut() { | ||
// if out, first descriptor in set closes existing open | ||
if d.segNum == 1 { | ||
if d.TypeID() == out.TypeID() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same story here, I think this is already done in the close rules map and this is just extra, unneeded logic. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. |
||
return true | ||
} | ||
} | ||
|
@@ -296,6 +299,21 @@ func (d *segmentationDescriptor) Equal(c SegmentationDescriptor) bool { | |
if d.EventID() != c.EventID() { | ||
return false | ||
} | ||
if d.SegmentNumber() != c.SegmentNumber() { | ||
return false | ||
} | ||
if d.SegmentsExpected() != c.SegmentsExpected() { | ||
return false | ||
} | ||
if d.HasSubSegments() != c.HasSubSegments() { | ||
return false | ||
} | ||
if d.HasSubSegments() && c.HasSubSegments() && (d.SubSegmentNumber() != c.SubSegmentNumber()) { | ||
return false | ||
} | ||
if d.HasSubSegments() && c.HasSubSegments() && (d.SubSegmentsExpected() != c.SubSegmentsExpected()) { | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
|
@@ -307,6 +325,10 @@ func (d *segmentationDescriptor) SegmentsExpected() uint8 { | |
return d.segsExpected | ||
} | ||
|
||
func (d *segmentationDescriptor) HasSubSegments() bool { | ||
return d.hasSubSegments | ||
} | ||
|
||
func (d *segmentationDescriptor) SubSegmentNumber() uint8 { | ||
return d.subSegNum | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
/* | ||
MIT License | ||
|
||
Copyright 2016 Comcast Cable Communications Management, LLC | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. | ||
*/ | ||
package scte35 | ||
|
||
import "testing" | ||
|
||
var csp = []byte{ | ||
0x00, 0xfc, 0x30, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf0, 0x00, 0x00, 0x00, | ||
0x3d, 0x02, 0x3b, 0x43, 0x55, 0x45, 0x49, 0xc0, 0x00, 0x00, 0x00, 0x7f, 0xbf, 0x0f, 0x2c, 0x75, | ||
0x72, 0x6e, 0x3a, 0x6d, 0x65, 0x72, 0x6c, 0x69, 0x6e, 0x3a, 0x6c, 0x69, 0x6e, 0x65, 0x61, 0x72, | ||
0x3a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x3a, 0x38, 0x39, 0x38, 0x37, 0x32, 0x30, 0x35, 0x34, | ||
0x37, 0x34, 0x34, 0x32, 0x34, 0x39, 0x38, 0x34, 0x31, 0x36, 0x33, 0x01, 0x00, 0x00, 0x1a, 0x3f, | ||
0x5c, 0x92, | ||
} | ||
|
||
var unscheduled_event_start = []byte{ | ||
0x00, 0xfc, 0x30, 0x30, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x20, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, | ||
0x00, 0x02, 0xbf, 0xd4, 0x00, 0x1a, 0x02, 0x18, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x02, | ||
0x7f, 0xff, 0x00, 0x00, 0x0a, 0xff, 0x50, 0x09, 0x04, 0x54, 0x45, 0x53, 0x54, 0x40, 0x00, 0x00, | ||
0xff, 0xcb, 0x8c, 0xcc, | ||
} | ||
|
||
var network_end = []byte{ | ||
0x00, 0xfc, 0x30, 0x30, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x20, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, | ||
0x00, 0x02, 0xbf, 0xd4, 0x00, 0x1a, 0x02, 0x18, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x02, | ||
0x7f, 0xff, 0x00, 0x00, 0x0a, 0xff, 0x50, 0x09, 0x04, 0x54, 0x45, 0x53, 0x54, 0x51, 0x00, 0x00, | ||
0xfd, 0xfd, 0x2c, 0x21, | ||
} | ||
|
||
var program_start = []byte{ | ||
0x00, 0xfc, 0x30, 0x35, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x20, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, | ||
0x00, 0x02, 0xbf, 0xd4, 0x00, 0x1f, 0x02, 0x1d, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x02, | ||
0x7f, 0xff, 0x00, 0x09, 0xa7, 0xec, 0x80, 0x09, 0x09, 0x50, 0x72, 0x6f, 0x67, 0x53, 0x74, 0x61, | ||
0x72, 0x74, 0x10, 0x01, 0x01, 0xfd, 0xbe, 0x65, 0x8c, | ||
} | ||
|
||
var program_end = []byte{ | ||
0x00, 0xfc, 0x30, 0x2e, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x20, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, | ||
0x00, 0x02, 0xbf, 0xd4, 0x00, 0x18, 0x02, 0x16, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x02, | ||
0x7f, 0xbf, 0x09, 0x07, 0x50, 0x72, 0x6f, 0x67, 0x45, 0x6e, 0x64, 0x11, 0x01, 0x01, 0xfc, 0xbe, | ||
0x04, 0x2a, | ||
} | ||
|
||
var provider_ad_start = []byte{ | ||
0x00, 0xfc, 0x30, 0x3b, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x20, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, | ||
0x00, 0x02, 0xbf, 0xd4, 0x00, 0x25, 0x02, 0x23, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x02, | ||
0x7f, 0xff, 0x00, 0x00, 0x52, 0x65, 0xc0, 0x09, 0x0f, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, | ||
0x72, 0x41, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x30, 0x00, 0x00, 0xfa, 0xa9, 0xe1, 0x3f, | ||
} | ||
|
||
var distributor_po_start = []byte{ | ||
0x00, 0xfc, 0x30, 0x40, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x21, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, | ||
0x00, 0x02, 0xbf, 0xd4, 0x00, 0x2a, 0x02, 0x28, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x02, | ||
0x7f, 0xff, 0x00, 0x00, 0x52, 0x65, 0xc0, 0x09, 0x12, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, | ||
0x75, 0x74, 0x6f, 0x72, 0x50, 0x4f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x36, 0x00, 0x00, 0x00, 0x00, | ||
0xfd, 0xea, 0xaf, 0xb8, | ||
} | ||
|
||
var program_resumption = []byte{ | ||
0x00, 0xfc, 0x30, 0x3a, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x20, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, | ||
0x00, 0x02, 0xbf, 0xd4, 0x00, 0x24, 0x02, 0x22, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x02, | ||
0x7f, 0xff, 0x00, 0x09, 0xa7, 0xec, 0x80, 0x09, 0x0e, 0x50, 0x72, 0x6f, 0x67, 0x52, 0x65, 0x73, | ||
0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x14, 0x01, 0x01, 0xf9, 0x12, 0xba, 0x59, | ||
} | ||
|
||
func TestNoInRules(t *testing.T) { | ||
out, err := NewSCTE35(poOpen1) | ||
if err != nil { | ||
t.Error("NewSCTE35(poOpen1) returned err:", err.Error()) | ||
t.FailNow() | ||
} | ||
|
||
csp, err := NewSCTE35(csp) | ||
if err != nil { | ||
t.Error("NewSCTE35(csp) returned err:", err.Error()) | ||
t.FailNow() | ||
} | ||
|
||
canClose := csp.Descriptors()[0].CanClose(out.Descriptors()[0]) | ||
if canClose { | ||
t.Error("CanClose returned true") | ||
} | ||
} | ||
|
||
func TestOutNoRules(t *testing.T) { | ||
out, err := NewSCTE35(poOpen1) | ||
if err != nil { | ||
t.Error("NewSCTE35(poOpen1) returned err:", err.Error()) | ||
t.FailNow() | ||
} | ||
|
||
csp, err := NewSCTE35(csp) | ||
if err != nil { | ||
t.Error("NewSCTE35(csp) returned err:", err.Error()) | ||
t.FailNow() | ||
} | ||
|
||
canClose := out.Descriptors()[0].CanClose(csp.Descriptors()[0]) | ||
if canClose { | ||
t.Error("CanClose returned true") | ||
} | ||
} | ||
|
||
func TestCloseUnconditional(t *testing.T) { | ||
// Create a 0x40 (unscheduled event start) | ||
unschedEvent, err := NewSCTE35(unscheduled_event_start) | ||
if err != nil { | ||
t.Error("NewSCTE35(unscheduled_event_start) returned err:", err.Error()) | ||
t.FailNow() | ||
} | ||
|
||
// Create a 0x51 (network end) | ||
networkEnd, err := NewSCTE35(network_end) | ||
if err != nil { | ||
t.Error("NewSCTE35(network_end) returned err:", err.Error()) | ||
t.FailNow() | ||
} | ||
|
||
canClose := networkEnd.Descriptors()[0].CanClose(unschedEvent.Descriptors()[0]) | ||
if !canClose { | ||
t.Errorf("CanClose returned false.") | ||
} | ||
} | ||
|
||
func TestCloseEventId(t *testing.T) { | ||
// Create a 0x10 (program start) | ||
programStart, err := NewSCTE35(program_start) | ||
if err != nil { | ||
t.Error("NewSCTE35(program_start) returned err:", err.Error()) | ||
t.FailNow() | ||
} | ||
|
||
// Create a 0x11 (program end) | ||
programEnd, err := NewSCTE35(program_end) | ||
if err != nil { | ||
t.Error("NewSCTE35(program_end) returned err:", err.Error()) | ||
t.FailNow() | ||
} | ||
|
||
canClose := programEnd.Descriptors()[0].CanClose(programStart.Descriptors()[0]) | ||
if !canClose { | ||
t.Errorf("CanClose returned false.") | ||
} | ||
|
||
progEndSegDesc := programEnd.Descriptors()[0].(*segmentationDescriptor) | ||
progEndSegDesc.eventID = 4 | ||
|
||
canClose = programEnd.Descriptors()[0].CanClose(programStart.Descriptors()[0]) | ||
if canClose { | ||
t.Errorf("CanClose returned true.") | ||
} | ||
} | ||
|
||
func TestCloseDifferentPTS(t *testing.T) { | ||
// Create a 0x30 (provider ad start) | ||
adStart, err := NewSCTE35(provider_ad_start) | ||
if err != nil { | ||
t.Error("NewSCTE35(provider_ad_start) returned err:", err.Error()) | ||
t.FailNow() | ||
} | ||
|
||
// Create a 0x36 (distributor PO start) | ||
poStart, err := NewSCTE35(distributor_po_start) | ||
if err != nil { | ||
t.Error("NewSCTE35(distributor_po_start) returned err:", err.Error()) | ||
t.FailNow() | ||
} | ||
|
||
canClose := poStart.Descriptors()[0].CanClose(adStart.Descriptors()[0]) | ||
if !canClose { | ||
t.Errorf("CanClose returned false.") | ||
} | ||
|
||
poStartSignal := poStart.(*scte35) | ||
poStartSignal.pts = 367860 | ||
|
||
canClose = poStart.Descriptors()[0].CanClose(adStart.Descriptors()[0]) | ||
if canClose { | ||
t.Errorf("CanClose returned trues.") | ||
} | ||
} | ||
|
||
func TestCloseBreakaway(t *testing.T) { | ||
// Create a 0x10 (program start) | ||
programStart, err := NewSCTE35(program_start) | ||
if err != nil { | ||
t.Error("NewSCTE35(program_start) returned err:", err.Error()) | ||
t.FailNow() | ||
} | ||
|
||
// Create a 0x14 (program resumption) | ||
programResumption, err := NewSCTE35(program_resumption) | ||
if err != nil { | ||
t.Error("NewSCTE35(program_resumption) returned err:", err.Error()) | ||
t.FailNow() | ||
} | ||
|
||
canClose := programResumption.Descriptors()[0].CanClose(programStart.Descriptors()[0]) | ||
if !canClose { | ||
t.Errorf("CanClose returned false") | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this fallthrough still needed? From cog esam 2.0 doc, it seems like the logic has diverged enough that only the first if block is needed and all the old code can be deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
if d.EventId()...
statement is extraneous. As is thefallthrough
.