]> 127.0.0.1 Git - bili_danmu/.git/commitdiff
Fix 重置时间戳后的文件导致ffmpeg error #88
authorqydysky <qydysky@foxmail.com>
Tue, 3 Oct 2023 12:33:44 +0000 (20:33 +0800)
committerqydysky <qydysky@foxmail.com>
Tue, 3 Oct 2023 12:33:44 +0000 (20:33 +0800)
Reply/F.go
Reply/F/comp.go
Reply/F/fmp4Tomp4/fmp4Tomp4.go [new file with mode: 0644]
Reply/F/fmp4Tomp4/fmp4Tomp4_test.go [new file with mode: 0644]
Reply/F/fmp4Tomp4/reader.go [new file with mode: 0644]
Reply/F/fmp4Tomp4/reader_test.go [new file with mode: 0644]
Reply/F/fmp4Tomp4/writer.go [new file with mode: 0644]
Reply/F/fmp4Tomp4/writer_test.go [new file with mode: 0644]

index 9ed8969b40e67b96ac19dd23dbdcc7f3d3bd3ce4..c4c5e35cd43f742a57742da6c64544d84332033a 100644 (file)
@@ -1440,7 +1440,7 @@ func init() {
                                        }
 
                                        // 读取区间
-                                       // var rangeHeaderNum int
+                                       var rangeHeaderNum int
                                        // if rangeHeader := r.Header.Get(`range`); rangeHeader != "" {
                                        //      var e error
                                        //      if strings.Index(rangeHeader, "bytes=") != 0 {
@@ -1470,7 +1470,7 @@ func init() {
                                                }
                                        }
 
-                                       f := file.New(v, 0, false)
+                                       f := file.New(v, int64(rangeHeaderNum), false)
                                        defer f.Close()
 
                                        // 设置当前返回区间,并拷贝
@@ -1479,7 +1479,7 @@ func init() {
                                        //      flog.L(`W: `, e)
                                        //      return
                                        // } else {
-                                       //      w.Header().Add(`Content-Range`, fmt.Sprintf("bytes %d-%d/%d", rangeHeaderNum, fi.Size(), fi.Size()))
+                                       //      w.Header().Add(`Content-Range`, fmt.Sprintf("bytes %d-%d/%d", rangeHeaderNum, fi.Size()-1, fi.Size()))
                                        //      w.WriteHeader(http.StatusPartialContent)
 
                                        flog.L(`T: `, r.RemoteAddr, `接入录播`+v)
index afa099e56cecee4517681bf151ac5fffe9bbfd18..70a05e8c094cd0ad8de2fdee2554b8d6f5a406c7 100644 (file)
@@ -12,6 +12,7 @@ func init() {
                "github.com/qydysky/bili_danmu/Reply.startRecDanmu.stop": {
                        comp.Sign[danmuXml.Sign](`toXml`),
                        comp.Sign[reSetMp4TimeStamp.Sign](`resetTS`),
+                       // comp.Sign[fmp4Tomp4.Sign](`conver`),
                },
                "github.com/qydysky/bili_danmu/Reply.SerF.player.ws": {
                        comp.Sign[danmuXml.Sign](`toXml`),
diff --git a/Reply/F/fmp4Tomp4/fmp4Tomp4.go b/Reply/F/fmp4Tomp4/fmp4Tomp4.go
new file mode 100644 (file)
index 0000000..cbc23e3
--- /dev/null
@@ -0,0 +1,601 @@
+package fmp4Tomp4
+
+import (
+       "bytes"
+       "context"
+       "errors"
+       "fmt"
+       "time"
+
+       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的时间戳
+       conver func(ctx context.Context, ptr *string) error
+}
+
+func init() {
+       sign := Sign{
+               conver: conver,
+       }
+       if e := comp.Put[string](comp.Sign[Sign](`conver`), sign.conver); e != nil {
+               panic(e)
+       }
+}
+
+var (
+       ErrParse = errors.New("ErrParse")
+)
+
+func conver(ctx context.Context, ptr *string) error {
+       be := time.Now()
+       fmt.Println("conver")
+       defer fmt.Printf("conver fin (%v)\n", time.Since(be))
+
+       sf := file.New(*ptr+"0.mp4", 0, false)
+       if !sf.IsExist() {
+               return nil
+       }
+       defer sf.Close()
+
+       r, _ := NewReader(sf)
+       if e := r.Parse(); e != nil || len(r.Boxs) == 0 {
+               return e
+       }
+       traks, e := r.ParseTrun()
+       if e != nil {
+               return e
+       }
+       fmt.Printf("conver parse ok (%v)\n", time.Since(be))
+
+       boxReader := r.Read(r.getN("mvhd", 1)[0], 30)
+       timescale := boxReader.I32(20)
+       scaleDur := boxReader.I32(24)
+       mainDuration := float64(scaleDur) / float64(timescale)
+
+       tf := file.New(*ptr+"1.mp4", 0, false)
+       if tf.IsExist() {
+               _ = tf.Delete()
+       }
+       defer tf.Close()
+
+       w, e := NewBoxWriter(tf)
+       if e != nil {
+               return e
+       }
+
+       // ftyp
+       {
+               ftyp := w.Box("ftyp")
+               ftyp.Write([]byte("isom"))
+               ftyp.Write(itob32(512))
+               ftyp.Write([]byte("isom"))
+               ftyp.Write([]byte("iso2"))
+               ftyp.Write([]byte("avc1"))
+               ftyp.Write([]byte("mp41"))
+               if e := ftyp.Close(); e != nil {
+                       return e
+               }
+       }
+
+       // moov
+       {
+               moov := w.Box("moov")
+               // mvhd
+               {
+                       mvhd := moov.Box("mvhd")
+                       mvhd.Write(make([]byte, 12))
+                       mvhd.Write(itob32(1000))
+                       mvhd.Write(itob32(int32(mainDuration * 1000)))
+                       mvhd.Write(r.Read(r.getN("mvhd", 1)[0], -1).Buf[28:])
+                       if e := mvhd.Close(); e != nil {
+                               return e
+                       }
+               }
+               // trak
+               var trakCount = -1
+               for trakId, trakSum := range traks {
+                       fmt.Printf("conver traks (%v)(%v)\n", trakId, time.Since(be))
+
+                       trakCount++
+                       trak := moov.Box("trak")
+                       // tkhd
+                       {
+                               if boxs := r.getN("trak", 2); len(boxs) != 2 {
+                                       return errors.Join(ErrParse, fmt.Errorf("trak"))
+                               } else {
+                                       tkhd := trak.Box("tkhd")
+                                       tkhd.Write([]byte{0, 0, 0, 3})
+                                       tkhd.Write(make([]byte, 8))
+                                       tkhd.Write(itob32(trakId))
+                                       tkhd.Write(make([]byte, 4))
+                                       tkhd.Write(itob32(int32(mainDuration * 1000)))
+                                       tkhd.Write(r.Read(boxs[trakCount], -1).Buf[32:])
+                                       if e := tkhd.Close(); e != nil {
+                                               return e
+                                       }
+                               }
+                       }
+                       // mdia
+                       {
+                               mdia := trak.Box("mdia")
+                               // mdhd
+                               {
+                                       if boxs := r.getN("mdhd", 2); len(boxs) != 2 {
+                                               return errors.Join(ErrParse, fmt.Errorf("mdhd"))
+                                       } else {
+                                               mdhd := mdia.Box("mdhd")
+                                               mdhd.Write(r.Read(boxs[trakCount], -1).Buf)
+                                               if e := mdhd.Close(); e != nil {
+                                                       return e
+                                               }
+                                       }
+                               }
+                               // hdlr
+                               var handlerType = make([]byte, 4)
+                               {
+                                       if boxs := r.getN("hdlr", 2); len(boxs) != 2 {
+                                               return errors.Join(ErrParse, fmt.Errorf("hdlr"))
+                                       } else {
+                                               hdlr := mdia.Box("hdlr")
+                                               boxReader := r.Read(boxs[trakCount], -1)
+                                               copy(handlerType, boxReader.Buf[16:20])
+                                               hdlr.Write(boxReader.Buf)
+                                               if e := hdlr.Close(); e != nil {
+                                                       return e
+                                               }
+                                       }
+                               }
+                               // minf
+                               {
+                                       minf := mdia.Box("minf")
+                                       // vmhd
+                                       if bytes.Equal(handlerType, []byte("vide")) {
+                                               if boxs := r.getN("vmhd", 1); len(boxs) != 1 {
+                                                       return errors.Join(ErrParse, fmt.Errorf("vmhd"))
+                                               } else {
+                                                       vmhd := minf.Box("vmhd")
+                                                       vmhd.Write(r.Read(boxs[0], -1).Buf)
+                                                       if e := vmhd.Close(); e != nil {
+                                                               return e
+                                                       }
+                                               }
+                                       }
+                                       // dinf
+                                       {
+                                               if boxs := r.getN("dinf", 2); len(boxs) != 2 {
+                                                       return errors.Join(ErrParse, fmt.Errorf("dinf"))
+                                               } else {
+                                                       dinf := minf.Box("dinf")
+                                                       dinf.Write(r.Read(boxs[trakCount], -1).Buf)
+                                                       if e := dinf.Close(); e != nil {
+                                                               return e
+                                                       }
+                                               }
+                                       }
+                                       // stbl
+                                       {
+                                               stbl := minf.Box("stbl")
+                                               // stsd
+                                               {
+                                                       if boxs := r.getN("stsd", 2); len(boxs) != 2 {
+                                                               return errors.Join(ErrParse, fmt.Errorf("stsd"))
+                                                       } else {
+                                                               stsd := stbl.Box("stsd")
+                                                               stsd.Write(r.Read(boxs[trakCount], -1).Buf)
+                                                               if e := stsd.Close(); e != nil {
+                                                                       return e
+                                                               }
+                                                       }
+                                               }
+                                               // stts
+                                               {
+                                                       stts := stbl.Box("stts")
+                                                       stts.Write([]byte{0, 0, 0, 0})
+                                                       stts.Write(itob32(int32(len(trakSum.dur))))
+                                                       for k, v := range trakSum.dur {
+                                                               stts.Write(itob32(int32(k)))
+                                                               stts.Write(itob32(v))
+                                                       }
+                                                       if e := stts.Close(); e != nil {
+                                                               return e
+                                                       }
+                                               }
+                                               // stsc
+                                               {
+                                                       stsc := stbl.Box("stsc")
+                                                       stsc.Write([]byte{0, 0, 0, 0})
+                                                       stsc.Write(itob32(int32(len(trakSum.sampleCount))))
+                                                       for k, v := range trakSum.sampleCount {
+                                                               stsc.Write(itob32(int32(k)))
+                                                               stsc.Write(itob32(v))
+                                                               stsc.Write([]byte{0, 0, 0, 1})
+                                                       }
+                                                       if e := stsc.Close(); e != nil {
+                                                               return e
+                                                       }
+                                               }
+                                               // stsz
+                                               {
+                                                       stsz := stbl.Box("stsz")
+                                                       stsz.Write([]byte{0, 0, 0, 0})
+                                                       stsz.Write(itob32(int32(len(trakSum.size))))
+                                                       for _, v := range trakSum.size {
+                                                               stsz.Write(itob32(v))
+                                                       }
+                                                       if e := stsz.Close(); e != nil {
+                                                               return e
+                                                       }
+                                               }
+                                               // co64
+                                               {
+                                                       co64 := stbl.Box("co64")
+                                                       co64.Write([]byte{0, 0, 0, 0})
+                                                       co64.Write(itob32(int32(len(trakSum.chunkSize))))
+
+                                                       cuIndex, _ := tf.CurIndex()
+                                                       cuIndex += int64(8*len(trakSum.chunkSize)) + 8
+
+                                                       co64.Write(itob64(cuIndex))
+                                                       for i := 0; i < len(trakSum.chunkSize)-1; i++ {
+                                                               co64.Write(itob64(cuIndex + trakSum.chunkSize[i]))
+                                                       }
+                                                       if e := co64.Close(); e != nil {
+                                                               return e
+                                                       }
+                                               }
+                                               if e := stbl.Close(); e != nil {
+                                                       return e
+                                               }
+                                       }
+                                       if e := minf.Close(); e != nil {
+                                               return e
+                                       }
+                               }
+                               if e := mdia.Close(); e != nil {
+                                       return e
+                               }
+                       }
+                       if e := trak.Close(); e != nil {
+                               return e
+                       }
+               }
+               if e := moov.Close(); e != nil {
+                       return e
+               }
+               fmt.Printf("conver moov fin (%v)\n", time.Since(be))
+       }
+
+       // mdat
+       {
+               mdat := w.Box("mdat")
+               for i := 0; i < len(r.Boxs); i++ {
+                       box := r.Boxs[i]
+                       if box.Name == "mdat" {
+                               if e := sf.SeekIndex(box.Index+box.HeaderSize, file.AtOrigin); e != nil {
+                                       return e
+                               }
+                               mdat.CopyFrom(sf, uint64(box.Size-box.HeaderSize))
+                       }
+               }
+               if e := mdat.Close(); e != nil {
+                       return e
+               }
+       }
+       return nil
+}
+
+// func bufChange(buf []byte, size int) {
+//     if n := size - len(buf); n > 0 {
+//             if size <= cap(buf) {
+//                     buf = buf[:size]
+//             } else {
+//                     buf = append(buf, make([]byte, n)...)
+//             }
+//     } else if n < 0 {
+//             buf = buf[:size]
+//     }
+//     clear(buf)
+// }
+
+// type track struct {
+//     chunkSize   []int64
+//     sampleCount []int32
+//     dur         []int32
+//     size        []int32
+// }
+
+// type wt struct {
+//     sf  *reader
+//     f   *file.File
+//     buf []byte
+//     m   map[string]float64
+// }
+
+// func NewWt(sf *reader, tf *file.File) *wt {
+//     return &wt{
+//             sf:  sf,
+//             f:   tf,
+//             buf: make([]byte, 1<<20),
+//             m:   make(map[string]float64),
+//     }
+// }
+
+// func (t *wt) start(boxName string, wrongPanic bool) (wSize int) {
+//     if _, e := t.f.Write([]byte{0, 0, 0, 1}, false); e != nil {
+//             panic(e)
+//     }
+//     if _, e := t.f.Write([]byte(boxName), false); e != nil {
+//             panic(e)
+//     }
+//     if _, e := t.f.Write(make([]byte, 8), false); e != nil {
+//             panic(e)
+//     }
+//     wSize = 16
+//     return
+// }
+
+// func (t *wt) skipt(size int) (n int) {
+//     if size == 0 {
+//             return
+//     }
+//     t.bufChange(size)
+//     if n, e := io.Writer(t.f.File()).Write(t.buf); e != nil {
+//             panic(e)
+//     } else {
+//             return n
+//     }
+// }
+
+// func (t *wt) skips(size int64) (n int64) {
+//     if size == 0 {
+//             return
+//     }
+//     if e := t.sf.f.SeekIndex(size, file.AtCurrent); e != nil {
+//             panic(e)
+//     }
+//     return size
+// }
+
+// func (t *wt) w(p []byte) (n int) {
+//     if n, e := t.f.Write(p, false); e != nil {
+//             panic(e)
+//     } else {
+//             return n
+//     }
+// }
+
+// func (t *wt) r(size int) (n int64) {
+//     t.bufChange(size)
+//     if n, e := t.sf.f.Read(t.buf); e != nil {
+//             panic(e)
+//     } else {
+//             return int64(n)
+//     }
+// }
+
+// func (t *wt) bufChange(size int) {
+//     if n := size - len(t.buf); n > 0 {
+//             if size <= cap(t.buf) {
+//                     t.buf = t.buf[:size]
+//             } else {
+//                     t.buf = append(t.buf, make([]byte, n)...)
+//             }
+//     } else if n < 0 {
+//             t.buf = t.buf[:size]
+//     }
+//     clear(t.buf)
+// }
+
+// func (t *wt) fin(size int) int {
+//     if e := t.f.SeekIndex(-int64(size), file.AtCurrent); e != nil {
+//             panic(e)
+//     }
+//     if e := t.f.SeekIndex(8, file.AtCurrent); e != nil {
+//             panic(e)
+//     }
+//     if _, e := t.f.Write(itob64(int64(size)), false); e != nil {
+//             panic(e)
+//     }
+//     if e := t.f.SeekIndex(int64(size-16), file.AtCurrent); e != nil {
+//             panic(e)
+//     }
+//     return size
+// }
+
+// func (t *wt) copyBox(boxName string) int {
+//     wSize := t.start(boxName, true)
+//     t.r(int(lSize))
+//     wSize += t.w(t.buf)
+//     return t.fin(wSize)
+// }
+
+// func (t *wt) ftyp() {
+//     n := t.start("ftyp", true)
+//     n += t.w([]byte("isom"))
+//     n += t.w(itob32(512))
+//     n += t.w([]byte("isom"))
+//     n += t.w([]byte("iso2"))
+//     n += t.w([]byte("avc1"))
+//     n += t.w([]byte("mp41"))
+//     t.fin(n)
+// }
+
+// func (t *wt) moov() {
+//     n, _ := t.start("moov", true)
+//     n += t.mvhd()
+//     n += t.trak()
+//     t.fin(n)
+// }
+
+// func (t *wt) mvhd() int {
+//     wSize, lSize := t.start("mvhd", true)
+//     lSize -= t.skips(12)
+//     lSize -= t.r(4)
+//     timescale := btoi32(t.buf, 0)
+//     lSize -= t.r(4)
+//     duration := btoi32(t.buf, 0)
+//     t.m["mainDuration"] = float64(duration) / float64(timescale)
+//     wSize += t.skipt(12)
+//     wSize += t.w(itob32(1000))
+//     wSize += t.w(itob32(timescale * duration / 1000))
+//     lSize -= t.r(56)
+//     wSize += t.w(t.buf)
+//     t.skips(lSize)
+//     return t.fin(wSize)
+// }
+
+// func (t *wt) trak() int {
+//     wSize, _ := t.start("trak", false)
+//     if wSize == 0 {
+//             return 0
+//     }
+
+//     wSize += t.tkhd()
+//     wSize += t.mdia()
+
+//     return t.fin(wSize)
+// }
+
+// func (t *wt) tkhd() int {
+//     wSize, lSize := t.start("mvhd", true)
+//     lSize -= t.r(12)
+//     wSize += t.w(t.buf)
+//     lSize -= t.r(4)
+//     wSize += t.w(t.buf)
+//     wSize += t.skipt(4)
+//     wSize += t.w(itob32(int32(t.m["mainDuration"] / 1000)))
+//     wSize += t.skipt(10)
+//     lSize -= t.skips(18)
+//     lSize -= t.r(52)
+//     wSize += t.w(t.buf)
+//     t.skips(lSize)
+//     return t.fin(wSize)
+// }
+
+// func (t *wt) mdia() int {
+//     wSize, _ := t.start("mdia", false)
+//     if wSize == 0 {
+//             return 0
+//     }
+
+//     wSize += t.mdhd()
+//     wSize += t.copyBox(`hdlr`)
+//     wSize += t.minf()
+
+//     return t.fin(wSize)
+// }
+
+// func (t *wt) mdhd() int {
+//     wSize, lSize := t.start("mdhd", true)
+//     lSize -= t.r(16)
+//     wSize += t.w(t.buf)
+//     lSize -= t.skips(8)
+//     wSize += t.w(itob32(1000))
+//     wSize += t.w(itob32(int32(t.m["mainDuration"] / 1000)))
+//     lSize -= t.r(4)
+//     wSize += t.w(t.buf)
+//     t.skips(lSize)
+//     return t.fin(wSize)
+// }
+
+// func (t *wt) minf() int {
+//     wSize, _ := t.start("mdia", false)
+//     if wSize == 0 {
+//             return 0
+//     }
+
+//     wSize += t.copyBox(`vmhd`)
+//     wSize += t.copyBox(`dinf`)
+//     wSize += t.stbl()
+
+//     return t.fin(wSize)
+// }
+
+// func (t *wt) stbl() int {
+//     wSize, _ := t.start("stbl", false)
+//     if wSize == 0 {
+//             return 0
+//     }
+
+//     wSize += t.copyBox(`stsd`)
+//     wSize += t.stts()
+
+//     return t.fin(wSize)
+// }
+
+// func (t *wt) stts() int {
+//     wSize, lSize := t.start("stts", true)
+//     for {
+//             lSize -= t.r(1 << 20)
+//     }
+//     // lSize -= t.r(16)
+//     // wSize += t.w(t.buf)
+//     // lSize -= t.skips(8)
+//     // wSize += t.w(itob32(1000))
+//     // wSize += t.w(itob32(int32(t.m["mainDuration"] / 1000)))
+//     // lSize -= t.r(4)
+//     // wSize += t.w(t.buf)
+//     t.skips(lSize)
+//     return t.fin(wSize)
+// }
+
+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/Reply/F/fmp4Tomp4/fmp4Tomp4_test.go b/Reply/F/fmp4Tomp4/fmp4Tomp4_test.go
new file mode 100644 (file)
index 0000000..7c7d306
--- /dev/null
@@ -0,0 +1,13 @@
+package fmp4Tomp4
+
+import (
+       "context"
+       "testing"
+)
+
+func Test_conver(t *testing.T) {
+       path := "/codefile/testdata/"
+       if e := conver(context.Background(), &path); e != nil {
+               t.Fatal(e)
+       }
+}
diff --git a/Reply/F/fmp4Tomp4/reader.go b/Reply/F/fmp4Tomp4/reader.go
new file mode 100644 (file)
index 0000000..95d8125
--- /dev/null
@@ -0,0 +1,241 @@
+package fmp4Tomp4
+
+import (
+       "bytes"
+       "errors"
+       "fmt"
+
+       file "github.com/qydysky/part/file"
+       "golang.org/x/exp/slices"
+)
+
+type Box struct {
+       Name       string
+       Size       int64
+       HeaderSize int64
+       Index      int64
+}
+
+type reader struct {
+       f    *file.File
+       Boxs []Box
+       buf  []byte
+}
+
+func NewReader(f *file.File) (*reader, error) {
+       if f.Config.AutoClose {
+               return nil, errors.New("file AutoClose must false")
+       }
+       return &reader{f: f}, nil
+}
+
+func (t *reader) getN(name string, n int) (ls []Box) {
+       for i := 0; i < len(t.Boxs); i++ {
+               box := t.Boxs[i]
+               if box.Name == name {
+                       ls = append(ls, box)
+                       if len(ls) >= n {
+                               return
+                       }
+               }
+       }
+       return
+}
+
+type BoxReader struct {
+       Buf []byte
+}
+
+func (t *BoxReader) I32(offset int) int32 {
+       return btoi32(t.Buf, offset)
+}
+func (t *BoxReader) I64(offset int) int64 {
+       return btoi64(t.Buf, offset)
+}
+
+func (t *reader) Read(box Box, size int) BoxReader {
+       if size == -1 {
+               t.bufChange(int(box.Size))
+       } else {
+               t.bufChange(size)
+       }
+       t.f.SeekIndex(box.Index, file.AtOrigin)
+       t.f.Read(t.buf)
+       return BoxReader{t.buf}
+}
+
+func (t *reader) bufChange(size int) {
+       if n := size - len(t.buf); n > 0 {
+               if size <= cap(t.buf) {
+                       t.buf = t.buf[:size]
+               } else {
+                       t.buf = append(t.buf, make([]byte, n)...)
+               }
+       } else if n < 0 {
+               t.buf = t.buf[:size]
+       }
+       clear(t.buf)
+}
+
+// 正常将返回io.EOF
+func (t *reader) Parse() error {
+       stat, e := t.f.Stat()
+       if e != nil {
+               return e
+       }
+
+       var (
+               b4          = make([]byte, 4)
+               b8          = make([]byte, 8)
+               parseInside = []string{"moov", "trak", "mdia", "minf", "stbl", "moof", "traf"}
+       )
+       for i := int64(0); i < stat.Size(); {
+               boxHeaderSize := 0
+               if n, e := t.f.Read(b4); e != nil {
+                       return e
+               } else {
+                       boxHeaderSize += n
+               }
+               size := int64(btoi32(b4, 0))
+               if n, e := t.f.Read(b4); e != nil {
+                       return e
+               } else {
+                       boxHeaderSize += n
+               }
+               name := string(b4)
+               if size == 1 {
+                       if n, e := t.f.Read(b8); e != nil {
+                               return e
+                       } else {
+                               boxHeaderSize += n
+                       }
+                       size = btoi64(b8, 0)
+               }
+               t.Boxs = append(t.Boxs, Box{
+                       Name:       name,
+                       Size:       size,
+                       Index:      i,
+                       HeaderSize: int64(boxHeaderSize),
+               })
+               if !slices.Contains(parseInside, name) {
+                       seedSize := size - int64(boxHeaderSize)
+                       if e := t.f.SeekIndex(seedSize, file.AtCurrent); e != nil {
+                               return e
+                       }
+                       i += size
+               } else {
+                       i += int64(boxHeaderSize)
+               }
+       }
+       return nil
+}
+
+type Track struct {
+       chunkSize   []int64
+       sampleCount []int32
+       dur         []int32
+       size        []int32
+}
+
+func (t *reader) ParseTrun() (tracks map[int32]*Track, err error) {
+       tracks = make(map[int32]*Track)
+
+       var (
+               b4  = make([]byte, 4)
+               b8  = make([]byte, 8)
+               b12 = make([]byte, 12)
+       )
+
+       for i := 0; i < len(t.Boxs); i++ {
+               box := t.Boxs[i]
+               if box.Name == "tfhd" {
+                       _ = t.f.SeekIndex(box.Index+box.HeaderSize, file.AtOrigin)
+
+                       if _, e := t.f.Read(b8); e != nil {
+                               err = e
+                               return
+                       }
+                       trackID := btoi32(b8, 4)
+                       defaultSampleDuration := int32(0)
+                       {
+                               var offset int64
+                               if b8[3]&0x01 == 0x01 {
+                                       offset += 8
+                               }
+                               if b8[3]&0x02 == 0x02 {
+                                       offset += 4
+                               }
+                               if b8[3]&0x08 == 0x08 {
+                                       _ = t.f.SeekIndex(offset, file.AtCurrent)
+                                       if _, e := t.f.Read(b4); e != nil {
+                                               err = e
+                                               return
+                                       }
+                               }
+                               defaultSampleDuration = btoi32(b4, 0)
+                       }
+
+                       trackO, ok := tracks[trackID]
+                       if !ok {
+                               tracks[trackID] = &Track{}
+                               trackO = tracks[trackID]
+                       }
+
+                       for ; i < len(t.Boxs); i++ {
+                               box := t.Boxs[i]
+                               if box.Name == "trun" {
+                                       _ = t.f.SeekIndex(box.Index+box.HeaderSize, file.AtOrigin)
+                                       if _, e := t.f.Read(b8); e != nil {
+                                               err = e
+                                               return
+                                       }
+
+                                       _ = t.f.SeekIndex(8, file.AtCurrent)
+
+                                       var chunkSize int64
+                                       if bytes.Equal(b8[:4], []byte{0x01, 0x00, 0x0b, 0x05}) {
+                                               sampleCount := btoi32(b8, 4)
+                                               if sampleCount*12 != int32(box.Size-24) {
+                                                       err = errors.New("wrong trun trunSize not match sampleCount")
+                                                       return
+                                               }
+                                               trackO.sampleCount = append(trackO.sampleCount, sampleCount)
+                                               for i := int32(0); i < sampleCount; i++ {
+                                                       if _, e := t.f.Read(b12); e != nil {
+                                                               err = e
+                                                               return
+                                                       }
+                                                       trackO.dur = append(trackO.dur, btoi32(b12, 0))
+                                                       trackO.size = append(trackO.size, btoi32(b12, 4))
+                                                       chunkSize += int64(btoi32(b12, 4))
+                                               }
+                                       } else if bytes.Equal(b8[:4], []byte{0x01, 0x00, 0x02, 0x05}) {
+                                               sampleCount := btoi32(b8, 4)
+                                               if sampleCount*4 != int32(box.Size-24) {
+                                                       err = errors.New("wrong trun trunSize not match sampleCount")
+                                                       return
+                                               }
+                                               trackO.sampleCount = append(trackO.sampleCount, sampleCount)
+                                               for i := int32(0); i < sampleCount; i++ {
+                                                       if _, e := t.f.Read(b4); e != nil {
+                                                               err = e
+                                                               return
+                                                       }
+                                                       trackO.dur = append(trackO.dur, defaultSampleDuration)
+                                                       trackO.size = append(trackO.size, btoi32(b4, 0))
+                                                       chunkSize += int64(btoi32(b4, 0))
+                                               }
+                                       } else {
+                                               err = fmt.Errorf("wrong trun tr_flag(%v)", b8[:4])
+                                               return
+                                       }
+                                       trackO.chunkSize = append(trackO.chunkSize, chunkSize)
+
+                                       break
+                               }
+                       }
+               }
+       }
+
+       return
+}
diff --git a/Reply/F/fmp4Tomp4/reader_test.go b/Reply/F/fmp4Tomp4/reader_test.go
new file mode 100644 (file)
index 0000000..b0ae6c2
--- /dev/null
@@ -0,0 +1,26 @@
+package fmp4Tomp4
+
+import (
+       "errors"
+       "io"
+       "testing"
+
+       file "github.com/qydysky/part/file"
+)
+
+func Test_parse(t *testing.T) {
+       var read = reader{
+               f: file.New("/codefile/testdata/0.mp4", 0, false),
+       }
+       if e := read.Parse(); e != nil && !errors.Is(e, io.EOF) {
+               t.Fatal(e)
+       }
+       t.Log(len(read.Boxs))
+
+       m, e := read.ParseTrun()
+       t.Log(m)
+       t.Log(e)
+       // for i := 0; i < len(read.boxs); i++ {
+       //      t.Log(read.boxs[i].name)
+       // }
+}
diff --git a/Reply/F/fmp4Tomp4/writer.go b/Reply/F/fmp4Tomp4/writer.go
new file mode 100644 (file)
index 0000000..2e4edc4
--- /dev/null
@@ -0,0 +1,80 @@
+package fmp4Tomp4
+
+import (
+       "errors"
+       "fmt"
+
+       file "github.com/qydysky/part/file"
+       pio "github.com/qydysky/part/io"
+)
+
+var (
+       ErrFileAutoClose = errors.New("file AutoClose must false")
+       ErrSeed          = errors.New("ErrSeed")
+       ErrWrite         = errors.New("ErrWrite")
+)
+
+type boxWriter struct {
+       f  *file.File
+       wn int64
+       e  error
+       p  *boxWriter
+}
+
+func NewBoxWriter(f *file.File) (t *boxWriter, err error) {
+       if f.Config.AutoClose {
+               return nil, ErrFileAutoClose
+       }
+       t = &boxWriter{f: f}
+       return
+}
+
+func (t *boxWriter) Box(name string) (tc *boxWriter) {
+       if t.e != nil {
+               return
+       }
+       tc = &boxWriter{f: t.f, p: t, e: t.e}
+       tc.Write([]byte{0, 0, 0, 1})
+       tc.Write([]byte(name))
+       tc.wn = 0
+       tc.Write(make([]byte, 8))
+       return
+}
+
+func (t *boxWriter) Write(b []byte) (tc *boxWriter) {
+       if t.e != nil {
+               return t
+       }
+       n := 0
+       n, t.e = t.f.Write(b, false)
+       t.wn += int64(n)
+       return t
+}
+
+func (t *boxWriter) CopyFrom(f *file.File, size uint64) (tc *boxWriter) {
+       if t.e != nil {
+               return t
+       }
+       t.e = f.CopyTo(t.f, pio.CopyConfig{MaxByte: size}, false)
+       t.wn += int64(size)
+       return t
+}
+
+func (t *boxWriter) Close() error {
+       if t.e != nil {
+               return t.e
+       }
+       t.e = t.f.SeekIndex(-t.wn, file.AtCurrent)
+       if t.e != nil {
+               return errors.Join(ErrSeed, t.e, fmt.Errorf("Arg %v", -t.wn))
+       }
+       _, t.e = t.f.Write(itob64(t.wn), false)
+       if t.e != nil {
+               return errors.Join(ErrWrite, t.e, fmt.Errorf("Arg %v", -t.wn))
+       }
+       t.e = t.f.SeekIndex(t.wn-8, file.AtCurrent)
+       if t.p != nil {
+               t.p.wn += t.wn
+       }
+       return t.e
+}
diff --git a/Reply/F/fmp4Tomp4/writer_test.go b/Reply/F/fmp4Tomp4/writer_test.go
new file mode 100644 (file)
index 0000000..becd9f4
--- /dev/null
@@ -0,0 +1,34 @@
+package fmp4Tomp4
+
+import (
+       "testing"
+
+       file "github.com/qydysky/part/file"
+)
+
+func Test_w(t *testing.T) {
+       tf := file.New("1.mp4", 0, false)
+       if tf.IsExist() {
+               _ = tf.Delete()
+       }
+       defer tf.Close()
+
+       w, e := NewBoxWriter(tf)
+       if e != nil {
+               t.Fatal(e)
+       }
+
+       // ftyp
+       {
+               ftyp := w.Box("ftyp")
+               ftyp.Write([]byte("isom"))
+               ftyp.Write(itob32(512))
+               ftyp.Write([]byte("isom"))
+               ftyp.Write([]byte("iso2"))
+               ftyp.Write([]byte("avc1"))
+               ftyp.Write([]byte("mp41"))
+               if e := ftyp.Close(); e != nil {
+                       t.Fatal(e)
+               }
+       }
+}