diff --git a/container/mts/mpegts_test.go b/container/mts/mpegts_test.go index 1e735095..ed4d8534 100644 --- a/container/mts/mpegts_test.go +++ b/container/mts/mpegts_test.go @@ -362,7 +362,7 @@ func TestSegmentForMeta(t *testing.T) { metaVals: [nPSI]string{"1", "2", val, val, val, "", "3", val, val, "4"}, expectIdxs: []rng{ scale(2, 5), - scale(2, 5), + scale(7, 9), }, }, { diff --git a/container/mts/payload.go b/container/mts/payload.go index b7ccba4e..64411228 100644 --- a/container/mts/payload.go +++ b/container/mts/payload.go @@ -226,3 +226,56 @@ func (c *Clip) TrimToMetaRange(key, from, to string) (*Clip, error) { } return nil, errMetaLowerBound } + +// SegmentForMeta segments sequences of frames within c possesing meta described +// by key and val and are appended to a []Clip which is subsequently returned. +func (c *Clip) SegmentForMeta(key, val string) []Clip { + var ( + segmenting bool // If true we are currently in a segment corresponsing to given meta. + res []Clip // The resultant [][]Clip holding the segments. + start int // The start index of the current segment. + ) + + // Go through frames of clip. + for i, frame := range c.frames { + // If there is no meta (meta = nil) and we are segmenting, then append the + // current segment to res. + if frame.Meta == nil { + if segmenting { + res = appendSegment(res, c, start, i) + segmenting = false + } + continue + } + + // If we've got the meta of interest in current frame and we're not + // segmenting, set this i as start and set segmenting true. If we don't + // have the meta of interest and we are segmenting then we + // want to stop and append the segment to res. + if frame.Meta[key] == val && !segmenting { + start = i + segmenting = true + } else if frame.Meta[key] != val && segmenting { + res = appendSegment(res, c, start, i) + segmenting = false + } + } + + // We've reached the end of the entire clip so if we're segmenting we need + // to append current segment to res. + if segmenting { + res = appendSegment(res, c, start, len(c.frames)) + } + + return res +} + +// appendSegment is a helper function used by Clip.SegmentForMeta to append a +// clip to a []Clip. +func appendSegment(segs []Clip, c *Clip, start, end int) []Clip { + return append(segs, Clip{ + frames: c.frames[start:end], + backing: c.backing[c.frames[start].idx : c.frames[end-1].idx+len(c.frames[end-1].Media)], + }, + ) +} diff --git a/container/mts/payload_test.go b/container/mts/payload_test.go index 4ca12f42..881f5890 100644 --- a/container/mts/payload_test.go +++ b/container/mts/payload_test.go @@ -399,3 +399,77 @@ func TestClipTrimToMetaRange(t *testing.T) { } } } + +// TestClipSegmentForMeta checks that Clip.SegmentForMeta correctly returns +// segments from a clip with consistent meta defined by a key and value. +func TestClipSegmentForMeta(t *testing.T) { + const ( + nFrames = 10 // The number of test frames we want to create. + fSize = 3 // The size of the frame media. + key = "n" // Meta key we will use. + val = "*" // The meta val of interest. + ) + + tests := []struct { + metaVals []string // These will be the meta vals each frame has. + fIndices []rng // These are the indices of the segments of interest. + }{ + { + metaVals: []string{"1", "2", "*", "*", "*", "3", "*", "*", "4", "5"}, + fIndices: []rng{{2, 5}, {6, 8}}, + }, + { + metaVals: []string{"1", "2", "*", "*", "*", "", "*", "*", "4", "5"}, + fIndices: []rng{{2, 5}, {6, 8}}, + }, + { + metaVals: []string{"1", "2", "*", "*", "*", "3", "4", "5", "*", "*"}, + fIndices: []rng{{2, 5}, {8, nFrames}}, + }, + { + metaVals: []string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}, + fIndices: nil, + }, + } + + // Run the tests. + for testn, test := range tests { + clip := &Clip{} + + // Generate test frames. + for i := 0; i < nFrames; i++ { + clip.backing = append(clip.backing, []byte{byte(i), byte(i), byte(i)}...) + clip.frames = append( + clip.frames, + Frame{ + Media: clip.backing[i*fSize : (i+1)*fSize], + idx: i * fSize, + Meta: map[string]string{ + key: test.metaVals[i], + }, + }, + ) + } + + // Use function we're testing to get segments. + got := clip.SegmentForMeta(key, val) + + // Now get expected segments using indices defined in the test. + var want []Clip + for _, indices := range test.fIndices { + // Calculate the indices for the backing array from the original clip. + backStart := clip.frames[indices.start].idx + backEnd := clip.frames[indices.end-1].idx + len(clip.frames[indices.end-1].Media) + + // Use calculated indices to create Clip for current expected segment. + want = append(want, Clip{ + frames: clip.frames[indices.start:indices.end], + backing: clip.backing[backStart:backEnd], + }) + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("did not get expected result for test %v\nGot: %v\nWant: %v\n", testn, got, want) + } + } +}