func init() { //初始化反射型弹幕机
bb, err := file.New("config/config_auto_reply.json", 0, true).ReadAll(100, 1<<16)
- if err != nil {
+ if !errors.Is(err, io.EOF) {
return
}
var buf map[string]interface{}
import (
"encoding/json"
+ "errors"
+ "io"
c "github.com/qydysky/bili_danmu/CV"
file "github.com/qydysky/part/file"
func init() {
{ //加载不需要的消息
bb, err := file.New("config/config_disable_msg.json", 0, true).ReadAll(100, 1<<16)
- if err != nil {
+ if !errors.Is(err, io.EOF) {
return
}
var buf map[string]interface{}
import (
"bytes"
"errors"
+ "fmt"
F "github.com/qydysky/bili_danmu/F"
)
traks map[int]trak
}
-func (t *Fmp4Decoder) Init_fmp4(buf []byte) error {
- var cu int
+func (t *Fmp4Decoder) Init_fmp4(buf []byte) ([]byte, error) {
+ var (
+ cu int
+ lastMoovI int
+ lastMoovE int
+ )
+
+ //ftyp
+ ftypI := bytes.Index(buf[cu:], []byte("ftyp"))
+ if ftypI == -1 {
+ return nil, errors.New("未找到ftyp包")
+ }
+ ftypI = cu + ftypI - 4
+ ftypE := ftypI + int(F.Btoi(buf, ftypI, 4))
+ if ftypE > len(buf) {
+ return nil, errors.New("ftyp包破损")
+ }
+ cu = ftypI
+
for cu < len(buf) {
//moov
moovI := bytes.Index(buf[cu:], []byte("moov"))
moovI = cu + moovI - 4
moovE := moovI + int(F.Btoi(buf, moovI, 4))
if moovE > len(buf) {
- return errors.New("moov包破损")
+ return nil, errors.New("moov包破损")
}
cu = moovI
+ lastMoovI = moovI
+ lastMoovE = moovE
+
for cu < moovE {
//trak
trakI := bytes.Index(buf[cu:], []byte("trak"))
trakI = cu + trakI - 4
trakE := trakI + int(F.Btoi(buf, trakI, 4))
if trakE > moovE {
- return errors.New("trak包破损")
+ return nil, errors.New("trak包破损")
}
cu = trakI
//tkhd
tkhdI := bytes.Index(buf[cu:], []byte("tkhd"))
if tkhdI == -1 {
- return errors.New("未找到tkhd包")
+ return nil, errors.New("未找到tkhd包")
}
tkhdI = cu + tkhdI - 4
tkhdE := tkhdI + int(F.Btoi(buf, tkhdI, 4))
if tkhdE > trakE {
- return errors.New("tkhd包破损")
+ return nil, errors.New("tkhd包破损")
}
cu = tkhdI
//mdia
mdiaI := bytes.Index(buf[cu:], []byte("mdia"))
if mdiaI == -1 {
- return errors.New("未找到mdia包")
+ return nil, errors.New("未找到mdia包")
}
mdiaI = cu + mdiaI - 4
mdiaE := mdiaI + int(F.Btoi(buf, mdiaI, 4))
if mdiaE > trakE {
- return errors.New("mdia包破损")
+ return nil, errors.New("mdia包破损")
}
cu = mdiaI
//mdhd
mdhdI := bytes.Index(buf[cu:], []byte("mdhd"))
if mdhdI == -1 {
- return errors.New("未找到mdhd包")
+ return nil, errors.New("未找到mdhd包")
}
mdhdI = cu + mdhdI - 4
mdhdE := mdhdI + int(F.Btoi(buf, mdhdI, 4))
if mdhdE > mdiaE {
- return errors.New("mdhd包破损")
+ return nil, errors.New("mdhd包破损")
}
cu = mdhdI
//hdlr
hdlrI := bytes.Index(buf[cu:], []byte("hdlr"))
if hdlrI == -1 {
- return errors.New("未找到hdlr包")
+ return nil, errors.New("未找到hdlr包")
}
hdlrI = cu + hdlrI - 4
hdlrE := hdlrI + int(F.Btoi(buf, hdlrI, 4))
if hdlrE > mdiaE {
- return errors.New("hdlr包破损")
+ return nil, errors.New("hdlr包破损")
}
cu = hdlrI
}
}
if len(t.traks) == 0 {
- return errors.New("未找到trak包")
+ return nil, errors.New("未找到trak包")
}
- return nil
+ return append(buf[ftypI:ftypE], buf[lastMoovI:lastMoovE]...), nil
}
func (t *Fmp4Decoder) Seach_stream_fmp4(buf []byte) (keyframes [][]byte, last_avilable_offset int, err error) {
cu int
haveKeyframe bool
keyframe []byte
+ frameTime int
)
for cu < len(buf) {
if !iskeyFrame && buf[trunI+20] == byte(0x02) {
iskeyFrame = true
}
+
+ if track.handlerType == 'v' {
+ if timeStamp < frameTime {
+ err = fmt.Errorf("时间戳异常: (current)%d < (last)%d", timeStamp, frameTime)
+ break
+ }
+ frameTime = timeStamp
+ }
}
if err != nil {
if cu == 0 {
err = errors.New("未找到moof")
}
- if len(buf) > 1024*1024*20 {
+ if len(buf)-last_avilable_offset > 1024*1024*20 {
err = errors.New("buf超过20M")
}
return
--- /dev/null
+package reply
+
+// 线程不安全的[]byte操作
+// 需保证append到getSlice的[]byte已被使用之间,对象[]byte未改动
+type bufI struct {
+ b []int
+ e []int
+ size int
+ buf []byte
+ useDirect bool //true:直接使用源[]byte,仅适用于连续的append
+ useBufPool bool //true:使用内部buf,当getSlice调用时,上次getSlice输出[]byte失效
+ //useDirect、useBufPool都为false:每次都返回新创建的[]byte
+}
+
+func (t *bufI) reset() {
+ t.b = []int{}
+ t.e = []int{}
+ t.size = 0
+}
+
+func (t *bufI) append(b, e int) {
+ if len(t.e) > 0 && t.e[len(t.e)-1] == b {
+ t.e[len(t.e)-1] = e
+ } else {
+ t.b = append(t.b, b)
+ t.e = append(t.e, e)
+ }
+ t.size += e - b
+}
+
+func (t *bufI) getSlice(buf []byte) []byte {
+ if t.useDirect && len(t.b) == 1 {
+ return buf[t.b[0]:t.e[0]]
+ } else if t.useBufPool {
+ if len(t.buf) == 0 {
+ t.buf = make([]byte, t.size)
+ } else if len(t.buf) < t.size {
+ t.buf = append(t.buf, make([]byte, t.size-len(t.buf))...)
+ } else if diff := len(t.buf) - t.size; diff > 0 {
+ t.buf = t.buf[:t.size+diff/2]
+ }
+ i := 0
+ for k, bi := range t.b {
+ i += copy(t.buf[i:], buf[bi:t.e[k]])
+ }
+ return t.buf[:i]
+ } else {
+ var b = make([]byte, t.size)
+ if len(t.b) == 1 {
+ copy(b, buf[t.b[0]:t.e[0]])
+ } else {
+ i := 0
+ for k, bi := range t.b {
+ i += copy(b[i:], buf[bi:t.e[k]])
+ }
+ }
+ return b
+ }
+}
t.log.L(`I: `, `flv下载开始`)
- if err := r.Reqf(reqf.Rval{
+ r.Reqf(reqf.Rval{
Url: surl.String(),
SaveToPipeWriter: rw,
NoResponse: true,
+ Async: true,
Proxy: t.common.Proxy,
Header: map[string]string{
`Host`: surl.Host,
`Referer`: "https://live.bilibili.com/",
`Cookie`: reqf.Map_2_Cookies_String(CookieM),
},
- }); err != nil && !errors.Is(err, io.EOF) {
+ })
+ if e := r.Wait(); e != nil && !errors.Is(err, io.EOF) {
if reqf.IsCancel(err) {
t.log.L(`I: `, `flv下载停止`)
} else if !reqf.IsTimeout(err) {
Max: 3,
}
- var out *os.File
+ var out *file.File
if t.config.save_as_mp4 {
- var err error
- out, err = os.Create(t.Current_save_path + `0.mp4`)
- if err != nil {
- out.Close()
- }
+ out = file.New(t.Current_save_path+`0.mp4`, 0, false)
defer out.Close()
}
// 下载切片
for _, v := range download_seq {
+
+ // 已下载但还未移除的切片
+ if v.status == 2 {
+ download_limit.Block()
+ download_limit.UnBlock()
+ continue
+ }
+
go func(link *m4s_link_item, path string) {
defer download_limit.UnBlock()
download_limit.Block()
- // 已下载但还未移除的切片
- if link.status == 2 {
- return
- }
-
link.status = 1 // 设置切片状态为正在下载
link.tryDownCount += 1
{
for _, v := range download_seq {
if v.status == 2 {
- download_seq = download_seq[1:]
- buf = append(buf, v.data...)
-
if strings.Contains(v.Base, `h`) {
- if e := fmp4Decoder.Init_fmp4(v.data); e != nil {
- t.log.L(`E: `, e)
+ if header, e := fmp4Decoder.Init_fmp4(v.data); e != nil {
+ t.log.L(`E: `, e, ` 重试!`)
+ v.status = 3
+ break
} else {
for _, trak := range fmp4Decoder.traks {
t.log.L(`T: `, "找到trak:", string(trak.handlerType), trak.trackID, trak.timescale)
}
+ t.first_buf = header
+ if out != nil {
+ out.Write(t.first_buf, true)
+ out.Sync()
+ }
}
- t.first_buf = v.data
- if t.config.save_as_mp4 {
- out.Write(v.data)
- }
+ download_seq = download_seq[1:]
+ continue
+ } else if t.first_buf == nil {
+ download_seq = download_seq[1:]
continue
}
- fmp4KeyFrames, last_avilable_offset, e := fmp4Decoder.Seach_stream_fmp4(buf)
- if e != nil {
- t.log.L(`E: `, e)
+ download_seq = download_seq[1:]
+ buf = append(buf, v.data...)
+
+ fmp4KeyFrames, last_avilable_offset, err := fmp4Decoder.Seach_stream_fmp4(buf)
+ if err != nil {
+ t.log.L(`E: `, err)
+ if err.Error() == "未初始化traks" {
+ e = err
+ return
+ }
//丢弃所有数据
last_avilable_offset = len(buf)
}
for _, fmp4KeyFrame := range fmp4KeyFrames {
t.bootBufPush(fmp4KeyFrame)
t.Stream_msg.Push_tag(`data`, fmp4KeyFrame)
- if t.config.save_as_mp4 {
- out.Write(fmp4KeyFrame)
+ if out != nil {
+ out.Write(fmp4KeyFrame, true)
+ out.Sync()
}
}
break
}
}
- if t.config.save_as_mp4 {
- out.Sync()
- }
}
// 停止录制
"encoding/json"
"errors"
"fmt"
+ "io"
"net/url"
"os/exec"
"strings"
}
bb, err := file.New("config/config_tts.json", 0, true).ReadAll(100, 1<<16)
- if err != nil {
+ if !errors.Is(err, io.EOF) {
return
}
var buf map[string]interface{}
import (
"encoding/json"
+ "errors"
+ "io"
"strconv"
c "github.com/qydysky/bili_danmu/CV"
// 初始化表情代码
func init() {
bb, err := file.New("config/config_danmu_official.json", 0, true).ReadAll(1000, 1<<16)
- if err != nil {
+ if !errors.Is(err, io.EOF) {
return
}
var buf map[string]interface{}
"help":"禁用指定消息类型,false将忽略此类消息,true将显示此类消息的json纯文本",
"PK_BATTLE_PRE":false,
"LIVE_INTERACTIVE_GAME":false,
- "room_admin_entrance":false
+ "room_admin_entrance":false,
+ "LIKE_INFO_V3_CLICK":false
}
\ No newline at end of file
github.com/mdp/qrterminal/v3 v3.0.0 // indirect
github.com/miekg/dns v1.1.50 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
- github.com/qydysky/part v0.18.19 // indirect
+ github.com/qydysky/part v0.19.2 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
replace (
github.com/gotk3/gotk3 v0.5.2 => github.com/qydysky/gotk3 v0.0.0-20210103171910-327affdaaa80
github.com/qydysky/bili_danmu => ../
-//github.com/qydysky/part => ../../part
)
github.com/qydysky/part v0.10.18/go.mod h1:BG0tulTKW58jSkC0EZ0MrxDHe+gkPULfGNzksiGCayw=
github.com/qydysky/part v0.18.19 h1:s4CL+ljiGhySX633x0ohN6NA2c7T/BWg5YVpjm4xo30=
github.com/qydysky/part v0.18.19/go.mod h1:BG0tulTKW58jSkC0EZ0MrxDHe+gkPULfGNzksiGCayw=
+github.com/qydysky/part v0.19.0 h1:H0esXkedZKrYdEEACC76O+DL9TZDkApow67RYLSyT0U=
+github.com/qydysky/part v0.19.0/go.mod h1:BG0tulTKW58jSkC0EZ0MrxDHe+gkPULfGNzksiGCayw=
+github.com/qydysky/part v0.19.1 h1:3AdjJtm5Q594Jd/My1E1wQIiRPXgmS+KM1DJhofLr1M=
+github.com/qydysky/part v0.19.1/go.mod h1:BG0tulTKW58jSkC0EZ0MrxDHe+gkPULfGNzksiGCayw=
+github.com/qydysky/part v0.19.2 h1:peR1UBrBgnjB63nv5F100oJ72hRoJnn8cuZPXDiGZOM=
+github.com/qydysky/part v0.19.2/go.mod h1:BG0tulTKW58jSkC0EZ0MrxDHe+gkPULfGNzksiGCayw=
github.com/shirou/gopsutil v3.20.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil v3.21.5+incompatible h1:OloQyEerMi7JUrXiNzy8wQ5XN+baemxSl12QgIzt0jc=
github.com/shirou/gopsutil v3.21.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/gofrs/uuid v4.3.0+incompatible
github.com/gotk3/gotk3 v0.6.1
github.com/mdp/qrterminal/v3 v3.0.0
- github.com/qydysky/part v0.18.19
+ github.com/qydysky/part v0.19.2
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
golang.org/x/text v0.3.8
github.com/qydysky/part v0.10.18/go.mod h1:BG0tulTKW58jSkC0EZ0MrxDHe+gkPULfGNzksiGCayw=
github.com/qydysky/part v0.18.19 h1:s4CL+ljiGhySX633x0ohN6NA2c7T/BWg5YVpjm4xo30=
github.com/qydysky/part v0.18.19/go.mod h1:BG0tulTKW58jSkC0EZ0MrxDHe+gkPULfGNzksiGCayw=
+github.com/qydysky/part v0.19.0 h1:H0esXkedZKrYdEEACC76O+DL9TZDkApow67RYLSyT0U=
+github.com/qydysky/part v0.19.0/go.mod h1:BG0tulTKW58jSkC0EZ0MrxDHe+gkPULfGNzksiGCayw=
+github.com/qydysky/part v0.19.1 h1:3AdjJtm5Q594Jd/My1E1wQIiRPXgmS+KM1DJhofLr1M=
+github.com/qydysky/part v0.19.1/go.mod h1:BG0tulTKW58jSkC0EZ0MrxDHe+gkPULfGNzksiGCayw=
+github.com/qydysky/part v0.19.2 h1:peR1UBrBgnjB63nv5F100oJ72hRoJnn8cuZPXDiGZOM=
+github.com/qydysky/part v0.19.2/go.mod h1:BG0tulTKW58jSkC0EZ0MrxDHe+gkPULfGNzksiGCayw=
github.com/shirou/gopsutil v3.20.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=