From 934d8a96c07d52c6e670c07f57585ad3e473319b Mon Sep 17 00:00:00 2001 From: qydysky Date: Sat, 25 Jan 2025 02:16:59 +0800 Subject: [PATCH] =?utf8?q?Improve=20=E4=BC=98=E5=8C=96mp4=E5=88=87?= =?utf8?q?=E7=89=87=E4=B8=8B=E8=BD=BD=20(#152)?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit * Improve 添加提示fmp4切片下载超时应大于 * Improve 优化mp4切片下载 * Improve 优化fmp4默认参数 --- Reply/flvDecode.go | 17 +- Reply/flvDecode_test.go | 42 +-- Reply/fmp4Decode.go | 613 +++++++++++++++++++----------------- Reply/fmp4Decode_test.go | 42 +-- Reply/stream.go | 38 ++- demo/config/config_K_v.json | 3 +- go.mod | 2 +- go.sum | 4 +- 8 files changed, 361 insertions(+), 400 deletions(-) diff --git a/Reply/flvDecode.go b/Reply/flvDecode.go index 068e32f..528f3ae 100644 --- a/Reply/flvDecode.go +++ b/Reply/flvDecode.go @@ -35,6 +35,11 @@ var ( ErrStreamId = errors.New("ErrStreamId") ErrTagSize = errors.New("ErrTagSize") ErrSignLost = errors.New("ErrSignLost") + + ActionInitFlv perrors.Action = `InitFlv` + ActionGetIndexFlv perrors.Action = `GetIndexFlv` + ActionSeekFlv perrors.Action = `SeekFlv` + ActionOneFFlv perrors.Action = `OneFFlv` ) type FlvDecoder struct { @@ -315,7 +320,7 @@ func (t *FlvDecoder) CutSeed(reader io.Reader, startT, duration time.Duration, w if !t.init { if frontBuf, dropOffset, e := t.InitFlv(buff.GetPureBuf()); e != nil { - return perrors.New("InitFlv", e.Error()) + return perrors.New(e.Error(), ActionInitFlv) } else { if dropOffset != 0 { _ = buff.RemoveFront(dropOffset) @@ -331,10 +336,10 @@ func (t *FlvDecoder) CutSeed(reader io.Reader, startT, duration time.Duration, w } else { if !seek && seeker != nil && getIndex != nil { if index, e := getIndex(startT); e != nil { - return perrors.New("s", e.Error()) + return perrors.New(e.Error(), ActionGetIndexFlv) } else { if _, e := seeker.Seek(index, io.SeekStart); e != nil { - return perrors.New("s", e.Error()) + return perrors.New(e.Error(), ActionSeekFlv) } } seek = true @@ -342,7 +347,7 @@ func (t *FlvDecoder) CutSeed(reader io.Reader, startT, duration time.Duration, w buff.Clear() } if dropOffset, e := t.oneF(buff.GetPureBuf(), wf); e != nil { - return perrors.New("skip", e.Error()) + return perrors.New(e.Error(), ActionOneFFlv) } else { if dropOffset != 0 { _ = buff.RemoveFront(dropOffset) @@ -376,7 +381,7 @@ func (t *FlvDecoder) GenFastSeed(reader io.Reader, save func(seedTo time.Duratio if !t.init { if frontBuf, dropOffset, e := t.InitFlv(buff.GetPureBuf()); e != nil { - return perrors.New("InitFlv", e.Error()) + return perrors.New(e.Error(), ActionInitFlv) } else { if dropOffset != 0 { _ = buff.RemoveFront(dropOffset) @@ -394,7 +399,7 @@ func (t *FlvDecoder) GenFastSeed(reader io.Reader, save func(seedTo time.Duratio } return save(time.Millisecond*time.Duration(t-firstFT), int64(totalRead-buff.Size()+index)) }); e != nil { - return perrors.New("skip", e.Error()) + return perrors.New(e.Error(), ActionOneFFlv) } else { if dropOffset != 0 { _ = buff.RemoveFront(dropOffset) diff --git a/Reply/flvDecode_test.go b/Reply/flvDecode_test.go index fb49951..6e1d1c3 100644 --- a/Reply/flvDecode_test.go +++ b/Reply/flvDecode_test.go @@ -73,19 +73,7 @@ func Test_FLVCut(t *testing.T) { } e := NewFlvDecoder().Cut(f, time.Minute*10, time.Second*20, cutf.File()) - if perrors.Catch(e, "Read") { - t.Log("err Read", e) - } - if perrors.Catch(e, "InitFlv") { - t.Log("err InitFlv", e) - } - if perrors.Catch(e, "skip") { - t.Log("err skip", e) - } - if perrors.Catch(e, "cutW") { - t.Log("err cutW", e) - } - t.Log(e) + t.Log(perrors.ErrorFormat(e)) } func Test_FLVGenFastSeed(t *testing.T) { @@ -108,19 +96,7 @@ func Test_FLVGenFastSeed(t *testing.T) { e = NewFlvDecoder().GenFastSeed(f, func(seedTo time.Duration, cuIndex int64) error { return sf(seedTo, cuIndex) }) - if perrors.Catch(e, "Read") { - t.Log("err Read", e) - } - if perrors.Catch(e, "InitFlv") { - t.Log("err InitFlv", e) - } - if perrors.Catch(e, "skip") { - t.Log("err skip", e) - } - if perrors.Catch(e, "cutW") { - t.Log("err cutW", e) - } - t.Log(e) + t.Log(perrors.ErrorFormat(e)) } // 10s-30s 215.815423ms @@ -154,17 +130,5 @@ func Test_FLVCutSeed(t *testing.T) { } e = NewFlvDecoder().CutSeed(f, time.Minute*10, time.Second*20, cutf.File(), f, gf) - if perrors.Catch(e, "Read") { - t.Log("err Read", e) - } - if perrors.Catch(e, "InitFlv") { - t.Log("err InitFlv", e) - } - if perrors.Catch(e, "skip") { - t.Log("err skip", e) - } - if perrors.Catch(e, "cutW") { - t.Log("err cutW", e) - } - t.Log(e) + t.Log(perrors.ErrorFormat(e)) } diff --git a/Reply/fmp4Decode.go b/Reply/fmp4Decode.go index a2bbf89..c0418ff 100644 --- a/Reply/fmp4Decode.go +++ b/Reply/fmp4Decode.go @@ -10,10 +10,17 @@ import ( "github.com/dustin/go-humanize" F "github.com/qydysky/bili_danmu/F" - perrors "github.com/qydysky/part/errors" + pe "github.com/qydysky/part/errors" slice "github.com/qydysky/part/slice" ) +var ( + ActionInitFmp4 pe.Action = `InitFmp4` + ActionGetIndexFmp4 pe.Action = `GetIndexFmp4` + ActionSeekFmp4 pe.Action = `SeekFmp4` + ActionOneFFmp4 pe.Action = `OneFFmp4` +) + var boxs map[string]bool func init() { @@ -97,23 +104,24 @@ func (t *Fmp4Decoder) Init_fmp4(buf []byte) (b []byte, err error) { return } - err = deal(ies, - []string{"ftyp", "moov"}, - func(m []ie) (bool, error) { + err = deal(ies, dealIE{ + boxNames: []string{"ftyp", "moov"}, + fs: func(m []ie) error { ftypI = m[0].i ftypE = m[0].e moovI = m[1].i moovE = m[1].e - return true, nil - }) + return nil + }, + }) if err != nil { return nil, err } - err = deal(ies, - []string{"tkhd", "mdia", "mdhd", "hdlr"}, - func(m []ie) (bool, error) { + err = deal(ies, dealIE{ + boxNames: []string{"tkhd", "mdia", "mdhd", "hdlr"}, + fs: func(m []ie) error { tackId := int(F.Btoi(buf, m[0].i+20, 4)) t.traks[tackId] = &trak{ trackID: tackId, @@ -122,8 +130,9 @@ func (t *Fmp4Decoder) Init_fmp4(buf []byte) (b []byte, err error) { timescale: int(F.Btoi(buf, m[2].i+20, 4)), handlerType: buf[m[3].i+16], } - return false, nil - }) + return nil + }, + }) if err != nil { return nil, err @@ -251,157 +260,161 @@ func (t *Fmp4Decoder) Search_stream_fmp4(buf []byte, keyframe *slice.Buf[byte]) } 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, error){ - func(m []ie) (bool, error) { - 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 ts.handlerType == 'v' { - if e := checkSampleEntries(m[5].i, m[6].i); e != nil { - //skip + []dealIE{ + { + boxNames: []string{"moof", "mfhd", "traf", "tfhd", "tfdt", "trun", "mdat"}, + fs: func(m []ie) error { + 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 ts.handlerType == 'v' { + if e := checkSampleEntries(m[5].i, m[6].i); e != nil { + //skip + t.buf.Reset() + haveKeyframe = false + cu = m[0].i + return pe.Join(ErrDealIESkip.New(), e) + } + } + if e := check_set_maxT(ts, func(_ timeStamp) error { + return errors.New("skip") + }, func(_ timeStamp) error { t.buf.Reset() haveKeyframe = false cu = m[0].i - return false, e + return errors.New("skip") + }); e != nil { + return pe.Join(ErrDealIESkip.New(), e) } } - if e := 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") - }); e != nil { - return false, e - } - } - // fmt.Println(ts.getT(), "frame0", keyframeMoof, t.buf.size(), m[0].i, m[6].n, m[6].e) + // fmt.Println(ts.getT(), "frame0", keyframeMoof, t.buf.size(), m[0].i, m[6].n, m[6].e) - //deal frame - if keyframeMoof { - if v, e := t.buf.HadModified(bufModified); e == nil && v && !t.buf.IsEmpty() { - if e := t.buf.AppendTo(keyframe); e != nil { - return false, e + //deal frame + if keyframeMoof { + if v, e := t.buf.HadModified(bufModified); e == nil && v && !t.buf.IsEmpty() { + if e := t.buf.AppendTo(keyframe); e != nil { + return pe.Join(ErrDealIEBreak.New(), e) + } + cu = m[0].i + t.buf.Reset() } - cu = m[0].i - t.buf.Reset() + haveKeyframe = true + } else if !haveKeyframe { + cu = m[6].e } - haveKeyframe = true - } else if !haveKeyframe { - cu = m[6].e - } - if haveKeyframe { - if e := t.buf.Append(buf[m[0].i:m[6].e]); e != nil { - return false, e + if haveKeyframe { + if e := t.buf.Append(buf[m[0].i:m[6].e]); e != nil { + return pe.Join(ErrDealIEBreak.New(), e) + } } - } - return false, nil + return nil + }, }, - func(m []ie) (bool, error) { - 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) - if handlerType == 'v' { - if e := checkSampleEntries(m[5].i, m[6].i); e != nil { - //skip + { + boxNames: []string{"moof", "mfhd", "traf", "tfhd", "tfdt", "trun", "traf", "tfhd", "tfdt", "trun", "mdat"}, + fs: func(m []ie) error { + 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) + if handlerType == 'v' { + if e := checkSampleEntries(m[5].i, m[6].i); e != nil { + //skip + t.buf.Reset() + haveKeyframe = false + cu = m[0].i + return pe.Join(ErrDealIESkip.New(), e) + } + } + switch handlerType { + case 'v': + video = ts + case 's': + audio = ts + } + if e := check_set_maxT(ts, func(_ timeStamp) error { + return errors.New("skip") + }, func(_ timeStamp) error { t.buf.Reset() haveKeyframe = false cu = m[0].i - return false, e + return errors.New("skip") + }); e != nil { + return pe.Join(ErrDealIESkip.New(), e) } } - switch handlerType { - case 'v': - video = ts - case 's': - audio = ts - } - if e := 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") - }); e != nil { - return false, e - } - } - { - ts, handlerType := get_track_type(m[7].i, m[8].i) - if handlerType == 'v' { - if e := checkSampleEntries(m[9].i, m[10].i); e != nil { - //skip + { + ts, handlerType := get_track_type(m[7].i, m[8].i) + if handlerType == 'v' { + if e := checkSampleEntries(m[9].i, m[10].i); e != nil { + //skip + t.buf.Reset() + haveKeyframe = false + cu = m[0].i + return pe.Join(ErrDealIESkip.New(), e) + } + } + switch handlerType { + case 'v': + video = ts + case 's': + audio = ts + } + if e := check_set_maxT(ts, func(_ timeStamp) error { + return errors.New("skip") + }, func(_ timeStamp) error { t.buf.Reset() haveKeyframe = false cu = m[0].i - return false, e + return errors.New("skip") + }); e != nil { + return pe.Join(ErrDealIESkip.New(), e) } } - switch handlerType { - case 'v': - video = ts - case 's': - audio = ts + + //sync audio timeStamp + if t.AVTDiff <= 0.1 { + t.AVTDiff = 0.1 } - if e := 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") - }); e != nil { - return false, e + if diff := math.Abs(video.getT() - audio.getT()); diff > t.AVTDiff { + return pe.Join(ErrDealIEBreak.New(), fmt.Errorf("时间戳不匹配 %v %v (或许应调整fmp4音视频时间戳容差s>%.2f)", video.timeStamp, audio.timeStamp, diff)) + // copy(video.data, F.Itob64(int64(audio.getT()*float64(video.timescale)))) } - } - - //sync audio timeStamp - if t.AVTDiff <= 0.1 { - t.AVTDiff = 0.1 - } - if diff := math.Abs(video.getT() - audio.getT()); diff > t.AVTDiff { - return false, fmt.Errorf("时间戳不匹配 %v %v (或许应调整fmp4音视频时间戳容差s>%.2f)", video.timeStamp, audio.timeStamp, diff) - // copy(video.data, F.Itob64(int64(audio.getT()*float64(video.timescale)))) - } - //deal frame - if keyframeMoof { - if v, e := t.buf.HadModified(bufModified); e == nil && v && !t.buf.IsEmpty() { - if e := t.buf.AppendTo(keyframe); e != nil { - return false, e + //deal frame + if keyframeMoof { + if v, e := t.buf.HadModified(bufModified); e == nil && v && !t.buf.IsEmpty() { + if e := t.buf.AppendTo(keyframe); e != nil { + return pe.Join(ErrDealIEBreak.New(), e) + } + cu = m[0].i + t.buf.Reset() } - cu = m[0].i - t.buf.Reset() + haveKeyframe = true + } else if !haveKeyframe { + cu = m[10].e } - haveKeyframe = true - } else if !haveKeyframe { - cu = m[10].e - } - if haveKeyframe { - if e := t.buf.Append(buf[m[0].i:m[10].e]); e != nil { - return false, e + if haveKeyframe { + if e := t.buf.Append(buf[m[0].i:m[10].e]); e != nil { + return pe.Join(ErrDealIEBreak.New(), e) + } } - } - return false, nil - }}) + return nil + }, + }, + }) return } @@ -511,165 +524,168 @@ func (t *Fmp4Decoder) oneF(buf []byte, w ...dealFMp4) (cu int, err error) { return 0, e } - var ErrNormal = perrors.New("ErrNormal", "ErrNormal") + var ErrNormal = pe.New("ErrNormal", "ErrNormal") 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, error){ - func(m []ie) (bool, error) { - var ( - keyframeMoof = buf[m[5].i+20] == byte(0x02) - // moofSN = int(F.Btoi(buf, m[1].i+12, 4)) - video timeStamp - ) - - { - ts, handlerType := get_track_type(m[3].i, m[4].i) - if ts.handlerType == 'v' { - if e := checkSampleEntries(m[5].i, m[6].i); e != nil { - //skip + []dealIE{ + { + boxNames: []string{"moof", "mfhd", "traf", "tfhd", "tfdt", "trun", "mdat"}, + fs: func(m []ie) error { + var ( + keyframeMoof = buf[m[5].i+20] == byte(0x02) + // moofSN = int(F.Btoi(buf, m[1].i+12, 4)) + video timeStamp + ) + + { + ts, handlerType := get_track_type(m[3].i, m[4].i) + if ts.handlerType == 'v' { + if e := checkSampleEntries(m[5].i, m[6].i); e != nil { + //skip + t.buf.Reset() + haveKeyframe = false + cu = m[0].i + return pe.Join(ErrDealIESkip.New(), e) + } + } + if handlerType == 'v' { + video = ts + } + if e := check_set_maxT(ts, func(_ timeStamp) error { + return errors.New("skip") + }, func(_ timeStamp) error { t.buf.Reset() haveKeyframe = false cu = m[0].i - return false, e + return errors.New("skip") + }); e != nil { + return pe.Join(ErrDealIESkip.New(), e) } } - if handlerType == 'v' { - video = ts - } - if e := 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") - }); e != nil { - return false, e - } - } - // fmt.Println(ts.getT(), "frame0", keyframeMoof, t.buf.size(), m[0].i, m[6].n, m[6].e) + // fmt.Println(ts.getT(), "frame0", keyframeMoof, t.buf.size(), m[0].i, m[6].n, m[6].e) - //deal frame - if keyframeMoof { - if v, e := t.buf.HadModified(bufModified); e == nil && v && !t.buf.IsEmpty() { - cu = m[0].i - if haveKeyframe && len(w) > 0 { - err = w[0](video.getT(), cu, t.buf) + //deal frame + if keyframeMoof { + if v, e := t.buf.HadModified(bufModified); e == nil && v && !t.buf.IsEmpty() { + cu = m[0].i + if haveKeyframe && len(w) > 0 { + err = w[0](video.getT(), cu, t.buf) + t.buf.Reset() + return ErrNormal + } t.buf.Reset() - return true, ErrNormal } - t.buf.Reset() + haveKeyframe = true + } else if !haveKeyframe { + cu = m[6].e } - haveKeyframe = true - } else if !haveKeyframe { - cu = m[6].e - } - if haveKeyframe { - if e := t.buf.Append(buf[m[0].i:m[6].e]); e != nil { - return false, e + if haveKeyframe { + if e := t.buf.Append(buf[m[0].i:m[6].e]); e != nil { + return pe.Join(ErrDealIEBreak.New(), e) + } } - } - return false, nil + return nil + }, }, - func(m []ie) (bool, error) { - 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 - ) - - { - ts, handlerType := get_track_type(m[3].i, m[4].i) - if handlerType == 'v' { - if e := checkSampleEntries(m[5].i, m[6].i); e != nil { - //skip + { + boxNames: []string{"moof", "mfhd", "traf", "tfhd", "tfdt", "trun", "traf", "tfhd", "tfdt", "trun", "mdat"}, + fs: func(m []ie) error { + 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 + ) + + { + ts, handlerType := get_track_type(m[3].i, m[4].i) + if handlerType == 'v' { + if e := checkSampleEntries(m[5].i, m[6].i); e != nil { + //skip + t.buf.Reset() + haveKeyframe = false + cu = m[0].i + return pe.Join(ErrDealIESkip.New(), e) + } + } + switch handlerType { + case 'v': + video = ts + case 's': + audio = ts + } + if e := check_set_maxT(ts, func(_ timeStamp) error { + return errors.New("skip") + }, func(_ timeStamp) error { t.buf.Reset() haveKeyframe = false cu = m[0].i - return false, e + return errors.New("skip") + }); e != nil { + return pe.Join(ErrDealIESkip.New(), e) } } - switch handlerType { - case 'v': - video = ts - case 's': - audio = ts - } - if e := 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") - }); e != nil { - return false, e - } - } - { - ts, handlerType := get_track_type(m[7].i, m[8].i) - if handlerType == 'v' { - if e := checkSampleEntries(m[9].i, m[10].i); e != nil { - //skip + { + ts, handlerType := get_track_type(m[7].i, m[8].i) + if handlerType == 'v' { + if e := checkSampleEntries(m[9].i, m[10].i); e != nil { + //skip + t.buf.Reset() + haveKeyframe = false + cu = m[0].i + return pe.Join(ErrDealIESkip.New(), e) + } + } + switch handlerType { + case 'v': + video = ts + case 's': + audio = ts + } + if e := check_set_maxT(ts, func(_ timeStamp) error { + return errors.New("skip") + }, func(_ timeStamp) error { t.buf.Reset() haveKeyframe = false cu = m[0].i - return false, e + return errors.New("skip") + }); e != nil { + return pe.Join(ErrDealIESkip.New(), e) } } - switch handlerType { - case 'v': - video = ts - case 's': - audio = ts + + //sync audio timeStamp + if t.AVTDiff <= 0.1 { + t.AVTDiff = 0.1 } - if e := 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") - }); e != nil { - return false, e + if diff := math.Abs(video.getT() - audio.getT()); diff > t.AVTDiff { + return pe.Join(ErrDealIEBreak.New(), fmt.Errorf("时间戳不匹配 %v %v (或许应调整fmp4音视频时间戳容差s>%.2f)", video.timeStamp, audio.timeStamp, diff)) + // copy(video.data, F.Itob64(int64(audio.getT()*float64(video.timescale)))) } - } - - //sync audio timeStamp - if t.AVTDiff <= 0.1 { - t.AVTDiff = 0.1 - } - if diff := math.Abs(video.getT() - audio.getT()); diff > t.AVTDiff { - return false, fmt.Errorf("时间戳不匹配 %v %v (或许应调整fmp4音视频时间戳容差s>%.2f)", video.timeStamp, audio.timeStamp, diff) - // copy(video.data, F.Itob64(int64(audio.getT()*float64(video.timescale)))) - } - //deal frame - if keyframeMoof { - if v, e := t.buf.HadModified(bufModified); e == nil && v && !t.buf.IsEmpty() { - cu = m[0].i - if haveKeyframe && len(w) > 0 { - err = w[0](video.getT(), cu, t.buf) + //deal frame + if keyframeMoof { + if v, e := t.buf.HadModified(bufModified); e == nil && v && !t.buf.IsEmpty() { + cu = m[0].i + if haveKeyframe && len(w) > 0 { + err = w[0](video.getT(), cu, t.buf) + t.buf.Reset() + return ErrNormal + } t.buf.Reset() - return true, ErrNormal } - t.buf.Reset() + haveKeyframe = true + } else if !haveKeyframe { + cu = m[10].e } - haveKeyframe = true - } else if !haveKeyframe { - cu = m[10].e - } - if haveKeyframe { - if e := t.buf.Append(buf[m[0].i:m[10].e]); e != nil { - return false, e + if haveKeyframe { + if e := t.buf.Append(buf[m[0].i:m[10].e]); e != nil { + return pe.Join(ErrDealIEBreak.New(), e) + } } - } - return false, nil + return nil + }, }, }, ) @@ -724,7 +740,7 @@ func (t *Fmp4Decoder) CutSeed(reader io.Reader, startT, duration time.Duration, if !init { if frontBuf, e := t.Init_fmp4(buff.GetPureBuf()); e != nil { - return perrors.New("Init_fmp4", e.Error()) + return pe.New(e.Error(), ActionInitFmp4) } else { if len(frontBuf) == 0 { bufSize *= 2 @@ -740,10 +756,10 @@ func (t *Fmp4Decoder) CutSeed(reader io.Reader, startT, duration time.Duration, } else { if !seek && seeker != nil && getIndex != nil { if index, e := getIndex(startT); e != nil { - return perrors.New("s", e.Error()) + return pe.New(e.Error(), ActionGetIndexFmp4) } else { if _, e := seeker.Seek(index, io.SeekStart); e != nil { - return perrors.New("s", e.Error()) + return pe.New(e.Error(), ActionSeekFmp4) } } seek = true @@ -751,7 +767,7 @@ func (t *Fmp4Decoder) CutSeed(reader io.Reader, startT, duration time.Duration, buff.Clear() } if dropOffset, e := t.oneF(buff.GetPureBuf(), wf); e != nil { - return perrors.New("w", e.Error()) + return pe.New(e.Error(), ActionOneFFmp4) } else { if dropOffset != 0 { _ = buff.RemoveFront(dropOffset) @@ -786,7 +802,7 @@ func (t *Fmp4Decoder) GenFastSeed(reader io.Reader, save func(seedTo time.Durati if !init { if frontBuf, e := t.Init_fmp4(buff.GetPureBuf()); e != nil { - return perrors.New("Init_fmp4", e.Error()) + return pe.New(e.Error(), ActionInitFmp4) } else { if len(frontBuf) == 0 { bufSize *= 2 @@ -802,7 +818,7 @@ func (t *Fmp4Decoder) GenFastSeed(reader io.Reader, save func(seedTo time.Durati } return save(time.Second*time.Duration(t-firstFT), int64(totalRead-buff.Size()+index)) }); e != nil { - return perrors.New("w", e.Error()) + return pe.New(e.Error(), ActionOneFFmp4) } else { if dropOffset != 0 { _ = buff.RemoveFront(dropOffset) @@ -815,32 +831,39 @@ func (t *Fmp4Decoder) GenFastSeed(reader io.Reader, save func(seedTo time.Durati return } -func deal(ies []ie, boxNames []string, fs func([]ie) (breakloop bool, err error)) (err error) { - return deals(ies, [][]string{boxNames}, []func([]ie) (breakloop bool, err error){fs}) +var ( + ErrDealIEBreak = pe.Action(`ErrDealIEBreak`) + ErrDealIESkip = pe.Action(`ErrDealIESkip`) +) + +type dealIE struct { + matchCounts int + boxNames []string + fs func([]ie) (err error) } -func deals(ies []ie, boxNames [][]string, fs []func([]ie) (breakloop bool, e error)) (err error) { - if len(boxNames) != len(fs) { - panic("boxNames与fs数量不相等") +func (t *dealIE) deal(ies []ie, cu int) (err error) { + if t.boxNames[t.matchCounts] == ies[cu].n { + t.matchCounts += 1 + if t.matchCounts == len(t.boxNames) { + t.matchCounts = 0 + return t.fs(ies[cu+1-len(t.boxNames) : cu+1]) + } + } else { + t.matchCounts = 0 } - 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 breakloop, e := fs[i](ies[cu-len(boxNames[i])+1 : cu+1]); e != nil { - return e - } else if breakloop { - boxNames = append(boxNames[:i], boxNames[i+1:]...) - fs = append(fs[:i], fs[i+1:]...) - matchCounts = append(matchCounts[:i], matchCounts[i+1:]...) - i -= 1 - } - } - } else { - matchCounts[i] = 0 + return nil +} + +func deal(ies []ie, dealIEf dealIE) (err error) { + return deals(ies, []dealIE{dealIEf}) +} + +func deals(ies []ie, dealIEs []dealIE) (err error) { + for cu := 0; cu < len(ies) && len(dealIEs) != 0; cu++ { + for i := 0; i < len(dealIEs); i++ { + if e := dealIEs[i].deal(ies, cu); e != nil && !pe.Catch(e, ErrDealIESkip) { + return e } } } @@ -848,8 +871,8 @@ func deals(ies []ie, boxNames [][]string, fs []func([]ie) (breakloop bool, e err } var ( - ErrMisBox = perrors.New("decode", "ErrMisBox") - ErrCantResync = perrors.New("decode") + ErrMisBox = pe.New("decode", "ErrMisBox") + ErrCantResync = pe.New("decode") ) func decode(buf []byte, reSyncboxName string) (m []ie, err error) { @@ -885,7 +908,7 @@ func decode(buf []byte, reSyncboxName string) (m []ie, err error) { } var ( - ErrUnkownBox = perrors.New("ErrUnkownBox") + ErrUnkownBox = pe.New("ErrUnkownBox") ) func searchBox(buf []byte, cu *int) (boxName string, i int, e int, err error) { diff --git a/Reply/fmp4Decode_test.go b/Reply/fmp4Decode_test.go index 367e437..c286c46 100644 --- a/Reply/fmp4Decode_test.go +++ b/Reply/fmp4Decode_test.go @@ -87,19 +87,7 @@ func Test_Mp4Cut(t *testing.T) { } e := NewFmp4Decoder().Cut(f, time.Minute*30, time.Second*20, cutf.File()) - if perrors.Catch(e, "Read") { - t.Log("err Read", e) - } - if perrors.Catch(e, "Init_fmp4") { - t.Log("err Init_fmp4", e) - } - if perrors.Catch(e, "skip") { - t.Log("err skip", e) - } - if perrors.Catch(e, "cutW") { - t.Log("err cutW", e) - } - t.Log(e) + t.Log(perrors.ErrorFormat(e)) } func Test_Mp4GenFastSeed(t *testing.T) { @@ -118,19 +106,7 @@ func Test_Mp4GenFastSeed(t *testing.T) { e = NewFmp4Decoder().GenFastSeed(f, func(seedTo time.Duration, cuIndex int64) error { return sf(seedTo, cuIndex) }) - if perrors.Catch(e, "Read") { - t.Log("err Read", e) - } - if perrors.Catch(e, "Init_fmp4") { - t.Log("err Init_fmp4", e) - } - if perrors.Catch(e, "skip") { - t.Log("err skip", e) - } - if perrors.Catch(e, "cutW") { - t.Log("err cutW", e) - } - t.Log(e) + t.Log(perrors.ErrorFormat(e)) // VideoFastSeed.BeforeGet("testdata/1.fastSeed") // { @@ -175,17 +151,5 @@ func Test_Mp4CutSeed(t *testing.T) { } e = NewFmp4Decoder().CutSeed(f, time.Minute*30, time.Second*20, cutf.File(), f, gf) - if perrors.Catch(e, "Read") { - t.Log("err Read", e) - } - if perrors.Catch(e, "Init_fmp4") { - t.Log("err Init_fmp4", e) - } - if perrors.Catch(e, "skip") { - t.Log("err skip", e) - } - if perrors.Catch(e, "cutW") { - t.Log("err cutW", e) - } - t.Log(e) + t.Log(perrors.ErrorFormat(e)) } diff --git a/Reply/stream.go b/Reply/stream.go index 7021206..10b713a 100644 --- a/Reply/stream.go +++ b/Reply/stream.go @@ -29,7 +29,7 @@ import ( replyFunc "github.com/qydysky/bili_danmu/Reply/F" videoInfo "github.com/qydysky/bili_danmu/Reply/F/videoInfo" pctx "github.com/qydysky/part/ctx" - pe "github.com/qydysky/part/errors" + perrors "github.com/qydysky/part/errors" file "github.com/qydysky/part/file" funcCtrl "github.com/qydysky/part/funcCtrl" pio "github.com/qydysky/part/io" @@ -138,7 +138,11 @@ func (t *m4s_link_item) getNo() (int, error) { return strconv.Atoi(base[:len(base)-4]) } -func (link *m4s_link_item) download(reqPool *pool.Buf[reqf.Req], reqConfig reqf.Rval) error { +var ( + AEFDCTO perrors.Action = `ActionErrFmp4DownloadCareTO` +) + +func (link *m4s_link_item) download(reqPool *pool.Buf[reqf.Req], reqConfig reqf.Rval) (err error) { link.status = 1 // 设置切片状态为正在下载 link.err = nil link.tryDownCount += 1 @@ -148,16 +152,7 @@ func (link *m4s_link_item) download(reqPool *pool.Buf[reqf.Req], reqConfig reqf. defer reqPool.Put(r) reqConfig.Url = link.Url - // fmt.Println(`T: `, `下载`, link.Base) - // defer t.log.L(`T: `, `下载完成`, link.Base, link.status, link.err) - if e := r.Reqf(reqConfig); e != nil && !errors.Is(e, io.EOF) { - // t.log.L(`T: `, `下载错误`, link.Base, e) - // if !reqf.IsTimeout(e) { - // // 发生非超时错误 - // link.err = e - // link.tryDownCount = 3 // 设置切片状态为下载失败 - // } link.status = 3 // 设置切片状态为下载失败 link.err = e return e @@ -166,8 +161,11 @@ func (link *m4s_link_item) download(reqPool *pool.Buf[reqf.Req], reqConfig reqf. link.err = e return e } else { + if int64(reqConfig.Timeout) < r.UsedTime.Milliseconds()+3000 { + err = perrors.New(fmt.Sprintf("fmp4切片下载超时s(%d)或许应该大于%d", reqConfig.Timeout/1000, (r.UsedTime.Milliseconds()+4000)/1000), AEFDCTO) + } link.status = 2 // 设置切片状态为下载完成 - return nil + return } } @@ -478,7 +476,7 @@ func (t *M4SStream) fetchParseM3U8(lastM4s *m4s_link_item, fmp4ListUpdateTo floa if err := r.Reqf(rval); err != nil { v.DisableAuto() - t.log.L("W: ", fmt.Sprintf("服务器 %s 发生故障 %s", F.ParseHost(v.Url), pe.ErrorFormat(err, pe.ErrSimplifyFunc))) + t.log.L("W: ", fmt.Sprintf("服务器 %s 发生故障 %s", F.ParseHost(v.Url), perrors.ErrorFormat(err, perrors.ErrActionInLineFunc))) if t.common.ValidLive() == nil { e = errors.New("全部流服务器发生故障") break @@ -982,7 +980,7 @@ func (t *M4SStream) saveStreamM4s() (e error) { fmp4Decoder = NewFmp4Decoder() keyframe = slice.New[byte]() lastM4s *m4s_link_item - to = 3 + to = 5 fmp4ListUpdateTo = 5.0 fmp4Count = 0 startT = time.Now() @@ -1117,16 +1115,22 @@ func (t *M4SStream) saveStreamM4s() (e error) { go func(link *m4s_link_item) { defer done() - if e := link.download(t.reqPool, reqf.Rval{ + e := link.download(t.reqPool, reqf.Rval{ Timeout: to * 1000, WriteLoopTO: (to + 2) * 1000, Proxy: t.common.Proxy, Header: map[string]string{ `Connection`: `close`, }, - }); e != nil { + }) + if perrors.Catch(e, AEFDCTO) { + t.log.L(`W: `, e.Error()) + } else if e != nil { downErr.Store(true) - t.log.L(`W: `, `切片下载失败`, link.Base, e) + if reqf.IsTimeout(e) { + t.log.L(`W: `, fmt.Sprintf("fmp4切片下载超时s或许应该大于%d", to)) + } + t.log.L(`W: `, `切片下载失败`, link.Base, perrors.ErrorFormat(e, perrors.ErrActionInLineFunc)) } }(download_seq[i]) } diff --git a/demo/config/config_K_v.json b/demo/config/config_K_v.json index 2b34c54..c9770e0 100644 --- a/demo/config/config_K_v.json +++ b/demo/config/config_K_v.json @@ -82,7 +82,8 @@ "flv断流续接": true, "flv音视频时间戳容差ms-help": "默认100,小于默认无效,调大可以允许较差的流,但可能会音画不同步", "flv音视频时间戳容差ms": 100, - "fmp4切片下载超时s": 3, + "fmp4切片下载超时s-help": "默认5,小于默认无效,调大可以允许等待更长的时间,但可能会导致实时回放延迟等异常", + "fmp4切片下载超时s": 5, "fmp4获取更多服务器": true, "fmp4跳过解码出错的帧-help": "fmp4跳过解码出错的帧、但可能导致关键帧时间上的跳越", "fmp4跳过解码出错的帧": false, diff --git a/go.mod b/go.mod index 65a0300..20c6ebb 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.23 require ( github.com/gotk3/gotk3 v0.6.4 github.com/mdp/qrterminal/v3 v3.2.0 - github.com/qydysky/part v0.28.20250114181028 + github.com/qydysky/part v0.28.20250120190624 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.21.0 diff --git a/go.sum b/go.sum index e4febbd..474c27e 100644 --- a/go.sum +++ b/go.sum @@ -48,8 +48,8 @@ github.com/qydysky/biliApi v0.0.0-20240725184407-15076dddb6fb h1:dtSpNF9hLQa09TU github.com/qydysky/biliApi v0.0.0-20240725184407-15076dddb6fb/go.mod h1:om024vfxALQ5vxsbaGoMm8IS0esLYBnEOpJI8FsGoDg= github.com/qydysky/brotli v0.0.0-20240828134800-e9913a6e7ed9 h1:k451T+bpsLr+Dq9Ujo+Qtx0iomRA1XXS5ttlEojvfuQ= github.com/qydysky/brotli v0.0.0-20240828134800-e9913a6e7ed9/go.mod h1:cI8/gy/wjy2Eb+p2IUj2ZuDnC8R5Vrx3O0VMPvMvphA= -github.com/qydysky/part v0.28.20250114181028 h1:YWsQkOcwHtL7KkHMl2l+NP4ZdO0gCZ7Ft0qh8Z5bbZ8= -github.com/qydysky/part v0.28.20250114181028/go.mod h1:RAb3G05OaqCSRWFJz9FnONB6iqF/Dk4R+Z5c/H7mWSg= +github.com/qydysky/part v0.28.20250120190624 h1:KyxUMxO0k5/loU1TP9usKJpN8YDGPgrIC6Yc37OeGws= +github.com/qydysky/part v0.28.20250120190624/go.mod h1:RAb3G05OaqCSRWFJz9FnONB6iqF/Dk4R+Z5c/H7mWSg= 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