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  }