/* NAME psi.go DESCRIPTION See Readme.md AUTHOR Saxon Milton LICENSE psi.go is Copyright (C) 2018 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with revid in gpl.txt. If not, see http://www.gnu.org/licenses. */ // Package psi provides encoding of MPEG-TS program specific information. package psi import ( "errors" "github.com/Comcast/gots/psi" ) // PacketSize of psi (without MPEG-TS header) const PacketSize = 184 // Lengths of section definitions. const ( ESSDDefLen = 5 DescDefLen = 2 PMTDefLen = 4 PATLen = 4 TSSDefLen = 5 PSIDefLen = 3 ) // Table Type IDs. const ( patID = 0x00 pmtID = 0x02 ) // Consts relating to time description // TODO: remove this, we don't do metadata like this anymore. const ( TimeDescTag = 234 TimeTagIndx = 13 TimeDataIndx = 15 TimeDataSize = 8 // bytes, because time is stored in uint64 ) // Consts relating to location description // TODO: remove this, we don't do metadata like this anymore. const ( LocationDescTag = 235 LocationTagIndx = 23 LocationDataIndx = 25 LocationDataSize = 32 // bytes ) // crc hassh Size const crcSize = 4 // Consts relating to syntax section. const ( TotalSyntaxSecLen = 180 SyntaxSecLenIdx1 = 2 SyntaxSecLenIdx2 = 3 SyntaxSecLenMask1 = 0x03 SectionLenMask1 = 0x03 ) // Consts relating to program info len. const ( ProgramInfoLenIdx1 = 11 ProgramInfoLenIdx2 = 12 ProgramInfoLenMask1 = 0x03 ) // DescriptorsIdx is the index that the descriptors start at. const DescriptorsIdx = ProgramInfoLenIdx2 + 1 // MetadataTag is the descriptor tag used for metadata. const MetadataTag = 0x26 // NewPATPSI will provide a standard program specific information (PSI) table // with a program association table (PAT) specific data field. func NewPATPSI() *PSI { return &PSI{ Pf: 0x00, Tid: 0x00, Ssi: true, Pb: false, Sl: 0x0d, Tss: &TSS{ Tide: 0x01, V: 0, Cni: true, Sn: 0, Lsn: 0, Sd: &PAT{ Pn: 0x01, Pmpid: 0x1000, }, }, } } // NewPMTPSI will provide a standard program specific information (PSI) table // with a program mapping table specific data field. // NOTE: Media PID and stream ID are default to 0. func NewPMTPSI() *PSI { return &PSI{ Pf: 0x00, Tid: 0x02, Ssi: true, Sl: 0x12, Tss: &TSS{ Tide: 0x01, V: 0, Cni: true, Sn: 0, Lsn: 0, Sd: &PMT{ Pcrpid: 0x0100, Pil: 0, Essd: &ESSD{ St: 0, Epid: 0, Esil: 0x00, }, }, }, } } // TODO: get rid of these - not a good idea. type ( PSIBytes []byte Descriptor []byte ) // 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 } // 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 SpecificData // Specific data PAT/PMT } // Specific Data, (could be PAT or PMT) type SpecificData interface { Bytes() []byte } // Program association table, implements SpecificData type PAT struct { Pn uint16 // Program Number Pmpid uint16 // Program map PID } // Program mapping table, implements SpecificData type PMT struct { 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 } // Descriptor type Desc struct { Dt byte // Descriptor tag Dl byte // Descriptor length Dd []byte // Descriptor data } // Bytes outputs a byte slice representation of the PSI func (p *PSI) Bytes() []byte { out := make([]byte, 4) out[0] = p.Pf if p.Pf != 0 { panic("No support for pointer filler bytes") } out[1] = p.Tid out[2] = 0x80 | 0x30 | (0x03 & byte(p.Sl>>8)) out[3] = byte(p.Sl) out = append(out, p.Tss.Bytes()...) out = AddCrc(out) 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 & asByte(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) 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)) // byte 10 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()...) } out = append(out, p.Essd.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...) 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 = append(out, d.Bytes()...) } return out } func asByte(b bool) byte { if b { return 0x01 } return 0x00 } // AddDescriptor adds or updates a descriptor in a PSI given a descriptor tag // and data. If the psi is not a pmt, then an error is returned. If a descriptor // with the given tag is not found in the psi, room is made and a descriptor with // given tag and data is created. If a descriptor with the tag is found, the // descriptor is resized as required and the new data is copied in. func (p *PSIBytes) AddDescriptor(tag int, data []byte) error { if psi.TableID(*p) != pmtID { return errors.New("trying to add descriptor, but not pmt") } i, desc := p.HasDescriptor(tag) if desc == nil { err := p.createDescriptor(tag, data) return err } oldDescLen := desc.len() oldDataLen := int(desc[1]) newDataLen := len(data) newDescLen := 2 + newDataLen delta := newDescLen - oldDescLen // If the old data length is more than the new data length, we need shift data // after descriptor up, and then trim the psi. If the oldDataLen is less than // new data then we need reseize psi and shift data down. If same do nothing. switch { case oldDataLen > newDataLen: copy((*p)[i+newDescLen:], (*p)[i+oldDescLen:]) *p = (*p)[:len(*p)+delta] case oldDataLen < newDataLen: tmp := make([]byte, len(*p)+delta) copy(tmp, *p) *p = tmp copy((*p)[i+newDescLen:], (*p)[i+oldDescLen:]) } // Copy in new data (*p)[i+1] = byte(newDataLen) copy((*p)[i+2:], data) newProgInfoLen := p.ProgramInfoLen() + delta p.setProgInfoLen(newProgInfoLen) newSectionLen := int(psi.SectionLength(*p)) + delta p.setSectionLen(newSectionLen) UpdateCrc((*p)[1:]) return nil } // HasDescriptor checks if a descriptor of the given tag exists in a PSI. If the descriptor // of the given tag exists, an index of this descriptor, as well as the Descriptor is returned. // If the descriptor of the given tag cannot be found, -1 and a nil slice is returned. // // TODO: check if pmt, return error if not ? func (p *PSIBytes) HasDescriptor(tag int) (int, Descriptor) { descs := p.descriptors() if descs == nil { return -1, nil } for i := 0; i < len(descs); i += 2 + int(descs[i+1]) { if int(descs[i]) == tag { return i + DescriptorsIdx, descs[i : i+2+int(descs[i+1])] } } return -1, nil } // createDescriptor creates a descriptor in a psi given a tag and data. It does so // by resizing the psi, shifting existing data down and copying in new descriptor // in new space. func (p *PSIBytes) createDescriptor(tag int, data []byte) error { curProgLen := p.ProgramInfoLen() oldSyntaxSectionLen := SyntaxSecLenFrom(*p) if TotalSyntaxSecLen-(oldSyntaxSectionLen+2+len(data)) <= 0 { return errors.New("Not enough space in psi to create descriptor.") } dataLen := len(data) newDescIdx := DescriptorsIdx + curProgLen newDescLen := dataLen + 2 // Increase size of psi and copy data down to make room for new descriptor. tmp := make([]byte, len(*p)+newDescLen) copy(tmp, *p) *p = tmp copy((*p)[newDescIdx+newDescLen:], (*p)[newDescIdx:newDescIdx+newDescLen]) // Set the tag, data len and data of the new desriptor. (*p)[newDescIdx] = byte(tag) (*p)[newDescIdx+1] = byte(dataLen) copy((*p)[newDescIdx+2:newDescIdx+2+dataLen], data) // Set length fields and update the psi crc. addedLen := dataLen + 2 newProgInfoLen := curProgLen + addedLen p.setProgInfoLen(newProgInfoLen) newSyntaxSectionLen := int(oldSyntaxSectionLen) + addedLen p.setSectionLen(newSyntaxSectionLen) UpdateCrc((*p)[1:]) return nil } // setProgInfoLen sets the program information length in a psi with a pmt. func (p *PSIBytes) setProgInfoLen(l int) { (*p)[ProgramInfoLenIdx1] &= 0xff ^ ProgramInfoLenMask1 (*p)[ProgramInfoLenIdx1] |= byte(l>>8) & ProgramInfoLenMask1 (*p)[ProgramInfoLenIdx2] = byte(l) } // setSectionLen sets section length in a psi. func (p *PSIBytes) setSectionLen(l int) { (*p)[SyntaxSecLenIdx1] &= 0xff ^ SyntaxSecLenMask1 (*p)[SyntaxSecLenIdx1] |= byte(l>>8) & SyntaxSecLenMask1 (*p)[SyntaxSecLenIdx2] = byte(l) } // descriptors returns the descriptors in a psi if they exist, otherwise // a nil slice is returned. func (p *PSIBytes) descriptors() []byte { return (*p)[DescriptorsIdx : DescriptorsIdx+p.ProgramInfoLen()] } // len returns the length of a descriptor in bytes. func (d *Descriptor) len() int { return int(2 + (*d)[1]) } // ProgramInfoLen returns the program info length of a PSI. // // TODO: check if pmt - if not return 0 ? or -1 ? func (p *PSIBytes) ProgramInfoLen() int { return int((((*p)[ProgramInfoLenIdx1] & ProgramInfoLenMask1) << 8) | (*p)[ProgramInfoLenIdx2]) }