}
type Common struct {
- PID int `json:"pid"` //进程id
- Version string `json:"version"` //版本
- Uid int `json:"-"` //client uid
- Live []LiveQn `json:"-"` //直播流链接
- Live_qn int `json:"liveQn"` //当前直播流质量
- Live_want_qn int `json:"-"` //期望直播流质量
- Roomid int `json:"roomid"` //房间ID
- Cookie syncmap.Map `json:"-"` //Cookie
- Title string `json:"title"` //直播标题
- Uname string `json:"uname"` //主播名
- UpUid int `json:"upUid"` //主播uid
- Rev float64 `json:"rev"` //营收
- Renqi int `json:"renqi"` //人气
- Watched int `json:"watched"` //观看人数
- OnlineNum int `json:"onlineNum"` //在线人数
- GuardNum int `json:"guardNum"` //舰长数
- ParentAreaID int `json:"parentAreaID"` //父分区
- AreaID int `json:"areaID"` //子分区
- Locked bool `json:"locked"` //直播间封禁
- Note string `json:"note"` //分区排行
- Live_Start_Time time.Time `json:"liveStartTime"` //直播开始时间
- Liveing bool `json:"liveing"` //是否在直播
- Wearing_FansMedal int `json:"-"` //当前佩戴的粉丝牌
- Token string `json:"-"` //弹幕钥
- WSURL []string `json:"-"` //弹幕链接
- LiveBuvidUpdated time.Time `json:"-"` //LIVE_BUVID更新时间
- Stream_url *url.URL `json:"-"` //直播Web服务
- Proxy string `json:"-"` //全局代理
- AcceptQn map[int]string `json:"-"` //允许的直播流质量
- Qn map[int]string `json:"-"` //全部直播流质量
- StreamType StreamType `json:"streamType"` //当前直播流类型
- AllStreamType map[string]StreamType `json:"-"` //直播流类型
- K_v syncmap.Map `json:"-"` //配置文件
- Log *log.Log_interface `json:"-"` //日志
- Danmu_Main_mq *mq.Msgq `json:"-"` //消息
- ReqPool *pool.Buf[reqf.Req] `json:"-"` //请求池
- SerF *web.WebPath `json:"-"` //web服务处理
- SerLimit *web.Limits `json:"-"` //Web服务连接限制
- StartT time.Time `json:"startT"` //启动时间
- Cache syncmap.Map `json:"-"` //缓存
+ PID int `json:"pid"` //进程id
+ Version string `json:"version"` //版本
+ Uid int `json:"-"` //client uid
+ Live []LiveQn `json:"-"` //直播流链接
+ Live_qn int `json:"liveQn"` //当前直播流质量
+ Live_want_qn int `json:"-"` //期望直播流质量
+ Roomid int `json:"roomid"` //房间ID
+ Cookie syncmap.Map `json:"-"` //Cookie
+ Title string `json:"title"` //直播标题
+ Uname string `json:"uname"` //主播名
+ UpUid int `json:"upUid"` //主播uid
+ Rev float64 `json:"rev"` //营收
+ Renqi int `json:"renqi"` //人气
+ Watched int `json:"watched"` //观看人数
+ OnlineNum int `json:"onlineNum"` //在线人数
+ GuardNum int `json:"guardNum"` //舰长数
+ ParentAreaID int `json:"parentAreaID"` //父分区
+ AreaID int `json:"areaID"` //子分区
+ Locked bool `json:"locked"` //直播间封禁
+ Note string `json:"note"` //分区排行
+ Live_Start_Time time.Time `json:"liveStartTime"` //直播开始时间
+ Liveing bool `json:"liveing"` //是否在直播
+ Wearing_FansMedal int `json:"-"` //当前佩戴的粉丝牌
+ Token string `json:"-"` //弹幕钥
+ WSURL []string `json:"-"` //弹幕链接
+ LiveBuvidUpdated time.Time `json:"-"` //LIVE_BUVID更新时间
+ Stream_url *url.URL `json:"-"` //直播Web服务
+ Proxy string `json:"-"` //全局代理
+ AcceptQn map[int]string `json:"-"` //允许的直播流质量
+ Qn map[int]string `json:"-"` //全部直播流质量
+ // StreamType StreamType `json:"streamType"` //当前直播流类型
+ AllStreamType map[string]StreamType `json:"-"` //直播流类型
+ K_v syncmap.Map `json:"-"` //配置文件
+ Log *log.Log_interface `json:"-"` //日志
+ Danmu_Main_mq *mq.Msgq `json:"-"` //消息
+ ReqPool *pool.Buf[reqf.Req] `json:"-"` //请求池
+ SerF *web.WebPath `json:"-"` //web服务处理
+ SerLimit *web.Limits `json:"-"` //Web服务连接限制
+ StartT time.Time `json:"startT"` //启动时间
+ Cache syncmap.Map `json:"-"` //缓存
}
type LiveQn struct {
Proxy: t.Proxy,
AcceptQn: syncmap.Copy(t.AcceptQn),
Qn: syncmap.Copy(t.Qn),
- StreamType: t.StreamType,
- AllStreamType: syncmap.Copy(t.AllStreamType),
- K_v: t.K_v.Copy(),
- Log: t.Log,
- Danmu_Main_mq: t.Danmu_Main_mq,
- ReqPool: t.ReqPool,
- SerF: t.SerF,
- SerLimit: t.SerLimit,
- StartT: t.StartT,
- Cache: t.Cache.Copy(),
+ // StreamType: t.StreamType,
+ AllStreamType: syncmap.Copy(t.AllStreamType),
+ K_v: t.K_v.Copy(),
+ Log: t.Log,
+ Danmu_Main_mq: t.Danmu_Main_mq,
+ ReqPool: t.ReqPool,
+ SerF: t.SerF,
+ SerLimit: t.SerLimit,
+ StartT: t.StartT,
+ Cache: t.Cache.Copy(),
}
return &c
// 配置直播流类型
if val, exist := t.K_v.Load("直播流类型"); exist {
- if st, ok := t.AllStreamType[val.(string)]; ok {
- t.StreamType = st
- } else {
+ if _, ok := t.AllStreamType[val.(string)]; !ok {
panic("未找到设定类型" + val.(string))
}
}
if !t.Liveing {
t.Live_qn = 0
t.AcceptQn = t.Qn
- clear(t.Live)
+ t.Live = t.Live[:0]
return
}
// 配置直播流
func (t *GetFunc) configStreamType(sts []J.StreamType) {
- var chosenType c.StreamType
+ var (
+ wantTypes []c.StreamType
+ chosen int = -1
+ )
defer func() {
apilog := apilog.Base_add(`configStreamType`)
+ if chosen == -1 {
+ apilog.L(`E: `, `未能选择到流`)
+ return
+ }
if _, ok := t.Qn[t.Live_qn]; !ok {
apilog.L(`W: `, `未知的清晰度`, t.Live_qn)
}
- apilog.L(`T: `, fmt.Sprintf("使用直播流 %s %s %s", t.Qn[t.Live_qn], chosenType.Format_name, chosenType.Codec_name))
+ apilog.L(`T: `, fmt.Sprintf("使用 %d 条直播流 %s %s %s", len(t.Live), t.Qn[t.Live_qn], wantTypes[chosen].Format_name, wantTypes[chosen].Codec_name))
}()
+ // 期望类型
if v, ok := t.Common.K_v.LoadV(`直播流类型`).(string); ok {
if st, ok := t.AllStreamType[v]; ok {
- t.StreamType = st
- }
- }
-
- // 查找配置类型是否存在
- for _, v := range sts {
- if v.ProtocolName != t.StreamType.Protocol_name {
- continue
- }
-
- for _, v := range v.Format {
- if v.FormatName != t.StreamType.Format_name {
- continue
- }
-
- for _, v := range v.Codec {
- if v.CodecName != t.StreamType.Codec_name {
- continue
- }
-
- chosenType = t.StreamType
- //当前直播流质量
- t.Live_qn = v.CurrentQn
- if t.Live_want_qn == 0 {
- t.Live_want_qn = v.CurrentQn
- }
- //允许的清晰度
- {
- var tmp = make(map[int]string)
- for _, v := range v.AcceptQn {
- if s, ok := t.Qn[v]; ok {
- tmp[v] = s
- }
- }
- t.AcceptQn = tmp
- }
- //直播流链接
- clear(t.Live)
- for _, v1 := range v.URLInfo {
- item := c.LiveQn{
- Url: v1.Host + v.BaseURL + v1.Extra,
- }
-
- if query, e := url.ParseQuery(v1.Extra); e == nil {
- if expires, e := strconv.Atoi(query.Get("expires")); e == nil {
- item.Expires = expires
- }
- }
-
- t.Live = append(t.Live, item)
- }
-
- return
- }
+ wantTypes = append(wantTypes, st)
}
}
+ // 默认类型
+ wantTypes = append(wantTypes, t.AllStreamType[`fmp4`], t.AllStreamType[`flv`])
- apilog.Base_add(`configStreamType`).L(`W: `, "未找到配置的直播流类型,使用默认flv、fmp4")
-
- // 默认使用fmp4、flv
- for _, streamType := range []c.StreamType{
- t.AllStreamType[`fmp4`],
- t.AllStreamType[`flv`],
- } {
+ t.Live = t.Live[:0]
+ for k, streamType := range wantTypes {
for _, v := range sts {
if v.ProtocolName != streamType.Protocol_name {
continue
continue
}
- chosenType = streamType
+ chosen = k
//当前直播流质量
t.Live_qn = v.CurrentQn
if t.Live_want_qn == 0 {
t.AcceptQn = tmp
}
//直播流链接
- clear(t.Live)
for _, v1 := range v.URLInfo {
item := c.LiveQn{
Url: v1.Host + v.BaseURL + v1.Extra,
t.Live = append(t.Live, item)
}
+
+ // 已选定并设置好参数 退出
+ return
}
}
}
if !t.Liveing {
t.Live_qn = 0
t.AcceptQn = t.Qn
- clear(t.Live)
+ t.Live = t.Live[:0]
return
}
if !t.Liveing {
t.Live_qn = 0
t.AcceptQn = t.Qn
- clear(t.Live)
+ t.Live = t.Live[:0]
return
}
return
}
- if e := save_cookie(r.Response.Cookies()); e != nil {
+ if e := save_cookie(r.Response.Cookies()); e != nil && !errors.Is(e, ErrNoCookiesSave) {
apilog.L(`E: `, e)
}
}
return
}
+var ErrNoCookiesSave = errors.New("ErrNoCookiesSave")
+
func save_cookie(Cookies []*http.Cookie) error {
if len(Cookies) == 0 {
- return errors.New("no cookie")
+ return ErrNoCookiesSave
}
for k, v := range reqf.Cookies_List_2_Map(Cookies) {
// if sign != 0x00 {
// fmt.Printf("front_buf error:%x\n", sign)
// }
- clear(front_buf)
+ front_buf = front_buf[:0]
}
if bufl := keyframe.Size(); confirm_num != bufl {
_ = keyframe.RemoveBack(bufl - confirm_num)
err = E
if reSyncI := bytes.Index(buf[cu:], []byte(reSyncboxName)); reSyncI != -1 {
cu += reSyncI - 4
- clear(m)
+ m = m[:0]
continue
}
err = errors.New(E.Error() + " > 未能reSync")
defer t.reqPool.Put(r)
for _, v := range t.common.Live {
if nomcdn && strings.Contains(v.Url, ".mcdn.") {
- t.common.Live = t.common.Live[1:]
+ v.Disable(time.Now().Add(time.Hour * 100))
continue
}
Timeout: 5 * 1000,
JustResponseCode: true,
}); e != nil {
- t.log.L(`W: `, e)
+ t.log.L(`W: `, F.ParseHost(v.Url), e)
+ v.DisableAuto()
+ continue
}
if r.Response == nil {
- t.log.L(`W: `, `live响应错误`)
- t.common.Live = t.common.Live[1:]
+ t.log.L(`W: `, `live响应错误`, F.ParseHost(v.Url))
+ v.DisableAuto()
continue
} else if r.Response.StatusCode&200 != 200 {
- t.log.L(`W: `, `live响应错误`, r.Response.Status)
- t.common.Live = t.common.Live[1:]
+ t.log.L(`W: `, `live响应错误`, F.ParseHost(v.Url), r.Response.Status)
+ v.DisableAuto()
continue
}
t.log.L(`I: `, `使用流服务器`, F.ParseHost(v.Url))
}
- return len(t.common.Live) != 0
+ return t.common.ValidLive() != nil
}
func (t *M4SStream) fetchParseM3U8(fmp4ListUpdateTo float64) (m4s_links []*m4s_link_item, m3u8_addon []byte, e error) {
if index := bytes.Index(m3u8_addon, []byte(m4s_link.Base)); index != -1 {
index += len([]byte(m4s_link.Base))
if index == len(m3u8_addon) {
- clear(m3u8_addon)
+ m3u8_addon = m3u8_addon[:0]
} else {
m3u8_addon = m3u8_addon[index+1:]
}
t.frameCount = 0
if s, ok := t.common.K_v.LoadV("直播Web服务路径").(string); ok && s != "" {
- t.log.L(`I: `, "Web服务地址:", t.common.Stream_url.String()+s)
+ t.log.L(`I: `, "Web服务地址", t.common.Stream_url.String()+s)
}
// 录制回调
buff.Reset()
}()
- t.log.L(`I: `, `flv下载开始`)
+ t.log.L(`I: `, `flv下载开始`, F.ParseHost(surl.String()))
_ = r.Reqf(reqf.Rval{
Ctx: cancelC,
})
if err := r.Wait(); err != nil && !errors.Is(err, io.EOF) {
if reqf.IsCancel(err) {
- t.log.L(`I: `, `flv下载停止`)
+ t.log.L(`I: `, `flv下载停止`, F.ParseHost(surl.String()))
return
} else if err != nil && !reqf.IsTimeout(err) {
e = err
- t.log.L(`E: `, `flv下载失败:`, err)
+ t.log.L(`E: `, `flv下载失败:`, F.ParseHost(surl.String()), err)
+ } else {
+ t.log.L(`E: `, `flv下载超时`, F.ParseHost(surl.String()))
}
} else if err := errCtx.Get(); err != nil && strings.HasPrefix(err.Error(), "[decoder]") {
e = err