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 }