From fdb4c4b85ec757a210fae9984d40e56cc6427a95 Mon Sep 17 00:00:00 2001 From: qydysky Date: Sun, 27 Aug 2023 17:59:15 +0800 Subject: [PATCH] =?utf8?q?Add=20mp4=E6=97=B6=E9=97=B4=E6=88=B3=E4=BF=AE?= =?utf8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- F/api.go | 4 +- Reply/F.go | 17 +- Reply/F/comp.go | 2 + Reply/F/danmuXml/danmuXml.go | 3 +- .../F/reSetMp4TimeStamp/reSetMp4TimeStamp.go | 252 ++++++++++++++++++ go.mod | 2 +- go.sum | 4 +- 7 files changed, 271 insertions(+), 13 deletions(-) create mode 100644 Reply/F/reSetMp4TimeStamp/reSetMp4TimeStamp.go diff --git a/F/api.go b/F/api.go index fd7898c..92e6ee6 100644 --- a/F/api.go +++ b/F/api.go @@ -13,7 +13,6 @@ import ( "sync" "time" - "github.com/dustin/go-humanize" c "github.com/qydysky/bili_danmu/CV" J "github.com/qydysky/bili_danmu/Json" "github.com/skratchdot/open-golang/open" @@ -23,6 +22,7 @@ import ( file "github.com/qydysky/part/file" funcCtrl "github.com/qydysky/part/funcCtrl" g "github.com/qydysky/part/get" + pio "github.com/qydysky/part/io" limit "github.com/qydysky/part/limit" reqf "github.com/qydysky/part/reqf" @@ -1372,7 +1372,7 @@ func (t *GetFunc) Get_cookie() (missKey []string) { if c.DefaultHttpCheck(t.Common, w, r, http.MethodGet) { return } - _ = file.New("qr.png", 0, true).CopyToIoWriter(w, humanize.MByte, true) + _ = file.New("qr.png", 0, true).CopyToIoWriter(w, pio.CopyConfig{}) }) if t.K_v.LoadV(`扫码登录自动打开标签页`).(bool) { _ = open.Run(`http://127.0.0.1:` + t.Stream_url.Port() + scanPath) diff --git a/Reply/F.go b/Reply/F.go index bab7297..2dab9e5 100644 --- a/Reply/F.go +++ b/Reply/F.go @@ -38,6 +38,7 @@ import ( p "github.com/qydysky/part" comp "github.com/qydysky/part/component" file "github.com/qydysky/part/file" + pio "github.com/qydysky/part/io" limit "github.com/qydysky/part/limit" msgq "github.com/qydysky/part/msgq" psync "github.com/qydysky/part/sync" @@ -942,7 +943,7 @@ func (t *saveToJson) Init() { }, `stop`: func(_ []byte) (disable bool) { f := file.New(path, -1, false) - _ = f.Seed(-1, 2) + _ = f.SeekIndex(-1, file.AtEnd) _, _ = f.Write([]byte("]"), true) f.Close() return true @@ -1385,7 +1386,7 @@ func init() { //header w.Header().Set("Access-Control-Allow-Credentials", "true") w.Header().Set("Access-Control-Allow-Headers", "*") - w.Header().Set("Access-Control-Allow-Methods", "GET") + w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS") w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Connection", "keep-alive") w.Header().Set("Content-Transfer-Encoding", "binary") @@ -1475,15 +1476,17 @@ func init() { flog.L(`W: `, e) return } else { - var totalSec int64 = 10 + var count int64 = 5 allSize := fi.Size() - targetC := int64(rangeHeaderNum) + int64(speed)*totalSec + targetC := int64(rangeHeaderNum) + int64(speed)*count if allSize < targetC { targetC = allSize } - w.Header().Add(`Content-Range`, fmt.Sprintf("bytes %d-%d/%d", rangeHeaderNum, targetC, allSize)) + + w.Header().Add(`Content-Range`, fmt.Sprintf("bytes %d-%d/%d", rangeHeaderNum, targetC-1, allSize)) w.WriteHeader(http.StatusPartialContent) - if e := f.CopyToIoWriterUntil(pweb.WithFlush(w), int64(speed), totalSec, true); e != nil { + + if e := f.CopyToIoWriter(w, pio.CopyConfig{MaxByte: uint64(targetC - int64(rangeHeaderNum)), BytePerSec: speed}); e != nil { flog.L(`E: `, e) } } @@ -1660,7 +1663,7 @@ func init() { } } - if e := file.New(v+"0.xml", 0, true).CopyToIoWriter(w, 0, false); e != nil { + if e := file.New(v+"0.xml", 0, true).CopyToIoWriter(w, pio.CopyConfig{}); e != nil { flog.L(`W: `, e) } } diff --git a/Reply/F/comp.go b/Reply/F/comp.go index a735ce4..afa099e 100644 --- a/Reply/F/comp.go +++ b/Reply/F/comp.go @@ -3,6 +3,7 @@ package f import ( "github.com/qydysky/bili_danmu/Reply/F/danmuXml" "github.com/qydysky/bili_danmu/Reply/F/liveOver" + "github.com/qydysky/bili_danmu/Reply/F/reSetMp4TimeStamp" comp "github.com/qydysky/part/component" ) @@ -10,6 +11,7 @@ func init() { var linkMap = map[string][]string{ "github.com/qydysky/bili_danmu/Reply.startRecDanmu.stop": { comp.Sign[danmuXml.Sign](`toXml`), + comp.Sign[reSetMp4TimeStamp.Sign](`resetTS`), }, "github.com/qydysky/bili_danmu/Reply.SerF.player.ws": { comp.Sign[danmuXml.Sign](`toXml`), diff --git a/Reply/F/danmuXml/danmuXml.go b/Reply/F/danmuXml/danmuXml.go index 3fb189a..5e5a12d 100644 --- a/Reply/F/danmuXml/danmuXml.go +++ b/Reply/F/danmuXml/danmuXml.go @@ -70,7 +70,7 @@ func toXml(ctx context.Context, path *string) error { csvf := file.New((*path)+"0.csv", 0, false) var data = Data{} for i := 0; true; i += 1 { - if line, e := csvf.ReadUntil('\n', humanize.KByte, humanize.MByte); len(line) != 0 { + if line, e := csvf.ReadUntil([]byte{'\n'}, humanize.KByte, humanize.MByte); len(line) != 0 { lined := bytes.SplitN(line, []byte{','}, 3) if len(lined) == 3 { if e := json.Unmarshal(lined[2], &data); e == nil { @@ -97,6 +97,7 @@ func toXml(ctx context.Context, path *string) error { } f := file.New((*path)+"0.xml", 0, false) + _ = f.Delete() defer f.Close() if _, err := f.Write([]byte(xml.Header), true); err != nil { diff --git a/Reply/F/reSetMp4TimeStamp/reSetMp4TimeStamp.go b/Reply/F/reSetMp4TimeStamp/reSetMp4TimeStamp.go new file mode 100644 index 0000000..a4a0959 --- /dev/null +++ b/Reply/F/reSetMp4TimeStamp/reSetMp4TimeStamp.go @@ -0,0 +1,252 @@ +package reSetMp4TimeStamp + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + + comp "github.com/qydysky/part/component" + file "github.com/qydysky/part/file" +) + +// 直接保存下来的mp4在chrome上无法直接播放 +// +// https://serverfault.com/questions/738881/chrome-makes-way-too-many-requests-22000-while-downloading-mp4-video-34mb +type Sign struct { + // 重设mp4的时间戳 + resetTS func(ctx context.Context, ptr *string) error +} + +func init() { + sign := Sign{ + resetTS: resetTS, + } + if e := comp.Put[string](comp.Sign[Sign](`resetTS`), sign.resetTS); e != nil { + panic(e) + } +} + +func resetTS(ctx context.Context, ptr *string) error { + fmt.Println("resetTS") + defer fmt.Println("resetTS fin") + + f := file.New(*ptr+"0.mp4", 0, false) + if !f.IsExist() { + return nil + } + defer f.Close() + var tfdtBuf = make([]byte, 16) + var tfhdBuf = make([]byte, 12) + var boxBuf = make([]byte, 4) + var trackBuf = make([]byte, 4) + var timescaleBuf = make([]byte, 4) + var timescale int32 + var opTs = make(map[int32]int) + var cuTs = make(map[int32]int) + + if e := f.SeekUntil([]byte("mvhd"), file.AtCurrent, 1<<17, 1<<22); e != nil && !errors.Is(e, file.ErrMaxReadSizeReach) { + return e + } + if _, e := f.Read(boxBuf); e != nil { + return e + } else if !bytes.Equal(boxBuf, []byte("mvhd")) { + return fmt.Errorf("wrong box:%v", string(boxBuf)) + } + _ = f.SeekIndex(12, file.AtCurrent) + if _, e := f.Read(timescaleBuf); e != nil { + return e + } + timescale = btoi32(timescaleBuf, 0) + fmt.Printf("resetTS timescale:%v\n", timescale) + + for { + if e := f.SeekUntil([]byte("tkhd"), file.AtCurrent, 1<<17, 1<<22); e != nil { + if errors.Is(e, file.ErrMaxReadSizeReach) { + break + } + if errors.Is(e, io.EOF) { + break + } + return e + } + if _, e := f.Read(boxBuf); e != nil { + return e + } else if !bytes.Equal(boxBuf, []byte("tkhd")) { + return fmt.Errorf("wrong box:%v", string(boxBuf)) + } + _ = f.SeekIndex(12, file.AtCurrent) + if _, e := f.Read(trackBuf); e != nil { + return e + } + trackId := btoi32(trackBuf, 0) + fmt.Printf("trackId %v\n", trackId) + opTs[trackId] = -1 + cuTs[trackId] = 0 + } + + // fmt.Println("resetTimeStamp Druation %v(%v-%v)", time.Duration(int(time.Second)*(cuTS-opTs)), cuTS, opTs) + + _ = f.SeekIndex(0, file.AtOrigin) + + for { + if e := f.SeekUntil([]byte("tfhd"), file.AtCurrent, 1<<17, 1<<20); e != nil { + if errors.Is(e, file.ErrMaxReadSizeReach) { + continue + } + if errors.Is(e, io.EOF) { + break + } + return e + } + if _, e := f.Read(tfhdBuf); e != nil { + return e + } + + trackID := btoi32(tfhdBuf, 8) + + if e := f.SeekUntil([]byte("tfdt"), file.AtCurrent, 1<<17, 1<<20); e != nil { + if errors.Is(e, file.ErrMaxReadSizeReach) { + continue + } + if errors.Is(e, io.EOF) { + break + } + return e + } + if _, e := f.Read(tfdtBuf); e != nil { + return e + } + switch tfdtBuf[4] { + case 0: + ts := int(btoi32(tfdtBuf, 12)) + cuTs[trackID] = ts + if e := f.SeekIndex(-4, file.AtCurrent); e != nil { + return e + } + if opTs[trackID] == -1 { + opTs[trackID] = ts + } + if _, e := f.Write(itob32(int32(ts-opTs[trackID])), false); e != nil { + return e + } + case 1: + ts := int(btoi64(tfdtBuf, 8)) + cuTs[trackID] = ts + if e := f.SeekIndex(-8, file.AtCurrent); e != nil { + return e + } + if opTs[trackID] == -1 { + opTs[trackID] = ts + } + if _, e := f.Write(itob64(int64(ts-opTs[trackID])), false); e != nil { + return e + } + default: + return fmt.Errorf("unknow tfdt version %x", tfdtBuf[8]) + } + } + + for k, v := range opTs { + fmt.Printf("track %v opTs:%v cuTS:%v\n", k, v, cuTs[k]) + } + // fmt.Println("resetTimeStamp Druation %v(%v-%v)", time.Duration(int(time.Second)*(cuTS-opTs)), cuTS, opTs) + + var duration int32 + for k, v := range opTs { + duration = int32(cuTs[k]-v) / timescale + break + } + fmt.Printf("resetTS dur:%v\n", duration) + + _ = f.SeekIndex(0, file.AtOrigin) + + if e := f.SeekUntil([]byte("mvhd"), file.AtCurrent, 1<<17, 1<<22); !errors.Is(e, file.ErrMaxReadSizeReach) { + return e + } + _ = f.SeekIndex(20, file.AtCurrent) + if _, e := f.Write(itob32(duration), false); e != nil { + return e + } + + // write tkhd + _ = f.SeekIndex(0, file.AtOrigin) + for i := 0; i < len(opTs); i++ { + if e := f.SeekUntil([]byte("tkhd"), file.AtCurrent, 1<<17, 1<<20); e != nil { + if errors.Is(e, file.ErrMaxReadSizeReach) { + continue + } + if errors.Is(e, io.EOF) { + break + } + return e + } + _ = f.SeekIndex(16, file.AtCurrent) + if _, e := f.Read(trackBuf); e != nil { + return e + } + trackID := btoi32(trackBuf, 0) + _ = f.SeekIndex(4, file.AtCurrent) + if _, e := f.Write(itob32(int32(cuTs[trackID]-opTs[trackID])/timescale), false); e != nil { + return e + } + } + + return nil +} + +func btoi64(b []byte, offset int) int64 { + s := 8 + bu := make([]byte, s) + l := len(b) - offset + if l > s { + l = s + } + for i := 0; i < s && i < l; i++ { + bu[i+s-l] = b[offset+i] + } + + //binary.BigEndian.Uint64 + return int64(uint64(bu[7]) | uint64(bu[6])<<8 | uint64(bu[5])<<16 | uint64(bu[4])<<24 | + uint64(bu[3])<<32 | uint64(bu[2])<<40 | uint64(bu[1])<<48 | uint64(bu[0])<<56) +} + +func btoi32(b []byte, offset int) int32 { + s := 4 + bu := make([]byte, s) + l := len(b) - offset + if l > s { + l = s + } + for i := 0; i < s && i < l; i++ { + bu[i+s-l] = b[offset+i] + } + + //binary.BigEndian.Uint32 + return int32((uint32(bu[3]) | uint32(bu[2])<<8 | uint32(bu[1])<<16 | uint32(bu[0])<<24)) +} + +func itob64(v int64) []byte { + //binary.BigEndian.PutUint64 + b := make([]byte, 8) + b[0] = byte(v >> 56) + b[1] = byte(v >> 48) + b[2] = byte(v >> 40) + b[3] = byte(v >> 32) + b[4] = byte(v >> 24) + b[5] = byte(v >> 16) + b[6] = byte(v >> 8) + b[7] = byte(v) + return b +} + +func itob32(v int32) []byte { + //binary.BigEndian.PutUint32 + b := make([]byte, 4) + b[0] = byte(v >> 24) + b[1] = byte(v >> 16) + b[2] = byte(v >> 8) + b[3] = byte(v) + return b +} diff --git a/go.mod b/go.mod index b52800d..625f7d2 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21 require ( github.com/gotk3/gotk3 v0.6.2 github.com/mdp/qrterminal/v3 v3.1.1 - github.com/qydysky/part v0.28.1-0.20230815161426-479a1258c761 + github.com/qydysky/part v0.28.1-0.20230827094611-9921d2be4ddb 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.12.0 diff --git a/go.sum b/go.sum index 6cb9167..2a83c69 100644 --- a/go.sum +++ b/go.sum @@ -34,8 +34,8 @@ github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/qydysky/part v0.28.1-0.20230815161426-479a1258c761 h1:QF8corbhIb+PtiD0cBab/ejZBI8aflPKpEIK9NhHCyM= -github.com/qydysky/part v0.28.1-0.20230815161426-479a1258c761/go.mod h1:iOK6EzUOqdqTyQZm+pf1qxYpKvLmdK+YHZehNN/2J3U= +github.com/qydysky/part v0.28.1-0.20230827094611-9921d2be4ddb h1:Ic3I0DS8DaCP3v2zg6GGYJDf7ZUy5b+0S69SnA77IYI= +github.com/qydysky/part v0.28.1-0.20230827094611-9921d2be4ddb/go.mod h1:iOK6EzUOqdqTyQZm+pf1qxYpKvLmdK+YHZehNN/2J3U= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= -- 2.39.2