From 777d0d72bd43a42c3536236465c2df97569c1169 Mon Sep 17 00:00:00 2001 From: qydysky Date: Tue, 3 Oct 2023 20:33:44 +0800 Subject: [PATCH] =?utf8?q?Fix=20=20=E9=87=8D=E7=BD=AE=E6=97=B6=E9=97=B4?= =?utf8?q?=E6=88=B3=E5=90=8E=E7=9A=84=E6=96=87=E4=BB=B6=E5=AF=BC=E8=87=B4f?= =?utf8?q?fmpeg=20error=20#88?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- Reply/F.go | 6 +- Reply/F/comp.go | 1 + Reply/F/fmp4Tomp4/fmp4Tomp4.go | 601 ++++++++++++++++++++++++++++ Reply/F/fmp4Tomp4/fmp4Tomp4_test.go | 13 + Reply/F/fmp4Tomp4/reader.go | 241 +++++++++++ Reply/F/fmp4Tomp4/reader_test.go | 26 ++ Reply/F/fmp4Tomp4/writer.go | 80 ++++ Reply/F/fmp4Tomp4/writer_test.go | 34 ++ 8 files changed, 999 insertions(+), 3 deletions(-) create mode 100644 Reply/F/fmp4Tomp4/fmp4Tomp4.go create mode 100644 Reply/F/fmp4Tomp4/fmp4Tomp4_test.go create mode 100644 Reply/F/fmp4Tomp4/reader.go create mode 100644 Reply/F/fmp4Tomp4/reader_test.go create mode 100644 Reply/F/fmp4Tomp4/writer.go create mode 100644 Reply/F/fmp4Tomp4/writer_test.go diff --git a/Reply/F.go b/Reply/F.go index 9ed8969..c4c5e35 100644 --- a/Reply/F.go +++ b/Reply/F.go @@ -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) diff --git a/Reply/F/comp.go b/Reply/F/comp.go index afa099e..70a05e8 100644 --- a/Reply/F/comp.go +++ b/Reply/F/comp.go @@ -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 index 0000000..cbc23e3 --- /dev/null +++ b/Reply/F/fmp4Tomp4/fmp4Tomp4.go @@ -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 index 0000000..7c7d306 --- /dev/null +++ b/Reply/F/fmp4Tomp4/fmp4Tomp4_test.go @@ -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 index 0000000..95d8125 --- /dev/null +++ b/Reply/F/fmp4Tomp4/reader.go @@ -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 index 0000000..b0ae6c2 --- /dev/null +++ b/Reply/F/fmp4Tomp4/reader_test.go @@ -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 index 0000000..2e4edc4 --- /dev/null +++ b/Reply/F/fmp4Tomp4/writer.go @@ -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 index 0000000..becd9f4 --- /dev/null +++ b/Reply/F/fmp4Tomp4/writer_test.go @@ -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) + } + } +} -- 2.39.2