av/stream/mts/psi/psi.go

305 lines
6.9 KiB
Go

/*
NAME
psi.go
DESCRIPTION
See Readme.md
AUTHOR
Saxon Milton <saxon@ausocean.org>
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
import (
"errors"
"github.com/Comcast/gots/psi"
)
const (
PacketSize = 184 // packet size of a psi.
)
// 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
const (
TimeDescTag = 234
TimeTagIndx = 13
TimeDataIndx = 15
TimeDataSize = 8 // bytes, because time is stored in uint64
)
// Consts relating to location description
const (
LocationDescTag = 235
LocationTagIndx = 23
LocationDataIndx = 25
LocationDataSize = 32 // bytes
)
// Other misc consts
const (
syntaxSecLenIndx = 3
crcSize = 4
)
const (
SectionLenIdx1 = 2
SectionLenIdx2 = 3
)
const (
SectionLenMask1 = 0x03
)
const (
ProgramInfoLenIdx1 = 11
ProgramInfoLenIdx2 = 12
ProgramInfoLenMask1 = 0x03
)
const (
DescriptorsIdx = ProgramInfoLenIdx2 + 1
)
const MetadataTag = 0x26
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)
out = addPadding(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
}
func (p *PSIBytes) AddDescriptor(tag int, data []byte) error {
if psi.TableID(*p) != pmtID {
return errors.New("trying to add descriptor, but not pmt")
}
desc := p.HasDescriptor(tag)
if desc == nil {
p.createDescriptor(tag, data)
return nil
}
desc.update(data)
return nil
}
func (p *PSIBytes) ProgramInfoLen() int {
return int((((*p)[ProgramInfoLenIdx1] & ProgramInfoLenMask1) << 8) | (*p)[ProgramInfoLenIdx2])
}
func (p *PSIBytes) HasDescriptor(tag int) Descriptor {
descs := p.descriptors()
if descs == nil {
return nil
}
for i := 0; i < len(descs); i += 2 + int(descs[i+1]) {
if int(descs[i]) == tag {
return descs[i : i+2+int(descs[i+1])]
}
}
return nil
}
func (p *PSIBytes) descriptors() []byte {
return (*p)[DescriptorsIdx : DescriptorsIdx+p.ProgramInfoLen()]
}
func (p *PSIBytes) createDescriptor(tag int, data []byte) {
curProgLen := p.ProgramInfoLen()
dataLen := len(data)
// Calculate the new descriptors index and length.
newDescIdx := DescriptorsIdx + curProgLen
newDescLen := dataLen + 2
// Copy data down from newDescIdx to create room for the new descriptor.
copy((*p)[newDescIdx+newDescLen:], (*p)[newDescIdx:dataLen-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)
// Update the program info length to account for the new descriptor.
newProgInfoLen := curProgLen + dataLen
(*p)[ProgramInfoLenIdx1] = byte(newProgInfoLen >> 8)
(*p)[ProgramInfoLenIdx2] = byte(newProgInfoLen)
}
func (d *Descriptor) update(data []byte) {
if len(data) > int((*d)[1]) {
// TODO: implement resizing of descriptor
panic("Can't resize descriptor data")
}
}