Skip to content

订阅者

订阅者的功能就是将音视频或者其他数据从 engine 中读取出来。

读取出来后,通常会发送给播放器,或者写入磁盘。

TIP

可以结合官方插件中对Subscriber的使用,来掌握订阅者的使用方法。

定义订阅者

虽然可以直接使用 Subscriber 作为订阅者,但是通常我们需要自定义一个结构,里面包含 Subscriber,这样就成为了一个特定功能的 Subscriber

import . "m7s.live/engine/v4"

type MySubscriber struct {
  Subscriber
}

包含 Subscriber 后,就自动实现了 ISubscriber 接口。 这个结构体中可以随意的放入自己需要的属性。

定义订阅者事件回调

v4 中事件回调取代了之前的所有的逻辑,下面演示了可能接收到的事件:

import (
  . "m7s.live/engine/v4"
  "m7s.live/engine/v4/track"
)
func (p *MySubscriber) OnEvent(event any) {
  switch v:=event.(type) {
    case ISubscriber://代表订阅成功事件,v就是p
    case SEclose://代表关闭事件
    case Track: //代表收到Track事件(包括所有的Track事件),响应了这个就屏蔽了下面三个
    case *track.Audio: //代表收到AudioTrack事件
    case *track.Video: //代表收到VideoTrack事件
    case *track.Data: //代表收到DataTrack事件
    case *AudioFrame: //代表收到AudioFrame事件
    case *VideoFrame: //代表收到VideoFrame事件
    case HaveFLV: //代表收到含有FLV数据的帧事件,其中相当于包括了AudioFrame和VideoFrame
    default:
      p.Subscriber.OnEvent(event)
  }
}

其中收到Track或者*track.Audio*track.Video*track.Data 后需要判断是否接收这个Track,即调用 p.AddTrack(v)

通常我们如果需要全盘接收的话,可以不响应这些类型的事件,而交给 SubscriberOnEvent 去处理(即从上方的default进入)。内部代码如下

func (s *Subscriber) OnEvent(event any) {
	switch v := event.(type) {
	case TrackRemoved:
		if a, ok := v.Track.(*track.Audio); ok && a == s.Audio.Track {
			s.Audio.ring = nil
		} else if v, ok := v.Track.(*track.Video); ok && v == s.Video.Track {
			s.Video.ring = nil
		}
	case Track: //默认接受所有track
		s.AddTrack(v)
	default:
		s.IO.OnEvent(event)
	}
}

开始订阅

订阅流需要先注册订阅流,成功后可以对音视频轨道进行读取操作。

注册订阅流(订阅)

该函数会阻塞一小段时间,直到注册成功或者失败。

sub := new(MySubscriber)
if plugin.Subscribe("live/test", sub) == nil {
  //注册成功
}

一旦注册成功就会在OnEvent收到事件。

开始读取

sub.PlayBlock() 

如果没有其他逻辑要分离处理也可以直接调用下面这个方法

func (opt *Plugin) SubscribeBlock(streamPath string, sub ISubscriber) (err error) {
	if err = opt.Subscribe(streamPath, sub); err == nil {
		sub.PlayBlock()
	}
	return
}

从字面上就看出是阻塞读取数据。 读取到的数据我们从OnEvent中去处理。 如果订阅完成或者中途退出了,那么这个阻塞就会释放。

停止订阅

sub.Stop()

资源跟随关闭

如果订阅者和某个可关闭的资源(例如conn)是强绑定关系,则可以调用

sub.SetIO(conn)

那么当这个sub被关闭的时候,也会调用conn的Close方法。 注意:主动停止订阅即,调用sub.Stop不会调用conn的Close方法。

给订阅者加父级Context

有时候我们需要订阅者跟随某个Context而关闭则可以调用

sub.SetParent(ctx)
订阅者 has loaded