From 320ec0940505285a1334ba4f90e2144eb2f9459a Mon Sep 17 00:00:00 2001 From: qydysky <32743305+qydysky@users.noreply.github.com> Date: Tue, 10 Jan 2023 21:40:48 +0800 Subject: [PATCH] fix fmp4 decode --- Reply/fmp4Decode.go | 277 +++++++++++++++++++++++++-------------- Reply/fmp4Decode_test.go | 17 ++- Reply/stream.go | 9 +- demo/go.mod | 2 +- demo/go.sum | 2 + go.mod | 2 +- go.sum | 2 + 7 files changed, 205 insertions(+), 106 deletions(-) diff --git a/Reply/fmp4Decode.go b/Reply/fmp4Decode.go index 3d53c24..752b7df 100644 --- a/Reply/fmp4Decode.go +++ b/Reply/fmp4Decode.go @@ -63,9 +63,17 @@ type Fmp4Decoder struct { func (t *Fmp4Decoder) Init_fmp4(buf []byte) (b []byte, err error) { var ftypI, ftypE, moovI, moovE int - err = deal(buf, + ies, e := decode(buf, "ftyp") + if len(ies) == 0 { + err = errors.New("未找到box") + } + if e != nil { + return + } + + err = deal(ies, []string{"ftyp", "moov"}, - func(m []*ie) bool { + func(m []ie) bool { ftypI = m[0].i ftypE = m[0].e moovI = m[1].i @@ -77,9 +85,9 @@ func (t *Fmp4Decoder) Init_fmp4(buf []byte) (b []byte, err error) { return nil, err } - err = deal(buf, + err = deal(ies, []string{"tkhd", "mdia", "mdhd", "hdlr"}, - func(m []*ie) bool { + func(m []ie) bool { tackId := int(F.Btoi(buf, m[0].i+20, 4)) if t.traks == nil { t.traks = make(map[int]trak) @@ -111,9 +119,11 @@ func (t *Fmp4Decoder) Seach_stream_fmp4(buf []byte, keyframes *bufB) (cu int, er t.buf.reset() var ( - haveKeyframe bool - bufModified = t.buf.getModifiedTime() - maxSequenceNumber int + haveKeyframe bool + bufModified = t.buf.getModifiedTime() + // maxSequenceNumber int //有时并不是单调增加 + maxVT float64 + maxAT float64 //get timeStamp get_timeStamp = func(tfdt int) (ts timeStamp) { @@ -139,119 +149,188 @@ func (t *Fmp4Decoder) Seach_stream_fmp4(buf []byte, keyframes *bufB) (cu int, er } return } + + //is t error? + check_set_maxT = func(ts timeStamp, equal func(ts timeStamp) error, larger func(ts timeStamp) error) (err error) { + switch ts.handlerType { + case 'v': + if maxVT == 0 { + maxVT = ts.getT() + } else if maxVT == ts.getT() && equal != nil { + err = equal(ts) + } else if maxVT > ts.getT() && larger != nil { + err = larger(ts) + } else { + maxVT = ts.getT() + } + case 'a': + if maxAT == 0 { + maxAT = ts.getT() + } else if maxAT == ts.getT() && equal != nil { + err = equal(ts) + } else if maxAT > ts.getT() && larger != nil { + err = larger(ts) + } else { + maxAT = ts.getT() + } + default: + } + return + } ) - err = deal(buf, - []string{"moof", "mfhd", - "traf", "tfhd", "tfdt", "trun", - "traf", "tfhd", "tfdt", "trun", - "mdat"}, - func(m []*ie) bool { - var ( - keyframeMoof = buf[m[5].i+20] == byte(0x02) || buf[m[9].i+20] == byte(0x02) - moofSN = int(F.Btoi(buf, m[1].i+12, 4)) - video timeStamp - audio timeStamp - ) - - // fmt.Println(moofSN, "frame", keyframeMoof, t.buf.size(), m[0].i, m[10].n, m[10].e) - - //is sn error? - if maxSequenceNumber == 0 { - maxSequenceNumber = moofSN - } else if moofSN == maxSequenceNumber { - return false - } else if moofSN != maxSequenceNumber+1 { - t.buf.reset() - haveKeyframe = false - cu = m[0].i + ies, e := decode(buf, "moof") + if len(ies) == 0 { + err = errors.New("未找到box") + } + if e != nil { + return + } + + err = deals(ies, + [][]string{ + {"moof", "mfhd", "traf", "tfhd", "tfdt", "trun", "mdat"}, + {"moof", "mfhd", "traf", "tfhd", "tfdt", "trun", "traf", "tfhd", "tfdt", "trun", "mdat"}}, + []func(m []ie) bool{ + func(m []ie) bool { + var ( + keyframeMoof = buf[m[5].i+20] == byte(0x02) + // moofSN = int(F.Btoi(buf, m[1].i+12, 4)) + ) + + { + ts, _ := get_track_type(m[3].i, m[4].i) + if nil != check_set_maxT(ts, func(_ timeStamp) error { + return errors.New("skip") + }, func(_ timeStamp) error { + t.buf.reset() + haveKeyframe = false + cu = m[0].i + return errors.New("skip") + }) { + return false + } + } + + // fmt.Println(ts.getT(), "frame0", keyframeMoof, t.buf.size(), m[0].i, m[6].n, m[6].e) + + //deal frame + if keyframeMoof { + if t.buf.hadModified(bufModified) && !t.buf.isEmpty() { + keyframes.append(t.buf.getPureBuf()) + cu = m[0].i + t.buf.reset() + } + haveKeyframe = true + } else if !haveKeyframe { + cu = m[6].e + } + if haveKeyframe { + t.buf.append(buf[m[0].i:m[6].e]) + } return false - } else { - maxSequenceNumber = moofSN - } - { - ts, handlerType := get_track_type(m[3].i, m[4].i) - switch handlerType { - case 'v': - video = ts - case 's': - audio = ts + }, + func(m []ie) bool { + var ( + keyframeMoof = buf[m[5].i+20] == byte(0x02) || buf[m[9].i+20] == byte(0x02) + // moofSN = int(F.Btoi(buf, m[1].i+12, 4)) + video timeStamp + audio timeStamp + ) + + // fmt.Println(moofSN, "frame1", keyframeMoof, t.buf.size(), m[0].i, m[10].n, m[10].e) + + { + ts, handlerType := get_track_type(m[3].i, m[4].i) + switch handlerType { + case 'v': + video = ts + case 's': + audio = ts + } + if nil != check_set_maxT(ts, func(_ timeStamp) error { + return errors.New("skip") + }, func(_ timeStamp) error { + t.buf.reset() + haveKeyframe = false + cu = m[0].i + return errors.New("skip") + }) { + return false + } } - } - { - ts, handlerType := get_track_type(m[7].i, m[8].i) - switch handlerType { - case 'v': - video = ts - case 's': - audio = ts + { + ts, handlerType := get_track_type(m[7].i, m[8].i) + switch handlerType { + case 'v': + video = ts + case 's': + audio = ts + } + if nil != check_set_maxT(ts, func(_ timeStamp) error { + return errors.New("skip") + }, func(_ timeStamp) error { + t.buf.reset() + haveKeyframe = false + cu = m[0].i + return errors.New("skip") + }) { + return false + } } - } - //deal frame - if keyframeMoof { //sync audio timeStamp if audio.getT() != video.getT() { date := F.Itob64(int64(video.getT() * float64(audio.timescale))) copy(audio.data, date) } - if t.buf.hadModified(bufModified) && !t.buf.isEmpty() { - keyframes.append(t.buf.getPureBuf()) - cu = m[0].i - t.buf.reset() - } - haveKeyframe = true - } else if !haveKeyframe { - cu = m[10].e - } - if haveKeyframe { - t.buf.append(buf[m[0].i:m[10].e]) - } - return false - }) - - if len(buf) > 1024*1024*20 { - err = errors.New("buf超过20M") - } + //deal frame + if keyframeMoof { + if t.buf.hadModified(bufModified) && !t.buf.isEmpty() { + keyframes.append(t.buf.getPureBuf()) + cu = m[0].i + t.buf.reset() + } + haveKeyframe = true + } else if !haveKeyframe { + cu = m[10].e + } + if haveKeyframe { + t.buf.append(buf[m[0].i:m[10].e]) + } + return false + }}) return } -func deal(buf []byte, boxName []string, f func([]*ie) (breakloop bool)) (err error) { +func deal(ies []ie, boxNames []string, fs func([]ie) (breakloop bool)) (err error) { + return deals(ies, [][]string{boxNames}, []func([]ie) (breakloop bool){fs}) +} - m, e := decode(buf, boxName[0]) - if len(m) == 0 { - return errors.New("未找到box") - } - if e != nil { - err = e +func deals(ies []ie, boxNames [][]string, fs []func([]ie) (breakloop bool)) (err error) { + if len(boxNames) != len(fs) { + panic("boxNames与fs数量不相等") } - - var matchCount = 0 - for cu := 0; cu < len(m); cu++ { - if m[cu].n == boxName[matchCount] { - matchCount += 1 - if matchCount == len(boxName) { - var ies []*ie - - for k, v := range boxName { - ies = append(ies, &ie{ - n: v, - i: m[cu-(matchCount-1)+k].i, - e: m[cu-(matchCount-1)+k].e, - }) - } - - if f(ies) { - break + var matchCounts = make([]int, len(boxNames)) + for cu := 0; cu < len(ies) && len(boxNames) != 0; cu++ { + for i := 0; i < len(boxNames); i++ { + if ies[cu].n == boxNames[i][matchCounts[i]] { + matchCounts[i] += 1 + if matchCounts[i] == len(boxNames[i]) { + matchCounts[i] = 0 + if fs[i](ies[cu-len(boxNames[i])+1 : cu+1]) { + boxNames = append(boxNames[:i], boxNames[i+1:]...) + fs = append(fs[:i], fs[i+1:]...) + matchCounts = append(matchCounts[:i], matchCounts[i+1:]...) + i -= 1 + } } - matchCount = 0 + } else { + matchCounts[i] = 0 } - } else { - matchCount = 0 } } - return } diff --git a/Reply/fmp4Decode_test.go b/Reply/fmp4Decode_test.go index 73dfe13..6b88783 100644 --- a/Reply/fmp4Decode_test.go +++ b/Reply/fmp4Decode_test.go @@ -7,15 +7,28 @@ import ( F "github.com/qydysky/bili_danmu/F" ) +//go:embed 32320131.m4s var buf []byte func Test_deal(t *testing.T) { - err := deal(buf, + ies, _ := decode(buf, "moof") + err := deal(ies, + []string{"moof", "mfhd", + "traf", "tfhd", "tfdt", "trun", + "mdat"}, + func(m []ie) bool { + moofSN := int(F.Btoi(buf, m[1].i+12, 4)) + keyframeMoof := buf[m[5].i+20] == byte(0x02) + t.Log(moofSN, "frame", keyframeMoof, m[0].i, m[6].n, m[6].e) + return false + }) + t.Log("err", err) + err = deal(ies, []string{"moof", "mfhd", "traf", "tfhd", "tfdt", "trun", "traf", "tfhd", "tfdt", "trun", "mdat"}, - func(m []*ie) bool { + func(m []ie) bool { moofSN := int(F.Btoi(buf, m[1].i+12, 4)) keyframeMoof := buf[m[5].i+20] == byte(0x02) || buf[m[9].i+20] == byte(0x02) t.Log(moofSN, "frame", keyframeMoof, m[0].i, m[10].n, m[10].e) diff --git a/Reply/stream.go b/Reply/stream.go index d70eca3..50a820c 100644 --- a/Reply/stream.go +++ b/Reply/stream.go @@ -745,7 +745,9 @@ func (t *M4SStream) saveStreamM4s() (e error) { } // 等待队列下载完成 - download_limit.PlanDone() + download_limit.PlanDone(func() { + time.Sleep(time.Millisecond * 10) + }) } // 传递已下载切片 @@ -814,7 +816,7 @@ func (t *M4SStream) saveStreamM4s() (e error) { } // no, _ := v.getNo() - // fmt.Println(no, "fmp4KeyFrames", len(fmp4KeyFrames), last_avilable_offset, err) + // fmt.Println(no, "fmp4KeyFrames", fmp4KeyFrames.size(), last_avilable_offset, err) if !fmp4KeyFrames.isEmpty() { fmp4KeyFramesBuf = fmp4KeyFrames.getCopyBuf() @@ -1017,6 +1019,7 @@ func (t *M4SStream) Stop() { func (t *M4SStream) Pusher(w http.ResponseWriter, r *http.Request) { switch t.stream_type { case `m3u8`: + t.pusherM4s(w, r) case `mp4`: t.pusherM4s(w, r) case `flv`: @@ -1104,7 +1107,7 @@ func (t *M4SStream) pusherFlv(w http.ResponseWriter, r *http.Request) { cancel := make(chan struct{}) - //hls切片 + //flv t.Stream_msg.Pull_tag(map[string]func(interface{}) bool{ `data`: func(data interface{}) bool { if b, ok := data.([]byte); ok { diff --git a/demo/go.mod b/demo/go.mod index c44aaab..7e834ce 100644 --- a/demo/go.mod +++ b/demo/go.mod @@ -15,7 +15,7 @@ require ( 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.20.0 // indirect + github.com/qydysky/part v0.20.1 // 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 diff --git a/demo/go.sum b/demo/go.sum index b16321b..61d450e 100644 --- a/demo/go.sum +++ b/demo/go.sum @@ -132,6 +132,8 @@ github.com/qydysky/part v0.19.2 h1:peR1UBrBgnjB63nv5F100oJ72hRoJnn8cuZPXDiGZOM= github.com/qydysky/part v0.19.2/go.mod h1:BG0tulTKW58jSkC0EZ0MrxDHe+gkPULfGNzksiGCayw= github.com/qydysky/part v0.20.0 h1:JkAdTrGwXjbL8FJuiinKK8Vrd2HU/rcRD+Bdx4RpGGw= github.com/qydysky/part v0.20.0/go.mod h1:BG0tulTKW58jSkC0EZ0MrxDHe+gkPULfGNzksiGCayw= +github.com/qydysky/part v0.20.1 h1:C0VDY/OZHEVRD1SeudG67t/7/0qUMlfJcy8CQQKVZ9A= +github.com/qydysky/part v0.20.1/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= diff --git a/go.mod b/go.mod index e05a0c8..024ae1a 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( 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.20.0 + github.com/qydysky/part v0.20.1 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 diff --git a/go.sum b/go.sum index 4012266..9df4c1b 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,8 @@ github.com/qydysky/part v0.10.17 h1:xcMgJaEvPlOPAEfOniTZZM/pDiafWW3FA5ZQXNPthpI= github.com/qydysky/part v0.10.17/go.mod h1:B3GD/j5jmvwfKtnzDWqRYFqnwOXEyoUg/jShFk1yQSM= github.com/qydysky/part v0.20.0 h1:JkAdTrGwXjbL8FJuiinKK8Vrd2HU/rcRD+Bdx4RpGGw= github.com/qydysky/part v0.20.0/go.mod h1:BG0tulTKW58jSkC0EZ0MrxDHe+gkPULfGNzksiGCayw= +github.com/qydysky/part v0.20.1 h1:C0VDY/OZHEVRD1SeudG67t/7/0qUMlfJcy8CQQKVZ9A= +github.com/qydysky/part v0.20.1/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= -- 2.39.2