Add MP4 atoms reader
This commit is contained in:
+33
-10
@@ -15,18 +15,39 @@ func NewReader(b []byte) *Reader {
|
|||||||
|
|
||||||
//goland:noinspection GoStandardMethods
|
//goland:noinspection GoStandardMethods
|
||||||
func (r *Reader) ReadByte() byte {
|
func (r *Reader) ReadByte() byte {
|
||||||
if r.bits == 0 {
|
if r.bits != 0 {
|
||||||
if r.pos >= len(r.buf) {
|
return r.ReadBits8(8)
|
||||||
r.EOF = true
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
b := r.buf[r.pos]
|
|
||||||
r.pos++
|
|
||||||
return b
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
func (r *Reader) ReadBit() byte {
|
||||||
@@ -61,6 +82,7 @@ func (r *Reader) ReadBits16(n byte) (res uint16) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadUEGolomb - ReadExponentialGolomb (unsigned)
|
||||||
func (r *Reader) ReadUEGolomb() uint32 {
|
func (r *Reader) ReadUEGolomb() uint32 {
|
||||||
var size byte
|
var size byte
|
||||||
for size = 0; size < 32; size++ {
|
for size = 0; size < 32; size++ {
|
||||||
@@ -71,6 +93,7 @@ func (r *Reader) ReadUEGolomb() uint32 {
|
|||||||
return r.ReadBits(size) + (1 << size) - 1
|
return r.ReadBits(size) + (1 << size) - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadSEGolomb - ReadSignedExponentialGolomb
|
||||||
func (r *Reader) ReadSEGolomb() int32 {
|
func (r *Reader) ReadSEGolomb() int32 {
|
||||||
if b := r.ReadUEGolomb(); b%2 == 0 {
|
if b := r.ReadUEGolomb(); b%2 == 0 {
|
||||||
return -int32(b >> 1)
|
return -int32(b >> 1)
|
||||||
|
|||||||
+16
-16
@@ -260,6 +260,22 @@ func (m *Movie) WriteAudioTrack(id uint32, codec string, timescale uint32, chann
|
|||||||
m.EndAtom() // TRAK
|
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) {
|
func (m *Movie) WriteMovieFragment(seq, tid, duration, size, flags uint32, time uint64) {
|
||||||
m.StartAtom(Moof)
|
m.StartAtom(Moof)
|
||||||
|
|
||||||
@@ -271,13 +287,6 @@ func (m *Movie) WriteMovieFragment(seq, tid, duration, size, flags uint32, time
|
|||||||
|
|
||||||
m.StartAtom(MoofTraf)
|
m.StartAtom(MoofTraf)
|
||||||
|
|
||||||
const (
|
|
||||||
TfhdDefaultSampleDuration = 0x000008
|
|
||||||
TfhdDefaultSampleSize = 0x000010
|
|
||||||
TfhdDefaultSampleFlags = 0x000020
|
|
||||||
TfhdDefaultBaseIsMoof = 0x020000
|
|
||||||
)
|
|
||||||
|
|
||||||
m.StartAtom(MoofTrafTfhd)
|
m.StartAtom(MoofTrafTfhd)
|
||||||
m.Skip(1) // version
|
m.Skip(1) // version
|
||||||
m.WriteUint24(
|
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.WriteUint64(time) // base media decode time
|
||||||
m.EndAtom()
|
m.EndAtom()
|
||||||
|
|
||||||
const (
|
|
||||||
TrunDataOffset = 0x000001
|
|
||||||
TrunFirstSampleFlags = 0x000004
|
|
||||||
TrunSampleDuration = 0x0000100
|
|
||||||
TrunSampleSize = 0x0000200
|
|
||||||
TrunSampleFlags = 0x0000400
|
|
||||||
TrunSampleCTS = 0x0000800
|
|
||||||
)
|
|
||||||
|
|
||||||
m.StartAtom(MoofTrafTrun)
|
m.StartAtom(MoofTrafTrun)
|
||||||
m.Skip(1) // version
|
m.Skip(1) // version
|
||||||
m.WriteUint24(TrunDataOffset) // flags
|
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/core"
|
||||||
"github.com/AlexxIT/go2rtc/pkg/h264/avc"
|
"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/gorilla/websocket"
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
)
|
)
|
||||||
@@ -232,31 +232,37 @@ func (c *Client) getTracks() error {
|
|||||||
|
|
||||||
func (c *Client) worker(buffer chan []byte) {
|
func (c *Client) worker(buffer chan []byte) {
|
||||||
for data := range buffer {
|
for data := range buffer {
|
||||||
moof := &fmp4io.MovieFrag{}
|
atoms, err := iso.DecodeAtoms(data)
|
||||||
if _, err := moof.Unmarshal(data, 0); err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
moofLen := binary.BigEndian.Uint32(data)
|
var trun *iso.Atom
|
||||||
_ = moofLen
|
var ts uint32
|
||||||
|
|
||||||
mdat := moof.Unknowns[0]
|
for _, atom := range atoms {
|
||||||
if mdat.Tag() != fmp4io.MDAT {
|
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
|
continue
|
||||||
}
|
}
|
||||||
i, _ := mdat.Pos() // offset, size
|
|
||||||
data = data[i+8:]
|
|
||||||
|
|
||||||
traf := moof.Tracks[0]
|
for i := 0; i < len(trun.SamplesDuration); i++ {
|
||||||
ts := uint32(traf.DecodeTime.Time)
|
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
|
// synchronize framerate for WebRTC and MSE
|
||||||
d := time.Duration(ts)*time.Millisecond - time.Since(c.t0)
|
d := time.Duration(ts)*time.Millisecond - time.Since(c.t0)
|
||||||
if d < 0 {
|
if d < 0 {
|
||||||
d = time.Duration(entry.Duration) * time.Millisecond / 2
|
d = time.Duration(duration) * time.Millisecond / 2
|
||||||
}
|
}
|
||||||
time.Sleep(d)
|
time.Sleep(d)
|
||||||
|
|
||||||
@@ -264,16 +270,12 @@ func (c *Client) worker(buffer chan []byte) {
|
|||||||
packet := &rtp.Packet{
|
packet := &rtp.Packet{
|
||||||
// ivideon clockrate=1000, RTP clockrate=90000
|
// ivideon clockrate=1000, RTP clockrate=90000
|
||||||
Header: rtp.Header{Timestamp: ts * 90},
|
Header: rtp.Header{Timestamp: ts * 90},
|
||||||
Payload: data[:entry.Size],
|
Payload: data[:size],
|
||||||
}
|
}
|
||||||
c.receiver.WriteRTP(packet)
|
c.receiver.WriteRTP(packet)
|
||||||
|
|
||||||
data = data[entry.Size:]
|
data = data[size:]
|
||||||
ts += entry.Duration
|
ts += duration
|
||||||
}
|
|
||||||
|
|
||||||
if len(data) != 0 {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user