Add MP4 atoms reader
This commit is contained in:
+33
-10
@@ -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)
|
||||
|
||||
+16
-16
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
+24
-22
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user