github.com/cnotch/ipchub@v1.1.0/media/stream.go (about)

     1  // Copyright (c) 2019,CAOHONGJU All rights reserved.
     2  // Use of this source code is governed by a MIT-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package media
     6  
     7  import (
     8  	"errors"
     9  	"strings"
    10  	"sync/atomic"
    11  	"time"
    12  
    13  	"github.com/cnotch/ipchub/av/codec"
    14  	"github.com/cnotch/ipchub/av/format/flv"
    15  	"github.com/cnotch/ipchub/av/format/hls"
    16  	"github.com/cnotch/ipchub/av/format/mpegts"
    17  	"github.com/cnotch/ipchub/av/format/rtp"
    18  	"github.com/cnotch/ipchub/av/format/sdp"
    19  	"github.com/cnotch/ipchub/config"
    20  	"github.com/cnotch/ipchub/media/cache"
    21  	"github.com/cnotch/ipchub/stats"
    22  	"github.com/cnotch/ipchub/utils"
    23  	"github.com/cnotch/queue"
    24  	"github.com/cnotch/xlog"
    25  )
    26  
    27  // 流状态
    28  const (
    29  	StreamOK       int32 = iota
    30  	StreamClosed         // 源关闭
    31  	StreamReplaced       // 流被替换
    32  	StreamNoConsumer
    33  )
    34  
    35  // 错误定义
    36  var (
    37  	// ErrStreamClosed 流被关闭
    38  	ErrStreamClosed = errors.New("stream is closed")
    39  	// ErrStreamReplaced 流被替换
    40  	ErrStreamReplaced = errors.New("stream is replaced")
    41  	statusErrors      = []error{nil, ErrStreamClosed, ErrStreamReplaced}
    42  )
    43  
    44  // Stream 媒体流
    45  type Stream struct {
    46  	startOn              time.Time // 启动时间
    47  	path                 string    // 流路径
    48  	rawsdp               string
    49  	size                 uint64 // 流已经接收到的输入(字节)
    50  	status               int32  // 流状态
    51  	consumerSequenceSeed uint32
    52  	consumptions         consumptions // 消费者列表
    53  	cache                packCache    // 媒体包缓存
    54  	rtpDemuxer           rtpDemuxer
    55  	flvMuxer             flvMuxer
    56  	flvConsumptions      consumptions
    57  	flvCache             packCache
    58  	tsMuxer              *mpegts.Muxer
    59  	hlsSG                *hls.SegmentGenerator
    60  	hlsPlaylist          *hls.Playlist
    61  	attrs                map[string]string // 流属性
    62  	multicast            Multicastable
    63  	hls                  Hlsable
    64  	logger               *xlog.Logger // 日志对象
    65  	Video                codec.VideoMeta
    66  	Audio                codec.AudioMeta
    67  }
    68  
    69  // NewStream 创建新的流
    70  func NewStream(path string, rawsdp string, options ...Option) *Stream {
    71  	s := &Stream{
    72  		startOn:              time.Now(),
    73  		path:                 utils.CanonicalPath(path),
    74  		rawsdp:               rawsdp,
    75  		status:               StreamOK,
    76  		consumerSequenceSeed: 0,
    77  		attrs:                make(map[string]string, 2),
    78  		logger:               xlog.L().With(xlog.Fields(xlog.F("path", path))),
    79  	}
    80  
    81  	// parseMeta
    82  	sdp.ParseMetadata(rawsdp, &s.Video, &s.Audio)
    83  
    84  	// init Cache
    85  	switch s.Video.Codec {
    86  	case "H264":
    87  		s.cache = cache.NewH264Cache(config.CacheGop())
    88  	case "H265":
    89  		s.cache = cache.NewHevcCache(config.CacheGop())
    90  	default:
    91  		s.cache = emptyCache{}
    92  	}
    93  
    94  	for _, option := range options {
    95  		option.apply(s)
    96  	}
    97  
    98  	// prepareOtherStream
    99  	s.prepareOtherStream()
   100  	return s
   101  }
   102  
   103  func (s *Stream) prepareOtherStream() {
   104  	// steam(rtp)->rtpdemuxer->stream(frame)->flvmuxer->stream(tag)
   105  
   106  	s.flvCache = emptyCache{}
   107  	s.flvMuxer = emptyFlvMuxer{}
   108  
   109  	// prepare rtp.Packet -> codec.Frame
   110  	var err error
   111  	if s.rtpDemuxer, err = rtp.NewDemuxer(&s.Video, &s.Audio,
   112  		s, s.logger.With(xlog.Fields(xlog.F("extra", "rtp2frame")))); err != nil {
   113  		s.rtpDemuxer = emptyRtpDemuxer{}
   114  		return
   115  	}
   116  
   117  	// prepare codec.Frame -> flv.Tag
   118  	var flvMuxer *flv.Muxer
   119  	if flvMuxer, err = flv.NewMuxer(&s.Video, &s.Audio,
   120  		s, s.logger.With(xlog.Fields(xlog.F("extra", "frame2flv")))); err == nil {
   121  		s.flvCache = cache.NewFlvCache(config.CacheGop())
   122  		s.flvMuxer = flvMuxer
   123  	}
   124  
   125  	// prepare codec.Frame -> mpegts.Frame
   126  	if s.Video.Codec == "H264" {
   127  		hlsPlaylist := hls.NewPlaylist()
   128  		sg, err := hls.NewSegmentGenerator(hlsPlaylist, s.path,
   129  			config.HlsFragment(),
   130  			config.HlsPath(), s.Audio.SampleRate,
   131  			s.logger.With(xlog.Fields(xlog.F("extra", "hls.Muxer"))))
   132  		if err != nil {
   133  			return
   134  		}
   135  		tsMuxer, err2 := mpegts.NewMuxer(&s.Video, &s.Audio, sg,
   136  			s.logger.With(xlog.Fields(xlog.F("extra", "ts.Muxer"))))
   137  		if err2 != nil {
   138  			return
   139  		}
   140  		s.tsMuxer = tsMuxer
   141  		s.hlsSG = sg
   142  		s.hlsPlaylist = hlsPlaylist
   143  	}
   144  }
   145  
   146  // Path 流路径
   147  func (s *Stream) Path() string {
   148  	return s.path
   149  }
   150  
   151  // Sdp  sdp 字串
   152  func (s *Stream) Sdp() string {
   153  	return s.rawsdp
   154  }
   155  
   156  // FlvTypeFlags 支持的 flv TypeFlags
   157  func (s *Stream) FlvTypeFlags() byte {
   158  	return s.flvMuxer.TypeFlags()
   159  }
   160  
   161  // Attr 流属性
   162  func (s *Stream) Attr(key string) string {
   163  	return s.attrs[strings.ToLower(strings.TrimSpace(key))]
   164  }
   165  
   166  // Close 关闭流
   167  func (s *Stream) Close() error {
   168  	return s.close(StreamClosed)
   169  }
   170  func (s *Stream) close(status int32) error {
   171  	if atomic.LoadInt32(&s.status) != StreamOK {
   172  		return nil
   173  	}
   174  
   175  	// 修改流状态
   176  	if status != StreamReplaced {
   177  		status = StreamClosed
   178  	}
   179  	atomic.StoreInt32(&s.status, status)
   180  
   181  	// 关闭 hls
   182  	if s.tsMuxer != nil {
   183  		s.tsMuxer.Close()
   184  		s.hlsSG.Close()
   185  		s.hlsPlaylist.Close()
   186  	}
   187  
   188  	// 关闭 flv 消费者和 Muxer
   189  	s.flvConsumptions.RemoveAndCloseAll()
   190  	s.flvCache.Reset()
   191  	s.flvMuxer.Close()
   192  
   193  	// 关闭 av.Frame 转换器
   194  	s.rtpDemuxer.Close()
   195  
   196  	s.consumptions.RemoveAndCloseAll()
   197  	s.cache.Reset()
   198  	return nil
   199  }
   200  
   201  // WriteRtpPacket 向流写入一个媒体包
   202  func (s *Stream) WriteRtpPacket(packet *rtp.Packet) error {
   203  	status := atomic.LoadInt32(&s.status)
   204  	if status != StreamOK {
   205  		return statusErrors[status]
   206  	}
   207  
   208  	atomic.AddUint64(&s.size, uint64(packet.Size()))
   209  
   210  	keyframe := s.cache.CachePack(packet)
   211  	s.consumptions.SendToAll(packet, keyframe)
   212  
   213  	s.rtpDemuxer.WriteRtpPacket(packet)
   214  	return nil
   215  }
   216  
   217  // WriteFrame .
   218  func (s *Stream) WriteFrame(frame *codec.Frame) error {
   219  	if err := s.flvMuxer.WriteFrame(frame); err != nil {
   220  		s.logger.Error(err.Error())
   221  	}
   222  	if s.tsMuxer != nil {
   223  		if err := s.tsMuxer.WriteFrame(frame); err != nil {
   224  			s.logger.Error(err.Error())
   225  		}
   226  	}
   227  	return nil
   228  }
   229  
   230  // WriteTag .
   231  func (s *Stream) WriteFlvTag(tag *flv.Tag) error {
   232  	status := atomic.LoadInt32(&s.status)
   233  	if status != StreamOK {
   234  		return statusErrors[status]
   235  	}
   236  
   237  	keyframe := s.flvCache.CachePack(tag)
   238  	s.flvConsumptions.SendToAll(tag, keyframe)
   239  	return nil
   240  }
   241  
   242  // Multicastable 返回组播支持能力,不支持返回nil
   243  func (s *Stream) Multicastable() Multicastable {
   244  	return s.multicast
   245  }
   246  
   247  // Hlsable 返回支持hls能力,不支持返回nil
   248  func (s *Stream) Hlsable() Hlsable {
   249  	return s.hlsPlaylist
   250  }
   251  
   252  func (s *Stream) startConsume(consumer Consumer, packetType PacketType, extra string, useGopCache bool) CID {
   253  	if packetType == FLVPacket && s.flvMuxer == nil {
   254  		return CID(0) // 不支持
   255  	}
   256  
   257  	c := &consumption{
   258  		startOn:    time.Now(),
   259  		stream:     s,
   260  		cid:        NewCID(packetType, &s.consumerSequenceSeed),
   261  		recvQueue:  queue.NewSyncQueue(),
   262  		consumer:   consumer,
   263  		packetType: packetType,
   264  		extra:      extra,
   265  		Flow:       stats.NewFlow(),
   266  		maxQLen:    1000,
   267  	}
   268  
   269  	c.logger = s.logger.With(xlog.Fields(
   270  		xlog.F("cid", uint32(c.cid)),
   271  		xlog.F("packettype", c.packetType.String()),
   272  		xlog.F("extra", c.extra)))
   273  
   274  	cs := &s.consumptions
   275  	cache := s.cache
   276  	if packetType == FLVPacket {
   277  		cs = &s.flvConsumptions
   278  		cache = s.flvCache
   279  	}
   280  
   281  	if useGopCache {
   282  		c.sendGop(cache) // 新消费者,先发送gop缓存
   283  	}
   284  	cs.Add(c)
   285  
   286  	go c.consume()
   287  	return c.cid
   288  }
   289  
   290  // StartConsume 开始消费
   291  func (s *Stream) StartConsume(consumer Consumer, packetType PacketType, extra string) CID {
   292  	return s.startConsume(consumer, packetType, extra, true)
   293  }
   294  
   295  // StartConsumeNoGopCache 开始消费,不使用GopCahce
   296  func (s *Stream) StartConsumeNoGopCache(consumer Consumer, packetType PacketType, extra string) CID {
   297  	return s.startConsume(consumer, packetType, extra, false)
   298  }
   299  
   300  // StopConsume 开始消费
   301  func (s *Stream) StopConsume(cid CID) {
   302  	cs := &s.consumptions
   303  	if cid.Type() == FLVPacket {
   304  		cs = &s.flvConsumptions
   305  	}
   306  
   307  	c := cs.Remove(cid)
   308  	if c != nil {
   309  		c.Close()
   310  	}
   311  }
   312  
   313  // ConsumerCount 流消费者计数
   314  func (s *Stream) ConsumerCount() int {
   315  	return s.consumptions.Count() + s.flvConsumptions.Count()
   316  }
   317  
   318  // StreamInfo 流信息
   319  type StreamInfo struct {
   320  	StartOn          string            `json:"start_on"`
   321  	Path             string            `json:"path"`
   322  	Addr             string            `json:"addr"`
   323  	Size             int               `json:"size"`
   324  	Video            *codec.VideoMeta  `json:"video,omitempty"`
   325  	Audio            *codec.AudioMeta  `json:"audio,omitempty"`
   326  	ConsumptionCount int               `json:"cc"`
   327  	Consumptions     []ConsumptionInfo `json:"cs,omitempty"`
   328  }
   329  
   330  // Info 获取流信息
   331  func (s *Stream) Info(includeCS bool) *StreamInfo {
   332  	si := &StreamInfo{
   333  		StartOn:          s.startOn.Format(time.RFC3339Nano),
   334  		Path:             s.path,
   335  		Addr:             s.Attr("addr"),
   336  		Size:             int(atomic.LoadUint64(&s.size) / 1024),
   337  		ConsumptionCount: s.ConsumerCount(),
   338  	}
   339  
   340  	if len(s.Video.Codec) != 0 {
   341  		si.Video = &s.Video
   342  	}
   343  	if len(s.Audio.Codec) != 0 {
   344  		si.Audio = &s.Audio
   345  	}
   346  	if includeCS {
   347  		si.Consumptions = s.consumptions.Infos()
   348  		si.Consumptions = append(si.Consumptions, s.flvConsumptions.Infos()...)
   349  	}
   350  	return si
   351  }
   352  
   353  // GetConsumption 获取指定消费信息
   354  func (s *Stream) GetConsumption(cid CID) (ConsumptionInfo, bool) {
   355  	cs := &s.consumptions
   356  	if cid.Type() == FLVPacket {
   357  		cs = &s.flvConsumptions
   358  	}
   359  
   360  	c, ok := cs.Load(cid)
   361  	if ok {
   362  		return c.(*consumption).Info(), ok
   363  	}
   364  	return ConsumptionInfo{}, false
   365  }