github.com/cnotch/ipchub@v1.1.0/media/global.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  	"fmt"
     9  	"sort"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/cnotch/ipchub/provider/route"
    14  	"github.com/cnotch/ipchub/utils"
    15  	"github.com/cnotch/scheduler"
    16  	"github.com/cnotch/xlog"
    17  )
    18  
    19  // 全局变量
    20  var (
    21  	streams     sync.Map            // 流媒体集合 string->*Stream
    22  	psFactories []PullStreamFactory // 拉流工厂
    23  )
    24  
    25  // PullStreamFactory 拉流工程流
    26  type PullStreamFactory interface {
    27  	Can(remoteURL string) bool
    28  	Create(localPath, remoteURL string) (*Stream, error)
    29  }
    30  
    31  // RegistPullStreamFactory 注册拉流工厂
    32  func RegistPullStreamFactory(f PullStreamFactory) {
    33  	for _, factroy := range psFactories {
    34  		if factroy == f {
    35  			return
    36  		}
    37  	}
    38  	psFactories = append(psFactories, f)
    39  }
    40  
    41  // Regist 注册流
    42  func Regist(s *Stream) {
    43  	// 获取同 path 的现有流
    44  	oldSI, ok := streams.Load(s.path)
    45  	if s == oldSI { // 如果是同一个源
    46  		return
    47  	}
    48  
    49  	// 设置新流
    50  	streams.Store(s.path, s)
    51  
    52  	// 如果存在旧流
    53  	if ok {
    54  		oldS := oldSI.(*Stream)
    55  		if oldS.ConsumerCount() <= 0 { // 没有消费者直接关闭
    56  			oldS.close(StreamReplaced)
    57  		} else { // 有消费者个5分钟检查一次,直到没有消费者就关闭
    58  			runZeroConsumersCloseTask(oldS, StreamReplaced)
    59  		}
    60  	}
    61  }
    62  
    63  // Unregist 取消注册
    64  func Unregist(s *Stream) {
    65  	si, ok := streams.Load(s.path)
    66  	if ok {
    67  		s2 := si.(*Stream)
    68  		if s2 == s {
    69  			streams.Delete(s.path)
    70  		}
    71  	}
    72  	s.Close()
    73  }
    74  
    75  // UnregistAll 取消全部注册的流
    76  func UnregistAll() {
    77  	streams.Range(func(key, value interface{}) bool {
    78  		streams.Delete(key)
    79  		s := value.(*Stream)
    80  		s.Close()
    81  		return true
    82  	})
    83  }
    84  
    85  // Get 获取路径为 path 已存在的流。
    86  func Get(path string) *Stream {
    87  	path = utils.CanonicalPath(path)
    88  
    89  	si, ok := streams.Load(path)
    90  	if ok {
    91  		return si.(*Stream)
    92  	}
    93  	return nil
    94  }
    95  
    96  // GetOrCreate 获取路径为 path 的流媒体,如果不存在尝试自动创建拉流。
    97  func GetOrCreate(path string) *Stream {
    98  	if s:=Get(path);s!=nil{
    99  		return s
   100  	}
   101  
   102  	// 检查路由
   103  	path = utils.CanonicalPath(path)
   104  	r := route.Match(path)
   105  	if r != nil {
   106  		var s *Stream
   107  		var err error
   108  		for _, psf := range psFactories {
   109  			if psf.Can(r.URL) {
   110  				s, err = psf.Create(r.Pattern, r.URL)
   111  				if err == nil {
   112  					if !r.KeepAlive {
   113  						// 启动没有消费时自动关闭任务
   114  						runZeroConsumersCloseTask(s, StreamNoConsumer)
   115  					}
   116  				} else {
   117  					xlog.Errorf("open pull stream from `%s` failed; %s.", r.URL, err.Error())
   118  				}
   119  
   120  				break
   121  			}
   122  		}
   123  
   124  		if s != nil {
   125  			return s
   126  		}
   127  	}
   128  
   129  	return nil
   130  }
   131  
   132  // Count 流媒体数量和消费者数量
   133  func Count() (sc, cc int) {
   134  	streams.Range(func(key, value interface{}) bool {
   135  		s := value.(*Stream)
   136  		sc++
   137  		cc += s.ConsumerCount()
   138  		return true
   139  	})
   140  	return
   141  }
   142  
   143  // Infos 返回所有的流媒体信息
   144  func Infos(pagetoken string, pagesize int, includeCS bool) (int, []*StreamInfo) {
   145  	rtp := make(map[string]*StreamInfo)
   146  
   147  	streams.Range(func(key, value interface{}) bool {
   148  		s := value.(*Stream)
   149  		rtp[s.Path()] = s.Info(includeCS)
   150  		return true
   151  	})
   152  
   153  	count := len(rtp)
   154  	ss := make([]*StreamInfo, 0, count)
   155  	for _, v := range rtp {
   156  		if v.Path > pagetoken {
   157  			ss = append(ss, v)
   158  		}
   159  	}
   160  
   161  	sort.Slice(ss, func(i, j int) bool {
   162  		return ss[i].Path < ss[j].Path
   163  	})
   164  
   165  	if pagesize > len(ss) {
   166  		return count, ss
   167  	}
   168  	return count, ss[:pagesize]
   169  }
   170  
   171  func runZeroConsumersCloseTask(s *Stream, closedStatus int32) {
   172  	timing := &runZeroConsumersClose{
   173  		s:           s,
   174  		d:           time.Minute * 5,
   175  		closedStats: closedStatus,
   176  	}
   177  	scheduler.PostFunc(timing, timing.run,
   178  		fmt.Sprintf("%s: The close task when the stream exceeds a certain amount of time without a consumer.", s.path))
   179  }
   180  
   181  // 不处于管理状态的流的监测计划
   182  type runZeroConsumersClose struct {
   183  	s           *Stream
   184  	d           time.Duration
   185  	closed      bool
   186  	closedStats int32
   187  }
   188  
   189  func (r *runZeroConsumersClose) Next(t time.Time) time.Time {
   190  	if r.closed {
   191  		return time.Time{}
   192  	}
   193  	return t.Add(r.d)
   194  }
   195  
   196  func (r *runZeroConsumersClose) run() {
   197  	if r.s.consumptions.Count() <= 0 {
   198  		hlsable := r.s.Hlsable()
   199  		if hlsable == nil || time.Now().Sub(hlsable.LastAccessTime()) >= r.d {
   200  			r.closed = true
   201  			r.s.close(r.closedStats)
   202  		}
   203  	}
   204  }