|
|
|
|
// Copyright 2020, Chef. All rights reserved.
|
|
|
|
|
// https://github.com/q191201771/lal
|
|
|
|
|
//
|
|
|
|
|
// Use of this source code is governed by a MIT-style license
|
|
|
|
|
// that can be found in the License file.
|
|
|
|
|
//
|
|
|
|
|
// Author: Chef (191201771@qq.com)
|
|
|
|
|
|
|
|
|
|
package base
|
|
|
|
|
|
|
|
|
|
// ----- 所有session -----
|
|
|
|
|
//
|
|
|
|
|
// server.pub: rtmp(ServerSession), rtsp(PubSession), customize(CustomizePubSessionContext), ps(gb28181.PubSession)
|
|
|
|
|
// server.sub: rtmp(ServerSession), rtsp(SubSession), flv(SubSession), ts(SubSession), 还有一个比较特殊的hls
|
|
|
|
|
//
|
|
|
|
|
// client.push: rtmp(PushSession), rtsp(PushSession)
|
|
|
|
|
// client.pull: rtmp(PullSession), rtsp(PullSession), flv(PullSession)
|
|
|
|
|
//
|
|
|
|
|
// other: rtmp.ClientSession, (rtmp.ServerSession)
|
|
|
|
|
// rtsp.BaseInSession, rtsp.BaseOutSession, rtsp.ClientCommandSession, rtsp.ServerCommandSession
|
|
|
|
|
// base.BasicHttpSubSession
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
type (
|
|
|
|
|
SessionType int
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
SessionTypeCustomizePub SessionType = SessionProtocolCustomize<<8 | SessionBaseTypePub
|
|
|
|
|
SessionTypeRtmpServerSession SessionType = SessionProtocolRtmp<<8 | SessionBaseTypePubSub
|
|
|
|
|
SessionTypeRtmpPush SessionType = SessionProtocolRtmp<<8 | SessionBaseTypePush
|
|
|
|
|
SessionTypeRtmpPull SessionType = SessionProtocolRtmp<<8 | SessionBaseTypePull
|
|
|
|
|
SessionTypeRtspPub SessionType = SessionProtocolRtsp<<8 | SessionBaseTypePub
|
|
|
|
|
SessionTypeRtspSub SessionType = SessionProtocolRtsp<<8 | SessionBaseTypeSub
|
|
|
|
|
SessionTypeRtspPush SessionType = SessionProtocolRtsp<<8 | SessionBaseTypePush
|
|
|
|
|
SessionTypeRtspPull SessionType = SessionProtocolRtsp<<8 | SessionBaseTypePull
|
|
|
|
|
SessionTypeFlvSub SessionType = SessionProtocolFlv<<8 | SessionBaseTypeSub
|
|
|
|
|
SessionTypeFlvPull SessionType = SessionProtocolFlv<<8 | SessionBaseTypePull
|
|
|
|
|
SessionTypeTsSub SessionType = SessionProtocolTs<<8 | SessionBaseTypeSub
|
|
|
|
|
SessionTypePsPub SessionType = SessionProtocolPs<<8 | SessionBaseTypePub
|
|
|
|
|
SessionTypeHlsSub SessionType = SessionProtocolHls<<8 | SessionBaseTypeSub
|
|
|
|
|
|
|
|
|
|
SessionProtocolCustomize = 1
|
|
|
|
|
SessionProtocolRtmp = 2
|
|
|
|
|
SessionProtocolRtsp = 3
|
|
|
|
|
SessionProtocolFlv = 4
|
|
|
|
|
SessionProtocolTs = 5
|
|
|
|
|
SessionProtocolPs = 6
|
|
|
|
|
SessionProtocolHls = 7
|
|
|
|
|
|
|
|
|
|
SessionBaseTypePubSub = 1
|
|
|
|
|
SessionBaseTypePub = 2
|
|
|
|
|
SessionBaseTypeSub = 3
|
|
|
|
|
SessionBaseTypePush = 4
|
|
|
|
|
SessionBaseTypePull = 5
|
|
|
|
|
|
|
|
|
|
SessionProtocolCustomizeStr = "CUSTOMIZE"
|
|
|
|
|
SessionProtocolRtmpStr = "RTMP"
|
|
|
|
|
SessionProtocolRtspStr = "RTSP"
|
|
|
|
|
SessionProtocolFlvStr = "FLV"
|
|
|
|
|
SessionProtocolTsStr = "TS"
|
|
|
|
|
SessionProtocolPsStr = "PS"
|
|
|
|
|
SessionProtocolHlsStr = "HLS"
|
|
|
|
|
|
|
|
|
|
SessionBaseTypePubSubStr = "PUBSUB"
|
|
|
|
|
SessionBaseTypePubStr = "PUB"
|
|
|
|
|
SessionBaseTypeSubStr = "SUB"
|
|
|
|
|
SessionBaseTypePushStr = "PUSH"
|
|
|
|
|
SessionBaseTypePullStr = "PULL"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type ISession interface {
|
|
|
|
|
ISessionUrlContext
|
|
|
|
|
IObject
|
|
|
|
|
ISessionStat
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type IClientSession interface {
|
|
|
|
|
// PushSession:
|
|
|
|
|
// Push()
|
|
|
|
|
// Write()
|
|
|
|
|
// Flush()
|
|
|
|
|
// PullSession:
|
|
|
|
|
// Pull()
|
|
|
|
|
|
|
|
|
|
IClientSessionLifecycle
|
|
|
|
|
ISession
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type IServerSession interface {
|
|
|
|
|
IServerSessionLifecycle
|
|
|
|
|
ISession
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
type IClientSessionLifecycle interface {
|
|
|
|
|
// Dispose 主动关闭session时调用
|
|
|
|
|
//
|
|
|
|
|
// 注意,只有Start(具体session的Start类型函数一般命令为Push和Pull)成功后的session才能调用,否则行为未定义
|
|
|
|
|
//
|
|
|
|
|
// Dispose可在任意协程内调用
|
|
|
|
|
//
|
|
|
|
|
// 注意,目前Dispose允许调用多次,但是未来可能不对该表现做保证
|
|
|
|
|
//
|
|
|
|
|
// Dispose后,调用Write函数将返回错误
|
|
|
|
|
//
|
|
|
|
|
// @return 可以通过返回值判断调用Dispose前,session是否已经被关闭了 TODO(chef) 这个返回值没有太大意义,后面可能会删掉
|
|
|
|
|
//
|
|
|
|
|
Dispose() error
|
|
|
|
|
|
|
|
|
|
// WaitChan Start成功后,可使用这个channel来接收session结束的消息
|
|
|
|
|
//
|
|
|
|
|
// 注意,只有Start成功后的session才能调用,否则行为未定义
|
|
|
|
|
//
|
|
|
|
|
// 注意,目前WaitChan只会被通知一次,但是未来可能不对该表现做保证,业务方应该只关注第一次通知
|
|
|
|
|
//
|
|
|
|
|
// TODO(chef): 是否应该严格保证:获取到关闭消息后,后续不应该再有该session的回调上来
|
|
|
|
|
//
|
|
|
|
|
// @return 一般关闭有以下几种情况:
|
|
|
|
|
// - 对端关闭,此时error为EOF
|
|
|
|
|
// - 本端底层关闭,比如协议非法等,此时error为具体的错误值
|
|
|
|
|
// - 本端上层主动调用Dispose关闭,此时error为nil
|
|
|
|
|
//
|
|
|
|
|
WaitChan() <-chan error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type IServerSessionLifecycle interface {
|
|
|
|
|
// RunLoop 开启session的事件循环,阻塞直到session结束
|
|
|
|
|
//
|
|
|
|
|
// 注意,rtsp的 pub和sub没有RunLoop,RunLoop是在cmd session上,所以暂时把这个函数从接口去除
|
|
|
|
|
//
|
|
|
|
|
//RunLoop() error
|
|
|
|
|
|
|
|
|
|
// Dispose 主动关闭session时调用
|
|
|
|
|
//
|
|
|
|
|
// 如果是session通知业务方session已关闭(比如`RunLoop`函数返回错误),则不需要调用`Dispose` TODO(chef): review现状
|
|
|
|
|
//
|
|
|
|
|
Dispose() error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ISessionStat
|
|
|
|
|
//
|
|
|
|
|
// 调用约束:对于Client类型的Session,调用Start函数并返回成功后才能调用,否则行为未定义
|
|
|
|
|
type ISessionStat interface {
|
|
|
|
|
// UpdateStat
|
|
|
|
|
//
|
|
|
|
|
// 周期性调用该函数,用于计算bitrate
|
|
|
|
|
//
|
|
|
|
|
// @param intervalSec 距离上次调用的时间间隔,单位毫秒
|
|
|
|
|
//
|
|
|
|
|
UpdateStat(intervalSec uint32)
|
|
|
|
|
|
|
|
|
|
// GetStat
|
|
|
|
|
//
|
|
|
|
|
// 获取session状态
|
|
|
|
|
//
|
|
|
|
|
// @return 注意,结构体中的`BitrateKbits`的值由最近一次`func UpdateStat`调用计算决定,其他值为当前最新值
|
|
|
|
|
//
|
|
|
|
|
GetStat() StatSession
|
|
|
|
|
|
|
|
|
|
// IsAlive
|
|
|
|
|
//
|
|
|
|
|
// 周期性调用该函数,判断是否有读取、写入数据
|
|
|
|
|
// 注意,判断的依据是,距离上次调用该函数的时间间隔内,是否有读取、写入数据
|
|
|
|
|
// 注意,不活跃,并一定是链路或网络有问题,也可能是业务层没有写入数据
|
|
|
|
|
//
|
|
|
|
|
// @return readAlive 读取是否获取
|
|
|
|
|
// @return writeAlive 写入是否活跃
|
|
|
|
|
//
|
|
|
|
|
IsAlive() (readAlive, writeAlive bool)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ISessionUrlContext 获取和流地址相关的信息
|
|
|
|
|
//
|
|
|
|
|
// 调用约束:对于Client类型的Session,调用Start函数并返回成功后才能调用,否则行为未定义
|
|
|
|
|
type ISessionUrlContext interface {
|
|
|
|
|
Url() string
|
|
|
|
|
AppName() string
|
|
|
|
|
StreamName() string
|
|
|
|
|
RawQuery() string // 参数,也即 url param
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type IObject interface {
|
|
|
|
|
// UniqueKey
|
|
|
|
|
//
|
|
|
|
|
// 对象的全局唯一标识
|
|
|
|
|
//
|
|
|
|
|
UniqueKey() string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO chef: rtmp.ClientSession修改为BaseClientSession更好些
|
|
|
|
|
|
|
|
|
|
// TODO(chef): [refactor] 整理 subsession 接口部分 IsFresh 和 ShouldWaitVideoKeyFrame
|
|
|
|
|
|
|
|
|
|
// ----- group中,session Dispose表现记录 -----
|
|
|
|
|
//
|
|
|
|
|
// Dispose结束后回调OnDel:
|
|
|
|
|
// rtmp.ServerSession(包含pub和sub) 1
|
|
|
|
|
// rtsp.PubSession和rtsp.SubSession 1
|
|
|
|
|
// rtmp.PullSession 2
|
|
|
|
|
// httpflv.SubSession 3
|
|
|
|
|
// httpts.SubSession 3
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// 情况1: 协议正常走完回调OnAdd,在自身server的RunLoop结束后,回调OnDel
|
|
|
|
|
// 情况2: 在group中pull阻塞结束后,手动回调OnDel
|
|
|
|
|
// 情况3: 在logic中sub RunLoop结束后,手动回调OnDel
|
|
|
|
|
|
|
|
|
|
// TODO(chef): 整理所有Server类型Session的生命周期管理
|
|
|
|
|
// -
|
|
|
|
|
// - rtmp没有独立的Pub、Sub Session结构体类型,而是直接使用ServerSession
|
|
|
|
|
// - write失败,需要反应到loop来
|
|
|
|
|
// - rtsp是否也应该上层使用Command作为代理,避免生命周期管理混乱
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
// ISessionUrlContext 实际测试
|
|
|
|
|
//
|
|
|
|
|
// | | 实际url | Url() | AppName, StreamName, RawQuery |
|
|
|
|
|
// | - | - | - | - |
|
|
|
|
|
// | rtmp pub推流 | rtmp://127.0.0.1:1935/live/test110 | 同实际url | live, test110, |
|
|
|
|
|
// | | rtmp://127.0.0.1:1935/a/b/c/d/test110?p1=1&p2=2 | 同实际url | a/b, c/d/test110, p1=1&p2=2 |
|
|
|
|
|
// | rtsp pub推流 | rtsp://localhost:5544/live/test110 | 同实际url | live, test110, |
|
|
|
|
|
// | rtsp pub推流 | rtsp://localhost:5544/a/b/c/d/test110?p1=1&p2=2 | 同实际url | a/b/c/d, test110, p1=1&p2=2 |
|
|
|
|
|
// | httpflv sub拉流 | http://127.0.0.1:8080/live/test110.flv | 同实际url | live, test110, |
|
|
|
|
|
// | | http://127.0.0.1:8080/a/b/c/d/test110.flv?p1=1&p2=2 | 同实际url | a/b/c/d, test110, p1=1&p2=2 |
|
|
|
|
|
// | rtmp sub拉流 | 同rtmp pub | . | . |
|
|
|
|
|
// | rtsp sub拉流 | 同rtsp pub | . | . |
|
|
|
|
|
// | httpts sub拉流 | 同httpflv sub,只是末尾的.flv换成.ts,不再赘述 | . | . |
|
|
|
|
|
|
|
|
|
|
// IsUseClosedConnectionError 当connection处于这些情况时,就不需要再Close了
|
|
|
|
|
// TODO(chef): 临时放这
|
|
|
|
|
// TODO(chef): 目前暂时没有使用,因为connection支持多次调用Close
|
|
|
|
|
//
|
|
|
|
|
//func IsUseClosedConnectionError(err error) bool {
|
|
|
|
|
// if err == io.EOF || (err != nil && strings.Contains(err.Error(), "use of closed network connection")) {
|
|
|
|
|
// return true
|
|
|
|
|
// }
|
|
|
|
|
// return false
|
|
|
|
|
//}
|