github.com/bluenviron/mediacommon@v1.9.3/pkg/formats/mpegts/track.go (about)

     1  package mpegts
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/asticode/go-astits"
     8  	"github.com/bluenviron/mediacommon/pkg/codecs/ac3"
     9  	"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
    10  )
    11  
    12  const (
    13  	h264Identifier = 'H'<<24 | 'D'<<16 | 'M'<<8 | 'V'
    14  	h265Identifier = 'H'<<24 | 'E'<<16 | 'V'<<8 | 'C'
    15  	opusIdentifier = 'O'<<24 | 'p'<<16 | 'u'<<8 | 's'
    16  )
    17  
    18  var errUnsupportedCodec = errors.New("unsupported codec")
    19  
    20  func findMPEG4AudioConfig(dem *astits.Demuxer, pid uint16) (*mpeg4audio.Config, error) {
    21  	for {
    22  		data, err := dem.NextData()
    23  		if err != nil {
    24  			return nil, err
    25  		}
    26  
    27  		if data.PES == nil || data.PID != pid {
    28  			continue
    29  		}
    30  
    31  		var adtsPkts mpeg4audio.ADTSPackets
    32  		err = adtsPkts.Unmarshal(data.PES.Data)
    33  		if err != nil {
    34  			return nil, fmt.Errorf("unable to decode ADTS: %w", err)
    35  		}
    36  
    37  		pkt := adtsPkts[0]
    38  		return &mpeg4audio.Config{
    39  			Type:         pkt.Type,
    40  			SampleRate:   pkt.SampleRate,
    41  			ChannelCount: pkt.ChannelCount,
    42  		}, nil
    43  	}
    44  }
    45  
    46  func findAC3Parameters(dem *astits.Demuxer, pid uint16) (int, int, error) {
    47  	for {
    48  		data, err := dem.NextData()
    49  		if err != nil {
    50  			return 0, 0, err
    51  		}
    52  
    53  		if data.PES == nil || data.PID != pid {
    54  			continue
    55  		}
    56  
    57  		var syncInfo ac3.SyncInfo
    58  		err = syncInfo.Unmarshal(data.PES.Data)
    59  		if err != nil {
    60  			return 0, 0, fmt.Errorf("invalid AC-3 frame: %w", err)
    61  		}
    62  
    63  		var bsi ac3.BSI
    64  		err = bsi.Unmarshal(data.PES.Data[5:])
    65  		if err != nil {
    66  			return 0, 0, fmt.Errorf("invalid AC-3 frame: %w", err)
    67  		}
    68  
    69  		return syncInfo.SampleRate(), bsi.ChannelCount(), nil
    70  	}
    71  }
    72  
    73  func findOpusRegistration(descriptors []*astits.Descriptor) bool {
    74  	for _, sd := range descriptors {
    75  		if sd.Registration != nil {
    76  			if sd.Registration.FormatIdentifier == opusIdentifier {
    77  				return true
    78  			}
    79  		}
    80  	}
    81  	return false
    82  }
    83  
    84  func findOpusChannelCount(descriptors []*astits.Descriptor) int {
    85  	for _, sd := range descriptors {
    86  		if sd.Extension != nil && sd.Extension.Tag == 0x80 &&
    87  			sd.Extension.Unknown != nil && len(*sd.Extension.Unknown) >= 1 {
    88  			return int((*sd.Extension.Unknown)[0])
    89  		}
    90  	}
    91  	return 0
    92  }
    93  
    94  func findOpusCodec(descriptors []*astits.Descriptor) *CodecOpus {
    95  	if !findOpusRegistration(descriptors) {
    96  		return nil
    97  	}
    98  
    99  	channelCount := findOpusChannelCount(descriptors)
   100  	if channelCount <= 0 {
   101  		return nil
   102  	}
   103  
   104  	return &CodecOpus{
   105  		ChannelCount: channelCount,
   106  	}
   107  }
   108  
   109  // Track is a MPEG-TS track.
   110  type Track struct {
   111  	PID   uint16
   112  	Codec Codec
   113  
   114  	isLeading  bool // Writer-only
   115  	mp3Checked bool // Writer-only
   116  }
   117  
   118  func (t *Track) marshal() (*astits.PMTElementaryStream, error) {
   119  	return t.Codec.marshal(t.PID)
   120  }
   121  
   122  func (t *Track) unmarshal(dem *astits.Demuxer, es *astits.PMTElementaryStream) error {
   123  	t.PID = es.ElementaryPID
   124  
   125  	switch es.StreamType {
   126  	case astits.StreamTypeH265Video:
   127  		t.Codec = &CodecH265{}
   128  		return nil
   129  
   130  	case astits.StreamTypeH264Video:
   131  		t.Codec = &CodecH264{}
   132  		return nil
   133  
   134  	case astits.StreamTypeMPEG4Video:
   135  		t.Codec = &CodecMPEG4Video{}
   136  		return nil
   137  
   138  	case astits.StreamTypeMPEG2Video, astits.StreamTypeMPEG1Video:
   139  		t.Codec = &CodecMPEG1Video{}
   140  		return nil
   141  
   142  	case astits.StreamTypeAACAudio:
   143  		conf, err := findMPEG4AudioConfig(dem, es.ElementaryPID)
   144  		if err != nil {
   145  			return err
   146  		}
   147  
   148  		t.Codec = &CodecMPEG4Audio{
   149  			Config: *conf,
   150  		}
   151  		return nil
   152  
   153  	case astits.StreamTypeMPEG1Audio:
   154  		t.Codec = &CodecMPEG1Audio{}
   155  		return nil
   156  
   157  	case astits.StreamTypeAC3Audio:
   158  		sampleRate, channelCount, err := findAC3Parameters(dem, es.ElementaryPID)
   159  		if err != nil {
   160  			return err
   161  		}
   162  
   163  		t.Codec = &CodecAC3{
   164  			SampleRate:   sampleRate,
   165  			ChannelCount: channelCount,
   166  		}
   167  		return nil
   168  
   169  	case astits.StreamTypePrivateData:
   170  		codec := findOpusCodec(es.ElementaryStreamDescriptors)
   171  		if codec != nil {
   172  			t.Codec = codec
   173  			return nil
   174  		}
   175  	}
   176  
   177  	return errUnsupportedCodec
   178  }