diff --git a/stream/mts/psi/psi.go b/stream/mts/psi/psi.go index 608aedec..3fb57c86 100644 --- a/stream/mts/psi/psi.go +++ b/stream/mts/psi/psi.go @@ -5,6 +5,7 @@ import ( "math/bits" ) +// Some common lengths const ( ESSDDefLen = 5 DescDefLen = 2 @@ -14,26 +15,32 @@ const ( PSIDefLen = 3 ) +// Table Type IDs +const ( + PATTableID = 0x00 + PMTTableID = 0x02 +) + // Program specific information type PSI struct { - pf byte // Point field - pfb []byte // Pointer filler bytes - tid byte // Table ID - ssi bool // Section syntax indicator (1 for PAT, PMT, CAT) - pb bool // Private bit (0 for PAT, PMT, CAT) - sl uint16 // Section length - tss *TSS // Table syntax section (length defined by SL) if length 0 then nil - crc uint32 // crc32 of entire table excluding pointer field, pointer filler bytes and the trailing CRC32 + Pf byte // Point field + Pfb []byte // Pointer filler bytes + Tid byte // Table ID + Ssi bool // Section syntax indicator (1 for PAT, PMT, CAT) + Pb bool // Private bit (0 for PAT, PMT, CAT) + Sl uint16 // Section length + Tss *TSS // Table syntax section (length defined by SL) if length 0 then nil + Crc uint32 // crc32 of entire table excluding pointer field, pointer filler bytes and the trailing CRC32 } // Table syntax section type TSS struct { - tide uint16 // Table ID extension - v byte // Version number - cni bool // Current/next indicator - sn byte // Section number - lsn byte // Last section number - sd SD // Specific data PAT/PMT + Tide uint16 // Table ID extension + V byte // Version number + Cni bool // Current/next indicator + Sn byte // Section number + Lsn byte // Last section number + Sd SD // Specific data PAT/PMT } // Specific Data, (could be PAT or PMT) @@ -43,49 +50,92 @@ type SD interface { // Program association table, implements SD type PAT struct { - pn uint16 // Program Number - pmpid uint16 // Program map PID + Pn uint16 // Program Number + Pmpid uint16 // Program map PID } // Program mapping table, implements SD type PMT struct { - pcrpid uint16 // Program clock reference pid - pil uint16 // Program info length - pd []Desc // Program descriptors - essd []ESSD // Elementary stream specific data + Pcrpid uint16 // Program clock reference pid + Pil uint16 // Program info length + Pd []Desc // Program descriptors + Essd []ESSD // Elementary stream specific data } // Elementary stream specific data type ESSD struct { - st byte // Stream type - epid uint16 // Elementary pid - esil uint16 // Elementary stream - esd []Desc // Elementary stream desriptors + St byte // Stream type + Epid uint16 // Elementary pid + Esil uint16 // Elementary stream + Esd []Desc // Elementary stream desriptors } // Descriptor type Desc struct { - dt byte // Descriptor tag - dl byte // Descriptor length - dd []byte // Descriptor data + Dt byte // Descriptor tag + Dl byte // Descriptor length + Dd []byte // Descriptor data } -// TODO: Implement this +// ReadPSI creates a PSI data structure from a given byte slice that represents a PSI func ReadPSI(data []byte) *PSI { + psi := PSI{} + pos := 0 + psi.Pf = data[pos] + if psi.Pf != 0 { + psi.Pfb = make([]byte, 0, psi.Pf) + pos++ + for i := 0; i < int(psi.Pf); i++ { + psi.Pfb = append(psi.Pfb, data[pos]) + pos++ + } + } + psi.Tid = data[pos] + pos++ + psi.Ssi = byteToBool(data[pos] & 0x80) + psi.Pb = byteToBool(data[pos] & 0x40) + psi.Sl = uint16(data[pos]&0x03)<<8 | uint16(data[pos+1]) + pos += 2 + psi.Tss = readTSS(data[pos:], &psi) + return &psi +} + +// ReadTSS creates a TSS data structure from a given byte slice that represents a TSS +func readTSS(data []byte, p *PSI) *TSS { + tss := TSS{} + pos := 0 + tss.Tide = uint16(data[pos])<<8 | uint16(data[pos+1]) + pos += 2 + tss.V = (data[pos] & 0x3e) >> 1 + tss.Cni = byteToBool(data[pos] & 0x01) + pos++ + tss.Sn = data[pos] + pos++ + tss.Lsn = data[pos] + pos++ + if p.Tid == PATTableID { + tss.Sd = readPAT(data, &tss) + } else if p.Tid == PMTTableID { + tss.Sd = readPMT(data, &tss) + } else { + panic("Can't yet deal with tables that are not PAT or PMT") + } + return nil } +// Bytes outputs a byte slice representation of the PSI func (p *PSI) Bytes() []byte { - l := 1 + len(p.pfb) + l := 1 + len(p.Pfb) out := make([]byte, l+PSIDefLen) - out[0] = p.pf - for i, b := range p.pfb { + out[0] = p.Pf + for i, b := range p.Pfb { out[1+i] = b } - out[l] = p.tid - out[l+1] = 0x80 | 0x40 | 0x30 | (0x03 & byte(p.sl>>8)) - out[l+2] = byte(p.sl) - out = append(out, p.tss.Bytes()...) + out[l] = p.Tid + out[l+1] = 0x80 | 0x40 | 0x30 | (0x03 & byte(p.Sl>>8)) + out[l+2] = byte(p.Sl) + out = append(out, p.Tss.Bytes()...) crc32 := crc32_Update(0xffffffff, crc32_MakeTable(bits.Reverse32(crc32.IEEE)), out[l:]) out = append(out, make([]byte, 0, 4)...) out = append(out, byte(crc32>>24)) @@ -95,57 +145,62 @@ func (p *PSI) Bytes() []byte { return out } +// Bytes outputs a byte slice representation of the TSS func (t *TSS) Bytes() []byte { out := make([]byte, TSSDefLen) - out[0] = byte(t.tide >> 8) - out[1] = byte(t.tide) - out[2] = 0xc0 | (0x3e & (t.v << 1)) | (0x01 & boolToByte(t.cni)) - out[3] = t.sn - out[4] = t.lsn - out = append(out, t.sd.Bytes()...) + out[0] = byte(t.Tide >> 8) + out[1] = byte(t.Tide) + out[2] = 0xc0 | (0x3e & (t.V << 1)) | (0x01 & boolToByte(t.Cni)) + out[3] = t.Sn + out[4] = t.Lsn + out = append(out, t.Sd.Bytes()...) return out } +// Bytes outputs a byte slice representation of the PAT func (p *PAT) Bytes() []byte { out := make([]byte, PATLen) - out[0] = byte(p.pn >> 8) - out[1] = byte(p.pn) - out[2] = 0xe0 | (0x1f & byte(p.pmpid>>8)) - out[3] = byte(p.pmpid) + out[0] = byte(p.Pn >> 8) + out[1] = byte(p.Pn) + out[2] = 0xe0 | (0x1f & byte(p.Pmpid>>8)) + out[3] = byte(p.Pmpid) return out } +// Bytes outputs a byte slice representation of the PMT func (p *PMT) Bytes() []byte { out := make([]byte, PMTDefLen) - out[0] = 0xe0 | (0x1f & byte(p.pcrpid>>8)) - out[1] = byte(p.pcrpid) - out[2] = 0xf0 | (0x03 & byte(p.pil>>8)) - out[3] = byte(p.pil) - for _, d := range p.pd { + out[0] = 0xe0 | (0x1f & byte(p.Pcrpid>>8)) + out[1] = byte(p.Pcrpid) + out[2] = 0xf0 | (0x03 & byte(p.Pil>>8)) + out[3] = byte(p.Pil) + for _, d := range p.Pd { out = append(out, d.Bytes()...) } - for _, e := range p.essd { + for _, e := range p.Essd { out = append(out, e.Bytes()...) } return out } +// Bytes outputs a byte slice representation of the Desc func (d *Desc) Bytes() []byte { out := make([]byte, DescDefLen) - out[0] = d.dt - out[1] = d.dl - out = append(out, d.dd...) + out[0] = d.Dt + out[1] = d.Dl + out = append(out, d.Dd...) return out } +// Bytes outputs a byte slice representation of the ESSD func (e *ESSD) Bytes() []byte { out := make([]byte, ESSDDefLen) - out[0] = e.st - out[1] = 0xe0 | (0x1f & byte(e.epid>>8)) - out[2] = byte(e.epid) - out[3] = 0xf0 | (0x03 & byte(e.esil>>8)) - out[4] = byte(e.esil) - for _, d := range e.esd { + out[0] = e.St + out[1] = 0xe0 | (0x1f & byte(e.Epid>>8)) + out[2] = byte(e.Epid) + out[3] = 0xf0 | (0x03 & byte(e.Esil>>8)) + out[4] = byte(e.Esil) + for _, d := range e.Esd { out = append(out, d.Bytes()...) } return out @@ -158,6 +213,13 @@ func boolToByte(b bool) byte { return 0x00 } +func byteToBool(b byte) bool { + if b == 0 { + return false + } + return true +} + func crc32_MakeTable(poly uint32) *crc32.Table { var t crc32.Table for i := range t {