From a5e641417a66c43021ce0efca9b5c7ed85bb980f Mon Sep 17 00:00:00 2001 From: qydysky <32743305+qydysky@users.noreply.github.com> Date: Mon, 31 Oct 2022 05:50:22 +0800 Subject: [PATCH] =?utf8?q?fmp4=E4=B8=A2=E5=BC=83=E6=97=B6=E9=97=B4?= =?utf8?q?=E6=88=B3=E5=BC=82=E5=B8=B8=E5=85=B3=E9=94=AE=E5=B8=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- F/B_I.go | 29 ++++++---- Reply/fmp4Decode.go | 127 ++++++++++++++++++++++++++++++++++++++++++++ Reply/stream.go | 69 +++++++++++++++++++++--- VERSION | 2 +- bili_danmu.go | 6 +++ 5 files changed, 214 insertions(+), 19 deletions(-) create mode 100644 Reply/fmp4Decode.go diff --git a/F/B_I.go b/F/B_I.go index 465010f..11c02ef 100644 --- a/F/B_I.go +++ b/F/B_I.go @@ -6,43 +6,52 @@ import ( p "github.com/qydysky/part" ) + /* - 整数 字节转换区 - 32 4字节 - 16 2字节 +整数 字节转换区 +32 4字节 +16 2字节 */ func Itob32(num int32) []byte { var buffer bytes.Buffer err := binary.Write(&buffer, binary.BigEndian, num) - if err != nil {p.Logf().E(err)} + if err != nil { + p.Logf().E(err) + } return buffer.Bytes() } func Itob16(num int16) []byte { var buffer bytes.Buffer err := binary.Write(&buffer, binary.BigEndian, num) - if err != nil {p.Logf().E(err)} + if err != nil { + p.Logf().E(err) + } return buffer.Bytes() } func btoi32(b []byte) int32 { var buffer int32 err := binary.Read(bytes.NewReader(b), binary.BigEndian, &buffer) - if err != nil {p.Logf().E(err)} + if err != nil { + p.Logf().E(err) + } return buffer } func btoi16(b []byte) int16 { var buffer int16 err := binary.Read(bytes.NewReader(b), binary.BigEndian, &buffer) - if err != nil {p.Logf().E(err)} + if err != nil { + p.Logf().E(err) + } return buffer } func Btoi32(b []byte, offset int) int32 { - return btoi32(b[offset:offset+4]) + return btoi32(b[offset : offset+4]) } func Btoi16(b []byte, offset int) int16 { - return btoi16(b[offset:offset+2]) -} \ No newline at end of file + return btoi16(b[offset : offset+2]) +} diff --git a/Reply/fmp4Decode.go b/Reply/fmp4Decode.go new file mode 100644 index 0000000..ef7ed5a --- /dev/null +++ b/Reply/fmp4Decode.go @@ -0,0 +1,127 @@ +package reply + +import ( + "bytes" + "errors" + + F "github.com/qydysky/bili_danmu/F" +) + +type fmp4KeyFrame struct { + videoTime int + audioTime int + data []byte +} + +func Seach_stream_fmp4(buf []byte) (keyframes []fmp4KeyFrame, last_avilable_offset int, err error) { + var ( + cu int + keyframe fmp4KeyFrame + ) + + for cu < len(buf) { + //moof + moofI := cu + bytes.Index(buf[cu:], []byte("moof")) - 4 + if moofI == -1 { + err = errors.New("未找到moof包") + break + } + moofE := moofI + int(F.Btoi32(buf, moofI)) + if moofE > len(buf) { + break + } + cu = moofI + + var ( + iskeyFrame bool + videoTime int + audioTime int + ) + + for trafCount := 0; trafCount < 2 && cu < moofE; trafCount += 1 { + + //traf + trafI := cu + bytes.Index(buf[cu:], []byte("traf")) - 4 + if trafI == -1 { + err = errors.New("未找到traf包") + break + } + cu = trafI + trafE := trafI + int(F.Btoi32(buf, trafI)) + if trafE > moofE { + err = errors.New("traf包破损") + break + } + + //tfdt + tfdtI := cu + bytes.Index(buf[cu:], []byte("tfdt")) - 4 + if tfdtI == -1 { + err = errors.New("未找到tfdt包") + break + } + cu = tfdtI + tfdtE := tfdtI + int(F.Btoi32(buf, tfdtI)) + if tfdtE > trafE { + err = errors.New("tfdt包破损") + break + } + + //trun + trunI := cu + bytes.Index(buf[cu:], []byte("trun")) - 4 + if trunI == -1 { + err = errors.New("未找到trun包") + break + } + cu = trunI + trunE := trunI + int(F.Btoi32(buf, trunI)) + if trunE > trafE { + err = errors.New("trun包破损") + break + } + + timeStamp := int(F.Btoi32(buf, tfdtI+16)) + if trafCount == 0 { + videoTime = timeStamp + } else if trafCount == 1 { + audioTime = timeStamp + } + + if !iskeyFrame && buf[trunI+20] == byte(0x02) { + iskeyFrame = true + } + } + + if err != nil { + break + } + + if iskeyFrame { + last_avilable_offset = moofI - 1 + if len(keyframe.data) != 0 { + keyframes = append(keyframes, keyframe) + } + keyframe = fmp4KeyFrame{ + videoTime: videoTime, + audioTime: audioTime, + } + } + + //mdat + mdatI := cu + bytes.Index(buf[cu:], []byte("mdat")) - 4 + if moofI == -1 { + err = errors.New("未找到mdat包") + break + } + cu = mdatI + mdatE := mdatI + int(F.Btoi32(buf, mdatI)) + if mdatE > len(buf) { + err = errors.New("mdat包破损") + break + } + + keyframe.data = append(keyframe.data, buf[moofI:mdatE]...) + cu = mdatE + } + + return +} diff --git a/Reply/stream.go b/Reply/stream.go index d3303e2..9385789 100644 --- a/Reply/stream.go +++ b/Reply/stream.go @@ -637,6 +637,17 @@ func (t *M4SStream) saveStreamM4s() (e error) { defer out.Close() } + // + var ( + buf []byte + lastVideoTime int + lastAudioTime int + diffVideoTime int + diffAudioTime int + diffSumVideoTime int + diffSumAudioTime int + ) + // 下载循环 for download_seq := []*m4s_link_item{}; ; { @@ -713,16 +724,58 @@ func (t *M4SStream) saveStreamM4s() (e error) { // 传递已下载切片 { for _, v := range download_seq { - if strings.Contains(v.Base, `h`) { - t.first_buf = v.data - } - if v.status == 2 { download_seq = download_seq[1:] - t.bootBufPush(v.data) - t.Stream_msg.Push_tag(`data`, v.data) - if t.config.save_as_mp4 { - out.Write(v.data) + buf = append(buf, v.data...) + + if strings.Contains(v.Base, `h`) { + t.first_buf = v.data + if t.config.save_as_mp4 { + out.Write(v.data) + } + continue + } + + fmp4KeyFrames, last_avilable_offset, e := Seach_stream_fmp4(buf) + if e != nil { + t.log.L(`E: `, e) + } + + for _, fmp4KeyFrame := range fmp4KeyFrames { + { // 视频时间戳 + if lastVideoTime != 0 { + if diffVideoTime != 0 { + diffSumVideoTime = fmp4KeyFrame.videoTime - lastVideoTime - diffVideoTime + } + diffVideoTime = fmp4KeyFrame.videoTime - lastVideoTime + } + lastVideoTime = fmp4KeyFrame.videoTime + } + { // 音频时间戳 + if lastAudioTime != 0 { + if diffAudioTime != 0 { + diffSumAudioTime = fmp4KeyFrame.audioTime - lastAudioTime - diffAudioTime + } + diffAudioTime = fmp4KeyFrame.audioTime - lastAudioTime + } + lastAudioTime = fmp4KeyFrame.audioTime + } + + if diffAudioTime != 0 && diffVideoTime != 0 { + if math.Abs(float64(diffSumVideoTime)) < 5000 && math.Abs(float64(diffSumAudioTime)) < 5000 { + t.bootBufPush(fmp4KeyFrame.data) + t.Stream_msg.Push_tag(`data`, fmp4KeyFrame.data) + if t.config.save_as_mp4 { + out.Write(fmp4KeyFrame.data) + } + } else { + t.log.L(`W: `, `时间戳失去同步`) + } + } + } + + if last_avilable_offset > 0 { + buf = buf[last_avilable_offset:] } } else { break diff --git a/VERSION b/VERSION index 6e48d84..a51a1be 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -20210515230141 +20221031054700 \ No newline at end of file diff --git a/bili_danmu.go b/bili_danmu.go index f7515a2..e417654 100644 --- a/bili_danmu.go +++ b/bili_danmu.go @@ -1,6 +1,7 @@ package bili_danmu import ( + _ "embed" "flag" "fmt" "net/url" @@ -21,6 +22,9 @@ import ( ws "github.com/qydysky/part/websocket" ) +//go:embed VERSION +var version string + func init() { go func() { //日期变化 var old = time.Now().Hour() @@ -41,6 +45,8 @@ func Start(roomid ...int) { var stop = sys.Sys().PreventSleep() defer stop.Done() + danmulog.L(`I: `, "version: ", version) + //ctrl+c退出 interrupt := make(chan os.Signal, 2) go func() { -- 2.39.2