github.com/puellanivis/breton@v0.2.16/lib/mpeg/ts/pes/pes.go (about)

     1  package pes
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/pkg/errors"
     8  )
     9  
    10  const (
    11  	idPaddingStream  = 0xBE
    12  	idPrivateStream2 = 0xBF
    13  
    14  	mandatoryHeaderLength = 6
    15  )
    16  
    17  // Stream is a structure defining properties of a Primitive Elementary Stream.
    18  type Stream struct {
    19  	ID byte // Stream ID
    20  
    21  	Header // Optional PES Header fields.
    22  }
    23  
    24  // HeaderLength returns the length in bytes of the Header for this Stream.
    25  func (s *Stream) HeaderLength() (int, error) {
    26  	l := mandatoryHeaderLength
    27  
    28  	switch s.ID {
    29  	case idPaddingStream, idPrivateStream2:
    30  		// Optional PES Header not present for these streams.
    31  	default:
    32  		h, err := s.marshalHeader()
    33  		if err != nil {
    34  			return 0, err
    35  		}
    36  
    37  		l += len(h)
    38  	}
    39  
    40  	return l, nil
    41  }
    42  
    43  func (s *Stream) String() string {
    44  	out := []string{
    45  		fmt.Sprintf("ID:x%02X", s.ID),
    46  	}
    47  
    48  	if s.ID != idPaddingStream && s.ID != idPrivateStream2 {
    49  		if s.ScrambleControl != 0 {
    50  			out = append(out, fmt.Sprintf("Scramble:x%X", s.ScrambleControl))
    51  		}
    52  
    53  		if s.Priority {
    54  			out = append(out, "PRI")
    55  		}
    56  
    57  		if s.DataAlignment {
    58  			out = append(out, "ALIGN")
    59  		}
    60  
    61  		if s.Copyright {
    62  			out = append(out, "COPYRIGHT")
    63  		}
    64  
    65  		if s.IsOriginal {
    66  			out = append(out, "ORIG")
    67  		}
    68  
    69  		if s.PTS != nil {
    70  			out = append(out, fmt.Sprintf("PTS:x%09X", *s.PTS))
    71  		}
    72  
    73  		if s.DTS != nil {
    74  			out = append(out, fmt.Sprintf("DTS:x%09X", *s.DTS))
    75  		}
    76  
    77  		if s.extFlags != 0 {
    78  			out = append(out, fmt.Sprintf("flags:%02X", s.extFlags))
    79  		}
    80  
    81  		if len(s.padding) > 0 {
    82  			out = append(out, fmt.Sprintf("padding[%d]{% 2X}", len(s.padding), s.padding))
    83  		}
    84  	}
    85  
    86  	return fmt.Sprintf("{%s}", strings.Join(out, " "))
    87  }
    88  
    89  // Header is the Optional PES Header defined in SO/IEC 13818-1 and ITU-T H.222.0.
    90  // It does not currently support any of the options that yield a variable length Header.
    91  type Header struct {
    92  	ScrambleControl byte
    93  
    94  	Priority      bool
    95  	DataAlignment bool
    96  	Copyright     bool
    97  	IsOriginal    bool
    98  
    99  	PTS *uint64
   100  	DTS *uint64
   101  
   102  	extLen int
   103  
   104  	extFlags byte
   105  	padding  []byte
   106  }
   107  
   108  const (
   109  	markerBits = 0x80
   110  
   111  	maskScramble  = 0x30
   112  	shiftScramble = 4
   113  
   114  	flagPriority  = 0x08
   115  	flagAlignment = 0x04
   116  	flagCopyright = 0x02
   117  	flagOriginal  = 0x01
   118  
   119  	flagPTSDTS = 0xC0
   120  )
   121  
   122  // unmarshalHeader fills in the values of an Optional PES Header from those encoded in the given byte-slice.
   123  func (h *Header) unmarshalHeader(b []byte) (int, error) {
   124  	length := 3 + int(b[2]) // full header length
   125  	b = b[:length]          // enforce header length with slice boundaries
   126  
   127  	h.ScrambleControl = (b[0] & maskScramble) >> shiftScramble
   128  	h.Priority = b[0]&flagPriority != 0
   129  	h.DataAlignment = b[0]&flagAlignment != 0
   130  	h.Copyright = b[0]&flagCopyright != 0
   131  	h.IsOriginal = b[0]&flagOriginal != 0
   132  
   133  	// where the padding starts
   134  	padStart := 3
   135  
   136  	switch b[1] & flagPTSDTS {
   137  	case 0x40:
   138  		return length, errors.New("invalid PTS/DTS flag value")
   139  
   140  	case 0x80:
   141  		h.PTS = decodeTS(b[padStart:])
   142  		padStart += 5
   143  
   144  	case 0xC0:
   145  		h.PTS = decodeTS(b[padStart:])
   146  		padStart += 5
   147  
   148  		h.DTS = decodeTS(b[padStart:])
   149  		padStart += 5
   150  	}
   151  
   152  	// we ignore all b[1] flags right now…
   153  	// if we were to read one of them, then padStart += lengthOf(field)
   154  	h.extFlags = b[1] &^ 0xc0
   155  
   156  	// we treat all of the rest of the header as “padding” for now
   157  	h.padding = append([]byte{}, b[padStart:]...)
   158  
   159  	return length, nil
   160  }
   161  
   162  // marshalHeader returns a byte-slice that is the encoding of a given Optional PES Header.
   163  func (h *Header) marshalHeader() ([]byte, error) {
   164  	if h.ScrambleControl&^0x03 != 0 {
   165  		return nil, errors.Errorf("invalid scramble control: 0x%02X", h.ScrambleControl)
   166  	}
   167  
   168  	out := make([]byte, 3)
   169  
   170  	out[0] = markerBits | (h.ScrambleControl << shiftScramble)
   171  
   172  	if h.Priority {
   173  		out[0] |= flagPriority
   174  	}
   175  
   176  	if h.DataAlignment {
   177  		out[0] |= flagAlignment
   178  	}
   179  
   180  	if h.Copyright {
   181  		out[0] |= flagCopyright
   182  	}
   183  
   184  	if h.IsOriginal {
   185  		out[0] |= flagOriginal
   186  	}
   187  
   188  	out[1] = 0
   189  
   190  	var pts, dts []byte
   191  
   192  	if h.PTS != nil {
   193  		pts = encodeTS(*h.PTS)
   194  		pts[0] |= 0x20
   195  		out[1] |= 0x80
   196  	}
   197  
   198  	if h.DTS != nil {
   199  		dts = encodeTS(*h.DTS)
   200  		pts[0] |= 0x10
   201  		dts[0] |= 0x10
   202  		out[1] |= 0x40
   203  	}
   204  
   205  	if len(pts) > 0 {
   206  		out = append(out, pts...)
   207  	}
   208  
   209  	if len(dts) > 0 {
   210  		out = append(out, dts...)
   211  	}
   212  
   213  	// The following fields are not supported and given is their presumed values:
   214  	// They would need to be implemented in the following order, as they are concatted one-after-another.
   215  	// PTS/DTS = both not included
   216  	// ESCR = false
   217  	// ES Rate = false
   218  	// DSM Trick Mode = false
   219  	// Additional Copy Info = false
   220  	// CRC = false
   221  	// Extension = false
   222  
   223  	// Stuff any “padding” here at the end.
   224  	out = append(out, h.padding...)
   225  
   226  	// remaining header length
   227  	out[2] = byte(len(out) - 3)
   228  
   229  	return out, nil
   230  }