diff --git a/stream/mts/psi/op.go b/stream/mts/psi/op.go index 5abb96a4..a2ce053a 100644 --- a/stream/mts/psi/op.go +++ b/stream/mts/psi/op.go @@ -32,14 +32,14 @@ import ( "errors" ) -func TimeToBytes(time uint64) []byte { - s := make([]byte, 8) - binary.BigEndian.PutUint64(s, time) +func TimeBytes(time uint64) (s []byte) { + s = make([]byte, timeSize) + binary.BigEndian.PutUint64(s[:], time) return s } func chkTime(d []byte) error { - if d[descTagIndx] != timeDescTag { + if d[timeTagIndx] != timeDescTag { return errors.New("PSI does not contain a time descriptor, cannot update") } return nil @@ -51,7 +51,7 @@ func UpdateTime(d []byte, t int) error { if err != nil { return err } - ts := TimeToBytes(uint64(t)) + ts := TimeBytes(uint64(t)) for i := range d[timeIndx : timeIndx+8] { d[i+timeIndx] = ts[i] } @@ -68,3 +68,9 @@ func TimeOf(d []byte) (t uint64, err error) { } return t, nil } + +func GpsStrBytes(l string) (out []byte) { + out = make([]byte, gpsSize) + copy(out, []byte(l)) + return +} diff --git a/stream/mts/psi/psi.go b/stream/mts/psi/psi.go index 294b4d21..52f1dcd1 100644 --- a/stream/mts/psi/psi.go +++ b/stream/mts/psi/psi.go @@ -50,8 +50,17 @@ const ( // Consts relating to time description const ( timeDescTag = 234 - descTagIndx = 13 + timeTagIndx = 13 timeIndx = 15 + timeSize = 8 +) + +// Consts relating to gps description +const ( + gpsDescTag = 235 + gpsTagIndx = 23 + gpsIndx = 25 + gpsSize = 32 // bytes ) // Program specific information diff --git a/stream/mts/psi/psi_test.go b/stream/mts/psi/psi_test.go index 10eb773f..b0edd7ee 100644 --- a/stream/mts/psi/psi_test.go +++ b/stream/mts/psi/psi_test.go @@ -31,212 +31,224 @@ import ( "testing" ) +// Times as ints for testing const ( - tstTime = 1235367435 // 0x49A2360B + tstTime1 = 1235367435 // 0x49A2360B tstTime2 = 1735357535 // 0x676F745F ) +// GPS string for testing +const ( + gpsTstStr1 = "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47" + gpsTstStr2 = "$GPGGA,183710,4902.048,N,02171.020,E,1,09,0.5,547.2,M,43.4,M,,*42" +) + +// err message const ( errCmp = "Incorrect output, for: %v wanted: %v, got: %v" ) -// Frist write tests - +// Test time to bytes test Data var ( timeSlice = []byte{ 0x00, 0x00, 0x00, 0x00, 0x49, 0xA2, 0x36, 0x0B, } - patPsi1 = PSI{ - Pf: 0x00, - Tid: 0x00, - Ssi: true, - Pb: false, - Sl: uint16(0x0d), - Tss: &TSS{ - Tide: uint16(0x01), - V: 0, - Cni: true, - Sn: 0, - Lsn: 0, - Sd: &PAT{ - Pn: uint16(0x01), - Pmpid: uint16(0x1000), - }, - }, +) + +// Parts to construct bytes of pmt with time and bytes +var ( + pmtTimeGpsBytesPart1 = []byte{ + 0x00, 0x02, 0xb0, 0x12, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, + byte(timeDescTag), // Descriptor tag for timestamp + byte(timeSize), // Length of bytes to follow + 0x00, 0x00, 0x00, 0x00, 0x67, 0x6F, 0x74, 0x5F, // Timestamp data + byte(gpsDescTag), // Descriptor tag for gps + byte(gpsSize), // Length of bytes to follow } - pmtPsi1 = PSI{ - Pf: 0x00, - Tid: 0x02, - Ssi: true, - Sl: uint16(0x12), - Tss: &TSS{ - Tide: uint16(0x01), - V: 0, - Cni: true, - Sn: 0, - Lsn: 0, - Sd: &PMT{ - Pcrpid: 0x0100, // wrong - Pil: 0, - Essd: &ESSD{ - St: 0x1b, - Epid: 0x0100, - Esil: 0x00, - }, - }, - }, - } - // pmt with descriptor in it - StdPmtTime = []byte{ - 0x00, // pointer - - // ---- section included in data sent to CRC32 during check - // table header - 0x02, // table id - 0xb0, // section syntax indicator:1|private bit:1|reserved:2|section length:2|more bytes...:2 - 0x12, // more bytes... - - // syntax section - 0x00, 0x01, // table id extension - 0xc1, // reserved bits:3|version:5|use now:1 - 0x00, // section number - 0x00, // last section number - // table data - 0xe1, 0x00, // reserved:3|PCR PID:13 - 0xf0, 0x0a, // reserved:4|unused:2|program info length:10 - // Desriptor - byte(timeDescTag), // Descriptor tag - byte(8), // Length of bytes to follow - 0x00, 0x00, 0x00, 0x00, 0x49, 0xA2, 0x36, 0x0B, // timestamp - // No program descriptors since program info length is 0. - // elementary stream info data - 0x1b, // stream type - 0xe1, 0x00, // reserved:3|elementary PID:13 - 0xf0, 0x00, // reserved:4|unused:2|ES info length:10 - // No elementary stream descriptors since ES info length is 0. - - // 0x15, 0xbd, 0x4d, 0x56, // CRC - // ---- - } - pmtPsiTime = PSI{ - Pf: 0x00, - Tid: 0x02, - Ssi: true, - Sl: uint16(0x12), - Tss: &TSS{ - Tide: uint16(0x01), - V: 0, - Cni: true, - Sn: 0, - Lsn: 0, - Sd: &PMT{ - Pcrpid: 0x0100, // wrong - Pil: 10, - Pd: []Desc{ - Desc{ - Dt: byte(timeDescTag), - Dl: byte(8), - Dd: TimeToBytes(tstTime), - }, - }, - Essd: &ESSD{ - St: 0x1b, - Epid: 0x0100, - Esil: 0x00, - }, - }, - }, - } - // pmt with descriptor in it - StdPmtTime2 = []byte{ - 0x00, // pointer - - // ---- section included in data sent to CRC32 during check - // table header - 0x02, // table id - 0xb0, // section syntax indicator:1|private bit:1|reserved:2|section length:2|more bytes...:2 - 0x12, // more bytes... - - // syntax section - 0x00, 0x01, // table id extension - 0xc1, // reserved bits:3|version:5|use now:1 - 0x00, // section number - 0x00, // last section number - // table data - 0xe1, 0x00, // reserved:3|PCR PID:13 - 0xf0, 0x0a, // reserved:4|unused:2|program info length:10 - // Desriptor - byte(timeDescTag), // Descriptor tag - byte(8), // Length of bytes to follow - 0x00, 0x00, 0x00, 0x00, 0x67, 0x6F, 0x74, 0x5F, // timestamp - // No program descriptors since program info length is 0. - // elementary stream info data - 0x1b, // stream type - 0xe1, 0x00, // reserved:3|elementary PID:13 - 0xf0, 0x00, // reserved:4|unused:2|ES info length:10 - // No elementary stream descriptors since ES info length is 0. - - // 0x15, 0xbd, 0x4d, 0x56, // CRC - // ---- + pmtTimeGpsBytesPart2 = []byte{ + 0x1b, 0xe1, 0x00, 0xf0, 0x00, } ) -// Test Bytes for a standard pat -func TestBytesPAT1(t *testing.T) { - got := patPsi1.Bytes() - // Remove crc32 - got = got[:len(got)-4] - if !bytes.Equal(StdPat, got) { - t.Errorf(errCmp, "TestBytesPAT1", StdPat, got) +var ( + // Bytes representing pmt with tstTime1 + pmtTimeBytes1 = []byte{ + 0x00, 0x02, 0xb0, 0x12, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, + byte(timeDescTag), // Descriptor tag + byte(timeSize), // Length of bytes to follow + 0x00, 0x00, 0x00, 0x00, 0x49, 0xA2, 0x36, 0x0B, // timestamp + 0x1b, 0xe1, 0x00, 0xf0, 0x00, } + + // Bytes representing pmt with tstTime 2 + pmtTimeBytes2 = []byte{ + 0x00, 0x02, 0xb0, 0x12, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, + byte(timeDescTag), // Descriptor tag + byte(timeSize), // Length of bytes to follow + 0x00, 0x00, 0x00, 0x00, 0x67, 0x6F, 0x74, 0x5F, // timestamp + 0x1b, 0xe1, 0x00, 0xf0, 0x00, + } + + // Bytes representing pmt with time1 and gps1 + pmtTimeGpsBytes1 = buildPmtTimeGpsBytes(gpsTstStr1) + + // bytes representing pmt with with time1 and gps 2 + pmtTimeGpsBytes2 = buildPmtTimeGpsBytes(gpsTstStr2) +) + +// bytesTests contains data for testing the Bytes() funcs for the PSI data struct +var bytesTests = []struct { + name string + input PSI + want []byte +}{ + // Pat test + { + name: "pat Bytes()", + input: stdPat, + want: stdPatBytes, + }, + + // Pmt test data no descriptor + { + name: "pmt to Bytes() without descriptors", + input: stdPmt, + want: stdPmtBytes, + }, + + // Pmt with time descriptor + { + name: "pmt to Bytes() with time descriptor", + input: PSI{ + Pf: 0x00, + Tid: 0x02, + Ssi: true, + Sl: uint16(0x12), + Tss: &TSS{ + Tide: uint16(0x01), + V: 0, + Cni: true, + Sn: 0, + Lsn: 0, + Sd: &PMT{ + Pcrpid: 0x0100, // wrong + Pil: 10, + Pd: []Desc{ + Desc{ + Dt: byte(timeDescTag), + Dl: byte(timeSize), + Dd: TimeBytes(tstTime1), + }, + }, + Essd: &ESSD{ + St: 0x1b, + Epid: 0x0100, + Esil: 0x00, + }, + }, + }, + }, + want: pmtTimeBytes1, + }, + + // Pmt with time and gps + { + name: "pmt Bytes() with time and gps", + input: PSI{ + Pf: 0x00, + Tid: 0x02, + Ssi: true, + Sl: uint16(0x12), + Tss: &TSS{ + Tide: uint16(0x01), + V: 0, + Cni: true, + Sn: 0, + Lsn: 0, + Sd: &PMT{ + Pcrpid: 0x0100, // wrong + Pil: 10, + Pd: []Desc{ + Desc{ + Dt: byte(timeDescTag), + Dl: byte(timeSize), + Dd: TimeBytes(tstTime2), + }, + Desc{ + Dt: byte(gpsDescTag), + Dl: byte(gpsSize), + Dd: GpsStrBytes(gpsTstStr1), + }, + }, + Essd: &ESSD{ + St: 0x1b, + Epid: 0x0100, + Esil: 0x00, + }, + }, + }, + }, + want: buildPmtTimeGpsBytes(gpsTstStr1), + }, } -// Test Bytes for a standard pmt -func TestBytesPMT1(t *testing.T) { - got := pmtPsi1.Bytes() - // Remove crc32 - got = got[:len(got)-4] - if !bytes.Equal(StdPmt, got) { - t.Errorf(errCmp, "TestBytesPMT1", StdPmt, got) +// TestBytes ensures that the Bytes() funcs are working correctly to take PSI +// structs and converting them to byte slices +func TestBytes(t *testing.T) { + for _, test := range bytesTests { + got := test.input.Bytes() + // Remove crc32s + got = got[:len(got)-4] + if !bytes.Equal(got, test.want) { + t.Errorf("unexpected error for test %v: got:%v want:%v", test.name, got, + test.want) + } } } // A quick sanity check of the int64 time to []byte func func TestTimestampToBytes(t *testing.T) { - timeBytes := TimeToBytes(tstTime) - if !bytes.Equal(timeSlice, timeBytes) { - t.Errorf(errCmp, "testTimeStampToBytes", timeSlice, timeBytes) - } -} - -// Test Bytes for a a standard PMT with a descripot containing a timestamp -func TestBytesPmt2(t *testing.T) { - got := pmtPsiTime.Bytes() - // Remove crc32 - got = got[:len(got)-4] - if !bytes.Equal(StdPmtTime, got) { - t.Errorf(errCmp, "testBytesPmt2", StdPmtTime, got) + tb := TimeBytes(tstTime1) + if !bytes.Equal(timeSlice, tb) { + t.Errorf(errCmp, "testTimeStampToBytes", timeSlice, tb) } } func TestTimeUpdate(t *testing.T) { - cpy := make([]byte, len(StdPmtTime)) - copy(cpy, StdPmtTime) + cpy := make([]byte, len(pmtTimeBytes1)) + copy(cpy, pmtTimeBytes1) err := UpdateTime(cpy, tstTime2) if err != nil { t.Errorf("Update time returned err: %v", err) } - if !bytes.Equal(StdPmtTime2, cpy) { - t.Errorf(errCmp, "TestTimeUpdate", StdPmtTime2, cpy) + if !bytes.Equal(pmtTimeBytes2, cpy) { + t.Errorf(errCmp, "TestTimeUpdate", pmtTimeBytes2, cpy) } } func TestTimeGet(t *testing.T) { - s, err := TimeOf(StdPmtTime) + s, err := TimeOf(pmtTimeBytes1) if err != nil { t.Errorf("Getting timestamp failed with err: %v", err) } - if s != tstTime { - t.Errorf(errCmp, "TestTimeGet", tstTime, s) + if s != tstTime1 { + t.Errorf(errCmp, "TestTimeGet", tstTime1, s) } } + +func TestGpsUpdate(t *testing.T) { + cpy := make([]byte, len(pmtTimeGpsBytes1)) + copy(cpy, pmtTimeGpsBytes1) + err := UpdateGps(cpy, tstTime2) + if err != nil { + t.Errorf("Update time returned err: %v", err) + } + if !bytes.Equal(pmtTimeGpsBytes2, cpy) { + t.Errorf(errCmp, "TestGpsUpdate", pmtTimeGpsBytes2, cpy) + } +} + +func buildPmtTimeGpsBytes(tstStr string) []byte { + return append(append(append(make([]byte, 0), pmtTimeGpsBytesPart1...), + GpsStrBytes(tstStr)...), pmtTimeGpsBytesPart2...) +} diff --git a/stream/mts/psi/std.go b/stream/mts/psi/std.go index 6beed1c8..f3448a6a 100644 --- a/stream/mts/psi/std.go +++ b/stream/mts/psi/std.go @@ -26,10 +26,54 @@ LICENSE package psi -// TODO: Finish off mts/psi so that we can create pat and pmt tables instead -// of hardcoding. +// Std PSI in struct form without descriptor var ( - StdPat = []byte{ + stdPat = PSI{ + Pf: 0x00, + Tid: 0x00, + Ssi: true, + Pb: false, + Sl: uint16(0x0d), + Tss: &TSS{ + Tide: uint16(0x01), + V: 0, + Cni: true, + Sn: 0, + Lsn: 0, + Sd: &PAT{ + Pn: uint16(0x01), + Pmpid: uint16(0x1000), + }, + }, + } + + stdPmt = PSI{ + Pf: 0x00, + Tid: 0x02, + Ssi: true, + Sl: uint16(0x12), + Tss: &TSS{ + Tide: uint16(0x01), + V: 0, + Cni: true, + Sn: 0, + Lsn: 0, + Sd: &PMT{ + Pcrpid: 0x0100, // wrong + Pil: 0, + Essd: &ESSD{ + St: 0x1b, + Epid: 0x0100, + Esil: 0x00, + }, + }, + }, + } +) + +// Std PSI in bytes form +var ( + stdPatBytes = []byte{ 0x00, // pointer // ---- section included in data sent to CRC32 during check @@ -50,7 +94,7 @@ var ( // 0x2a, 0xb1, 0x04, 0xb2, // CRC // ---- } - StdPmt = []byte{ + stdPmtBytes = []byte{ 0x00, // pointer // ---- section included in data sent to CRC32 during check