clone

a desktop tv and program guide for over-the-air broadcasts: what's on air, what your tuner gets, live tv, recordings

main
airwaves / internal / ts / ts_test.go
ts_test.go 4.9K
1package ts_test23import (4 "bytes"5 "testing"67 "airwaves/internal/ts"8 "airwaves/internal/ts/tstest"9)1011// denver is a multiplex like KWGN's: an HD main channel with English,12// Spanish and described audio, and two subchannels.13var denver = tstest.Mux{TSID: 467, Programs: []tstest.Program{14 {Number: 6, PMTPID: 0x30, VideoPID: 0x31, Major: 2, Minor: 1, Name: "KWGN-DT", Audio: []tstest.Audio{15 {PID: 0x34, Lang: "eng"}, {PID: 0x35, Lang: "spa"}, {PID: 0x36, Lang: "eng", Described: true},16 }},17 {Number: 7, PMTPID: 0x40, VideoPID: 0x41, VideoType: 0x1b, Major: 2, Minor: 2, Name: "Antenna", Audio: []tstest.Audio{18 {PID: 0x44, Type: 0x87, Lang: "eng"}, {PID: 0x45, Type: 0x0f, Lang: "spa", Described: true},19 }},20 {Number: 8, PMTPID: 0x50, VideoPID: 0x51, Major: 2, Minor: 3, Name: "Court", Audio: []tstest.Audio{{PID: 0x54}}},21}}2223func packets(b []byte) [][]byte {24 var out [][]byte25 for i := 0; i+ts.PacketSize <= len(b); i += ts.PacketSize {26 out = append(out, b[i:i+ts.PacketSize])27 }28 return out29}3031func TestSplitter(t *testing.T) {32 src := denver.Packets(60)33 for _, size := range []int{1, 7, 100, 187, 188, 189, 1000, len(src)} {34 var s ts.Splitter35 var got []byte36 for i := 0; i < len(src); i += size {37 s.Write(src[i:min(i+size, len(src))], func(pkt []byte) { got = append(got, pkt...) })38 }39 if !bytes.Equal(got, src) || s.Resyncs() != 0 {40 t.Errorf("chunks of %d: got %d bytes with %d resyncs, want the %d sent and none", size, len(got), s.Resyncs(), len(src))41 }42 }43}4445func TestSplitterResync(t *testing.T) {46 src := denver.Packets(20)47 // Half a packet lost, and garbage with a stray sync byte in it.48 var in []byte49 in = append(in, src[:5*ts.PacketSize]...)50 in = append(in, src[5*ts.PacketSize:5*ts.PacketSize+90]...)51 in = append(in, 0x00, 0x47, 0x12, 0x47, 0x00)52 in = append(in, src[6*ts.PacketSize:]...)53 for _, size := range []int{13, 188, len(in)} {54 var s ts.Splitter55 var got []byte56 for i := 0; i < len(in); i += size {57 s.Write(in[i:min(i+size, len(in))], func(pkt []byte) { got = append(got, pkt...) })58 }59 // Every whole packet comes out. Written all at once, the cut one is60 // seen for what it is; in pieces, it may go out before the bytes61 // after it show it was cut, taking the start of the next with it.62 want := len(src) - ts.PacketSize63 head, tail := src[:5*ts.PacketSize], src[7*ts.PacketSize:]64 ok := bytes.HasPrefix(got, head) && bytes.HasSuffix(got, tail) && len(got) >= want-ts.PacketSize && len(got) <= want65 if size == len(in) {66 ok = bytes.Equal(got, append(append([]byte{}, head...), src[6*ts.PacketSize:]...))67 }68 if !ok || s.Resyncs() != 1 {69 t.Errorf("chunks of %d: got %d packets with %d resyncs, want %d with 1", size, len(got)/ts.PacketSize, s.Resyncs(), want/ts.PacketSize)70 }71 }72}7374func TestCounter(t *testing.T) {75 dup := func(b []byte, i, times int) []byte {76 p := b[i*ts.PacketSize : (i+1)*ts.PacketSize]77 var out []byte78 out = append(out, b[:(i+1)*ts.PacketSize]...)79 for range times {80 out = append(out, p...)81 }82 return append(out, b[(i+1)*ts.PacketSize:]...)83 }84 // A packet of PID 0x100 with the given continuity counter, with or85 // without a payload, and with the discontinuity indicator set.86 pkt := func(cc byte, payload, disc bool) []byte {87 p := make([]byte, ts.PacketSize)88 p[0], p[1], p[2] = 0x47, 0x01, 0x0089 p[3] = 0x20 | cc90 if payload {91 p[3] |= 0x1092 }93 p[4] = 194 if disc {95 p[5] = 0x8096 }97 return p98 }99 null := make([]byte, ts.PacketSize)100 null[0], null[1], null[2], null[3] = 0x47, 0x1f, 0xff, 0x10101 tests := []struct {102 name string103 in func() []byte104 tei, cc int64105 }{106 {"clean", func() []byte { return denver.Packets(200) }, 0, 0},107 {"flagged", func() []byte { b := denver.Packets(200); tstest.SetTEI(b, 50); tstest.SetTEI(b, 51); return b }, 2, 0},108 {"lost", func() []byte { b := denver.Packets(200); tstest.SkipCC(b, 50); return b }, 0, 1},109 {"duplicate", func() []byte { return dup(denver.Packets(200), 50, 1) }, 0, 0},110 {"two duplicates", func() []byte { return dup(denver.Packets(200), 50, 2) }, 0, 1},111 {"discontinuity", func() []byte {112 return bytes.Join([][]byte{pkt(0, true, false), pkt(1, true, false), pkt(7, true, true), pkt(8, true, false)}, nil)113 }, 0, 0},114 {"no payload", func() []byte {115 return bytes.Join([][]byte{pkt(3, true, false), pkt(3, false, false), pkt(4, true, false)}, nil)116 }, 0, 0},117 {"null packets", func() []byte { return bytes.Join([][]byte{null, null, null}, nil) }, 0, 0},118 }119 for _, tt := range tests {120 var c ts.Counter121 in := tt.in()122 for _, p := range packets(in) {123 c.Packet(p)124 }125 if c.TEI != tt.tei || c.CC != tt.cc || c.Errors() != tt.tei+tt.cc || c.Packets != int64(len(in)/ts.PacketSize) {126 t.Errorf("%s: TEI %d, CC %d, %d packets; want %d, %d, %d", tt.name, c.TEI, c.CC, c.Packets, tt.tei, tt.cc, len(in)/ts.PacketSize)127 }128 }129}130131func TestCRC32(t *testing.T) {132 // The check value of CRC-32/MPEG-2.133 if got := ts.CRC32([]byte("123456789")); got != 0x0376e6e7 {134 t.Errorf("CRC32 = %#x, want 0x376e6e7", got)135 }136}