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

     1  // Package ts implements a Mux/Demux for the MPEG Transport Stream protocol.
     2  package ts
     3  
     4  import (
     5  	"io"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/pkg/errors"
    10  	"github.com/puellanivis/breton/lib/mpeg/ts/dvb"
    11  	"github.com/puellanivis/breton/lib/mpeg/ts/packet"
    12  	"github.com/puellanivis/breton/lib/mpeg/ts/psi"
    13  )
    14  
    15  // PacketSize is the length in bytes of an MPEG-TS packet.
    16  const PacketSize = packet.Length
    17  
    18  // Option defines a function that will apply some value or behavior to a TransportStream.
    19  type Option func(*TransportStream) Option
    20  
    21  // WithDebug sets a function that will be called for each MPEG-TS Packet processed by the TransportStream.
    22  func WithDebug(fn func(*packet.Packet)) Option {
    23  	return func(ts *TransportStream) Option {
    24  		ts.once.Do(ts.init)
    25  		ts.mu.Lock()
    26  		defer ts.mu.Unlock()
    27  
    28  		save := ts.debug
    29  		ts.debug = fn
    30  
    31  		return WithDebug(save)
    32  	}
    33  }
    34  
    35  func (ts *TransportStream) getDebug() func(*packet.Packet) {
    36  	ts.once.Do(ts.init)
    37  	ts.mu.Lock()
    38  	defer ts.mu.Unlock()
    39  
    40  	return ts.debug
    41  }
    42  
    43  // WithUpdateRate sets the rate at which the TransportStream will update TODO.
    44  func WithUpdateRate(d time.Duration) Option {
    45  	return func(ts *TransportStream) Option {
    46  		ts.once.Do(ts.init)
    47  		ts.mu.Lock()
    48  		defer ts.mu.Unlock()
    49  
    50  		save := ts.updateRate
    51  		ts.updateRate = d
    52  
    53  		return WithUpdateRate(save)
    54  	}
    55  }
    56  
    57  func (ts *TransportStream) getUpdateRate() time.Duration {
    58  	ts.once.Do(ts.init)
    59  	ts.mu.Lock()
    60  	defer ts.mu.Unlock()
    61  
    62  	return ts.updateRate
    63  }
    64  
    65  // TransportStream defines an MPEG Transport Stream.
    66  type TransportStream struct {
    67  	sink io.Writer
    68  
    69  	ticker  chan struct{}
    70  	counter int
    71  
    72  	once sync.Once
    73  	mu   sync.Mutex
    74  
    75  	debug      func(*packet.Packet)
    76  	updateRate time.Duration
    77  
    78  	patReady chan struct{}
    79  	pat      map[uint16]uint16
    80  	pmts     map[uint16]*Program
    81  	dvbSDT   *dvb.ServiceDescriptorTable
    82  
    83  	nextStreamPID  uint16
    84  	nextProgramPID uint16
    85  	lastStreamID   uint16
    86  
    87  	m *Mux
    88  }
    89  
    90  func (ts *TransportStream) init() {
    91  	ts.patReady = make(chan struct{})
    92  	ts.nextStreamPID = 0x100
    93  	ts.nextProgramPID = 0x1000
    94  	ts.updateRate = 1 * time.Second / 25 // 25 Hz
    95  }
    96  
    97  func (ts *TransportStream) writePackets(pkts ...*packet.Packet) (n int, err error) {
    98  	debug := ts.getDebug()
    99  
   100  	var q [][]byte
   101  
   102  	for _, pkt := range pkts {
   103  		if debug != nil {
   104  			debug(pkt)
   105  		}
   106  
   107  		b, err2 := pkt.Marshal()
   108  		if err2 != nil {
   109  			if err == nil {
   110  				err = err2
   111  			}
   112  
   113  			continue
   114  		}
   115  
   116  		q = append(q, b)
   117  	}
   118  
   119  	if len(q) < 1 {
   120  		return 0, err
   121  	}
   122  
   123  	ts.mu.Lock()
   124  	defer ts.mu.Unlock()
   125  
   126  	for _, b := range q {
   127  		n2, err2 := ts.sink.Write(b)
   128  
   129  		n += n2
   130  		if err == nil {
   131  			err = err2
   132  		}
   133  	}
   134  
   135  	if len(q) > 1 {
   136  		ts.counter += 15
   137  		return n, err
   138  	}
   139  
   140  	ts.counter--
   141  	if ts.counter <= 0 {
   142  		select {
   143  		case ts.ticker <- struct{}{}:
   144  			ts.counter += 15 // TODO: make configurable
   145  		default:
   146  		}
   147  	}
   148  
   149  	return n, err
   150  }
   151  
   152  func (ts *TransportStream) newStreamPID() uint16 {
   153  	ts.mu.Lock()
   154  	defer ts.mu.Unlock()
   155  
   156  	pid := ts.nextStreamPID
   157  	ts.nextStreamPID++
   158  	return pid
   159  }
   160  
   161  // NewProgram returns a new Program assigned to the given Stream ID.
   162  func (ts *TransportStream) NewProgram(streamID uint16) (*Program, error) {
   163  	ts.once.Do(ts.init)
   164  	ts.mu.Lock()
   165  	defer ts.mu.Unlock()
   166  
   167  	if ts.m == nil {
   168  		return nil, errors.New("cannot make a new program on a non-outbound transport stream")
   169  	}
   170  
   171  	if streamID == 0 {
   172  		streamID = ts.lastStreamID + 1
   173  
   174  		for ts.pat[streamID] != 0 {
   175  			streamID++
   176  		}
   177  	}
   178  	ts.lastStreamID = streamID
   179  
   180  	if ts.pat[streamID] != 0 {
   181  		return nil, errors.Errorf("stream_id 0x%04X is already assigned", streamID)
   182  	}
   183  
   184  	pid := ts.nextProgramPID
   185  	ts.nextProgramPID++
   186  
   187  	p := &Program{
   188  		pid: pid,
   189  		ts:  ts,
   190  		pmt: &psi.PMT{
   191  			Syntax: &psi.SectionSyntax{
   192  				TableIDExtension: streamID,
   193  				Current:          true,
   194  			},
   195  			PCRPID: 0x1FFF,
   196  		},
   197  	}
   198  
   199  	if ts.pat == nil {
   200  		ts.pat = make(map[uint16]uint16)
   201  
   202  		select {
   203  		case <-ts.patReady:
   204  		default:
   205  			close(ts.patReady)
   206  		}
   207  	}
   208  
   209  	ts.pat[streamID] = pid
   210  
   211  	if ts.pmts == nil {
   212  		ts.pmts = make(map[uint16]*Program)
   213  	}
   214  
   215  	ts.pmts[pid] = p
   216  
   217  	return p, nil
   218  }
   219  
   220  func cloneSDT(src *dvb.ServiceDescriptorTable) *dvb.ServiceDescriptorTable {
   221  	if src == nil {
   222  		return nil
   223  	}
   224  
   225  	sdt := *src
   226  
   227  	if src.Syntax != nil {
   228  		s := *src.Syntax
   229  		sdt.Syntax = &s
   230  	}
   231  
   232  	sdt.Services = nil
   233  	for _, s := range src.Services {
   234  		n := *s
   235  		n.Descriptors = nil
   236  
   237  		for _, d := range s.Descriptors {
   238  			if d, ok := d.(*dvb.ServiceDescriptor); ok {
   239  				nd := *d
   240  				n.Descriptors = append(n.Descriptors, &nd)
   241  			}
   242  		}
   243  
   244  		sdt.Services = append(sdt.Services, &n)
   245  	}
   246  
   247  	return &sdt
   248  }
   249  
   250  // SetDVBSDT sets the DVB Service Description Table for the TransportStream.
   251  func (ts *TransportStream) SetDVBSDT(sdt *dvb.ServiceDescriptorTable) {
   252  	clone := cloneSDT(sdt)
   253  
   254  	ts.mu.Lock()
   255  	defer ts.mu.Unlock()
   256  
   257  	ts.dvbSDT = clone
   258  }
   259  
   260  func (ts *TransportStream) getDVBSDT() *dvb.ServiceDescriptorTable {
   261  	ts.mu.Lock()
   262  	defer ts.mu.Unlock()
   263  
   264  	return ts.dvbSDT
   265  }
   266  
   267  // GetDVBSDT returns a deep copy of the DVB Service Description Table being used by the TransportStream.
   268  func (ts *TransportStream) GetDVBSDT() *dvb.ServiceDescriptorTable {
   269  	ts.mu.Lock()
   270  	defer ts.mu.Unlock()
   271  
   272  	return cloneSDT(ts.dvbSDT)
   273  }
   274  
   275  // GetPAT returns a map defining the Program Allocation Table of the TransportStream.
   276  func (ts *TransportStream) GetPAT() map[uint16]uint16 {
   277  	ts.once.Do(ts.init)
   278  
   279  	<-ts.patReady
   280  
   281  	ts.mu.Lock()
   282  	defer ts.mu.Unlock()
   283  
   284  	return ts.pat
   285  }
   286  
   287  func (ts *TransportStream) setPAT(pat map[uint16]uint16) {
   288  	ts.once.Do(ts.init)
   289  	ts.mu.Lock()
   290  	defer ts.mu.Unlock()
   291  
   292  	if pat != nil {
   293  		ts.pat = pat
   294  	}
   295  
   296  	select {
   297  	case <-ts.patReady:
   298  	default:
   299  		close(ts.patReady)
   300  	}
   301  }
   302  
   303  // GetPMTs returns a map defining the Program Map Tables indexed by their pid.
   304  //
   305  // TODO: confirm behavior.
   306  func (ts *TransportStream) GetPMTs() map[uint16]*Program {
   307  	ts.once.Do(ts.init)
   308  	ts.mu.Lock()
   309  	defer ts.mu.Unlock()
   310  
   311  	return ts.pmts
   312  }