github.com/puellanivis/breton@v0.2.16/lib/mpeg/ts/psi/pmt.go (about) 1 package psi 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/pkg/errors" 8 desc "github.com/puellanivis/breton/lib/mpeg/ts/descriptor" 9 ) 10 11 // PMT defines an MPEG-TS Program Map Table. 12 type PMT struct { 13 Syntax *SectionSyntax 14 15 PCRPID uint16 16 Descriptors []desc.Descriptor 17 Streams []*StreamData 18 19 crc uint32 20 } 21 22 func (pmt *PMT) String() string { 23 out := []string{ 24 "PMT", 25 } 26 27 if pmt.Syntax != nil { 28 out = append(out, fmt.Sprint(pmt.Syntax)) 29 } 30 31 if pmt.PCRPID != 0x1fff { 32 out = append(out, fmt.Sprintf("PCRPID:x%04X", pmt.PCRPID)) 33 } 34 35 for _, d := range pmt.Descriptors { 36 out = append(out, fmt.Sprintf("Desc:%v", d)) 37 } 38 39 for _, esd := range pmt.Streams { 40 out = append(out, fmt.Sprintf("Stream:%v", esd)) 41 } 42 43 return fmt.Sprintf("{%s}", strings.Join(out, " ")) 44 } 45 46 const ( 47 tableidPMT = 0x02 48 ) 49 50 func init() { 51 Register(tableidPMT, func() PSI { return new(PMT) }) 52 } 53 54 // TableID implements mpeg/ts/psi.PSI. 55 func (pmt *PMT) TableID() uint8 { 56 return tableidPMT 57 } 58 59 // SectionSyntax returns the embedded SectionSyntax, and implements mpeg/ts/psi.PSI. 60 func (pmt *PMT) SectionSyntax() *SectionSyntax { 61 return pmt.Syntax 62 } 63 64 // Unmarshal decodes a byte slice into the PMT. 65 func (pmt *PMT) Unmarshal(b []byte) error { 66 if b[0] != tableidPMT { 67 return errors.Errorf("table_id mismatch: x%02X != x%02X", b[0], tableidPMT) 68 } 69 70 syn, data, crc, err := CommonUnmarshal(b) 71 if err != nil { 72 return err 73 } 74 75 pmt.Syntax = syn 76 pmt.crc = crc 77 78 pmt.PCRPID = uint16(data[0]&0x1F)<<8 | uint16(data[1]) 79 pinfoLen := int(data[2]&0x03)<<8 | int(data[3]) 80 81 start := 4 82 end := pinfoLen + start 83 84 for start < end { 85 d, err := desc.Unmarshal(data[start:]) 86 if err != nil { 87 return err 88 } 89 90 pmt.Descriptors = append(pmt.Descriptors, d) 91 92 start += d.Len() 93 } 94 95 for start < len(data) { 96 b := data[start:] 97 98 esd := new(StreamData) 99 l, err := esd.unmarshal(b) 100 if err != nil { 101 return err 102 } 103 104 pmt.Streams = append(pmt.Streams, esd) 105 106 start += l 107 } 108 109 return nil 110 } 111 112 // Marshal encodes the PMT into a byte slice. 113 func (pmt *PMT) Marshal() ([]byte, error) { 114 data := make([]byte, 4) 115 116 data[0] = byte((pmt.PCRPID >> 8) & 0x1F) 117 data[1] = byte(pmt.PCRPID & 0xFF) 118 119 l := len(pmt.Descriptors) 120 if l > 0x3FF { 121 return nil, errors.Errorf("too many descriptors: %d > 0x3FF ", l) 122 } 123 124 data[2] = byte((l >> 8) & 0x03) 125 data[3] = byte(l & 0xFF) 126 127 for _, d := range pmt.Descriptors { 128 b, err := d.Marshal() 129 if err != nil { 130 return nil, err 131 } 132 133 data = append(data, b...) 134 } 135 136 for _, s := range pmt.Streams { 137 b, err := s.marshal() 138 if err != nil { 139 return nil, err 140 } 141 142 data = append(data, b...) 143 } 144 145 return CommonMarshal(tableidPMT, false, pmt.Syntax, data) 146 }