diff --git a/pkg/bits/reader.go b/pkg/bits/reader.go index a5bf78bb..67804be5 100644 --- a/pkg/bits/reader.go +++ b/pkg/bits/reader.go @@ -15,18 +15,39 @@ func NewReader(b []byte) *Reader { //goland:noinspection GoStandardMethods func (r *Reader) ReadByte() byte { - if r.bits == 0 { - if r.pos >= len(r.buf) { - r.EOF = true - return 0 - } - - b := r.buf[r.pos] - r.pos++ - return b + if r.bits != 0 { + return r.ReadBits8(8) } - return r.ReadBits8(8) + if r.pos >= len(r.buf) { + r.EOF = true + return 0 + } + + b := r.buf[r.pos] + r.pos++ + return b +} + +func (r *Reader) ReadUint16() uint16 { + if r.bits != 0 { + return r.ReadBits16(16) + } + return uint16(r.ReadByte())<<8 | uint16(r.ReadByte()) +} + +func (r *Reader) ReadUint24() uint32 { + if r.bits != 0 { + return r.ReadBits(24) + } + return uint32(r.ReadByte())<<16 | uint32(r.ReadByte())<<8 | uint32(r.ReadByte()) +} + +func (r *Reader) ReadUint32() uint32 { + if r.bits != 0 { + return r.ReadBits(32) + } + return uint32(r.ReadByte())<<24 | uint32(r.ReadByte())<<16 | uint32(r.ReadByte())<<8 | uint32(r.ReadByte()) } func (r *Reader) ReadBit() byte { @@ -61,6 +82,7 @@ func (r *Reader) ReadBits16(n byte) (res uint16) { return } +// ReadUEGolomb - ReadExponentialGolomb (unsigned) func (r *Reader) ReadUEGolomb() uint32 { var size byte for size = 0; size < 32; size++ { @@ -71,6 +93,7 @@ func (r *Reader) ReadUEGolomb() uint32 { return r.ReadBits(size) + (1 << size) - 1 } +// ReadSEGolomb - ReadSignedExponentialGolomb func (r *Reader) ReadSEGolomb() int32 { if b := r.ReadUEGolomb(); b%2 == 0 { return -int32(b >> 1) diff --git a/pkg/iso/atoms.go b/pkg/iso/atoms.go index cd9d7e95..bd00980f 100644 --- a/pkg/iso/atoms.go +++ b/pkg/iso/atoms.go @@ -260,6 +260,22 @@ func (m *Movie) WriteAudioTrack(id uint32, codec string, timescale uint32, chann m.EndAtom() // TRAK } +const ( + TfhdDefaultSampleDuration = 0x000008 + TfhdDefaultSampleSize = 0x000010 + TfhdDefaultSampleFlags = 0x000020 + TfhdDefaultBaseIsMoof = 0x020000 +) + +const ( + TrunDataOffset = 0x000001 + TrunFirstSampleFlags = 0x000004 + TrunSampleDuration = 0x0000100 + TrunSampleSize = 0x0000200 + TrunSampleFlags = 0x0000400 + TrunSampleCTS = 0x0000800 +) + func (m *Movie) WriteMovieFragment(seq, tid, duration, size, flags uint32, time uint64) { m.StartAtom(Moof) @@ -271,13 +287,6 @@ func (m *Movie) WriteMovieFragment(seq, tid, duration, size, flags uint32, time m.StartAtom(MoofTraf) - const ( - TfhdDefaultSampleDuration = 0x000008 - TfhdDefaultSampleSize = 0x000010 - TfhdDefaultSampleFlags = 0x000020 - TfhdDefaultBaseIsMoof = 0x020000 - ) - m.StartAtom(MoofTrafTfhd) m.Skip(1) // version m.WriteUint24( @@ -298,15 +307,6 @@ func (m *Movie) WriteMovieFragment(seq, tid, duration, size, flags uint32, time m.WriteUint64(time) // base media decode time m.EndAtom() - const ( - TrunDataOffset = 0x000001 - TrunFirstSampleFlags = 0x000004 - TrunSampleDuration = 0x0000100 - TrunSampleSize = 0x0000200 - TrunSampleFlags = 0x0000400 - TrunSampleCTS = 0x0000800 - ) - m.StartAtom(MoofTrafTrun) m.Skip(1) // version m.WriteUint24(TrunDataOffset) // flags diff --git a/pkg/iso/reader.go b/pkg/iso/reader.go new file mode 100644 index 00000000..ec436af7 --- /dev/null +++ b/pkg/iso/reader.go @@ -0,0 +1,99 @@ +package iso + +import ( + "encoding/binary" + "io" + + "github.com/AlexxIT/go2rtc/pkg/bits" +) + +type Atom struct { + Name string + Data []byte + + DecodeTime uint64 + + SamplesDuration []uint32 + SamplesSize []uint32 +} + +func DecodeAtoms(b []byte) ([]*Atom, error) { + var atoms []*Atom + for len(b) > 8 { + size := binary.BigEndian.Uint32(b) + if uint32(len(b)) < size { + return nil, io.EOF + } + + name := string(b[4:8]) + data := b[8:size] + + b = b[size:] + + switch name { + case Moof, MoofTraf: + childs, err := DecodeAtoms(data) + if err != nil { + return nil, err + } + + atoms = append(atoms, childs...) + + case MoofMfhd, MoofTrafTfhd: + continue + + case MoofTrafTfdt: + if len(data) < 8 { + return nil, io.EOF + } + + dt := binary.BigEndian.Uint64(data[4:]) + atoms = append(atoms, &Atom{Name: name, DecodeTime: dt}) + + case MoofTrafTrun: + rd := bits.NewReader(data) + + _ = rd.ReadByte() // version + flags := rd.ReadUint24() + samples := rd.ReadUint32() + + if flags&TrunDataOffset != 0 { + _ = rd.ReadUint32() // skip + } + if flags&TrunFirstSampleFlags != 0 { + _ = rd.ReadUint32() // skip + } + + atom := &Atom{Name: name} + + for i := uint32(0); i < samples; i++ { + if flags&TrunSampleDuration != 0 { + atom.SamplesDuration = append(atom.SamplesDuration, rd.ReadUint32()) + } + if flags&TrunSampleSize != 0 { + atom.SamplesSize = append(atom.SamplesSize, rd.ReadUint32()) + } + if flags&TrunSampleFlags != 0 { + _ = rd.ReadUint32() // skip + } + if flags&TrunSampleCTS != 0 { + _ = rd.ReadUint32() // skip + } + } + + if rd.EOF { + return nil, io.EOF + } + + atoms = append(atoms, atom) + + case Mdat: + atoms = append(atoms, &Atom{Name: name, Data: data}) + + default: + println("iso: unsupported atom: " + name) + } + } + + return atoms, nil +} diff --git a/pkg/ivideon/client.go b/pkg/ivideon/client.go index 2fed1726..e3bef473 100644 --- a/pkg/ivideon/client.go +++ b/pkg/ivideon/client.go @@ -13,7 +13,7 @@ import ( "github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/h264/avc" - "github.com/deepch/vdk/format/fmp4/fmp4io" + "github.com/AlexxIT/go2rtc/pkg/iso" "github.com/gorilla/websocket" "github.com/pion/rtp" ) @@ -232,31 +232,37 @@ func (c *Client) getTracks() error { func (c *Client) worker(buffer chan []byte) { for data := range buffer { - moof := &fmp4io.MovieFrag{} - if _, err := moof.Unmarshal(data, 0); err != nil { + atoms, err := iso.DecodeAtoms(data) + if err != nil { continue } - moofLen := binary.BigEndian.Uint32(data) - _ = moofLen + var trun *iso.Atom + var ts uint32 - mdat := moof.Unknowns[0] - if mdat.Tag() != fmp4io.MDAT { + for _, atom := range atoms { + switch atom.Name { + case iso.MoofTrafTrun: + trun = atom + case iso.MoofTrafTfdt: + ts = uint32(atom.DecodeTime) + case iso.Mdat: + data = atom.Data + } + } + + if trun == nil || trun.SamplesDuration == nil || trun.SamplesSize == nil { continue } - i, _ := mdat.Pos() // offset, size - data = data[i+8:] - traf := moof.Tracks[0] - ts := uint32(traf.DecodeTime.Time) + for i := 0; i < len(trun.SamplesDuration); i++ { + duration := trun.SamplesDuration[i] + size := trun.SamplesSize[i] - //println("!!!", (time.Duration(ts) * time.Millisecond).String(), time.Since(c.t0).String()) - - for _, entry := range traf.Run.Entries { // synchronize framerate for WebRTC and MSE d := time.Duration(ts)*time.Millisecond - time.Since(c.t0) if d < 0 { - d = time.Duration(entry.Duration) * time.Millisecond / 2 + d = time.Duration(duration) * time.Millisecond / 2 } time.Sleep(d) @@ -264,16 +270,12 @@ func (c *Client) worker(buffer chan []byte) { packet := &rtp.Packet{ // ivideon clockrate=1000, RTP clockrate=90000 Header: rtp.Header{Timestamp: ts * 90}, - Payload: data[:entry.Size], + Payload: data[:size], } c.receiver.WriteRTP(packet) - data = data[entry.Size:] - ts += entry.Duration - } - - if len(data) != 0 { - continue + data = data[size:] + ts += duration } } }