mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2026-04-22 15:47:06 +08:00
Add support B-frames for MP4 consumer
This commit is contained in:
+2
-1
@@ -13,7 +13,8 @@ import (
|
||||
type Packet struct {
|
||||
PayloadType uint8
|
||||
Sequence uint16
|
||||
Timestamp uint32
|
||||
Timestamp uint32 // PTS if DTS == 0 else DTS
|
||||
Composition uint32 // CTS = PTS-DTS (for support B-frames)
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
|
||||
+21
-9
@@ -276,7 +276,7 @@ const (
|
||||
TrunSampleCTS = 0x0000800
|
||||
)
|
||||
|
||||
func (m *Movie) WriteMovieFragment(seq, tid, duration, size, flags uint32, time uint64) {
|
||||
func (m *Movie) WriteMovieFragment(seq, tid, duration, size, flags uint32, dts uint64, cts uint32) {
|
||||
m.StartAtom(Moof)
|
||||
|
||||
m.StartAtom(MoofMfhd)
|
||||
@@ -302,17 +302,29 @@ func (m *Movie) WriteMovieFragment(seq, tid, duration, size, flags uint32, time
|
||||
m.EndAtom()
|
||||
|
||||
m.StartAtom(MoofTrafTfdt)
|
||||
m.WriteBytes(1) // version
|
||||
m.Skip(3) // flags
|
||||
m.WriteUint64(time) // base media decode time
|
||||
m.WriteBytes(1) // version
|
||||
m.Skip(3) // flags
|
||||
m.WriteUint64(dts) // base media decode time
|
||||
m.EndAtom()
|
||||
|
||||
m.StartAtom(MoofTrafTrun)
|
||||
m.Skip(1) // version
|
||||
m.WriteUint24(TrunDataOffset) // flags
|
||||
m.WriteUint32(1) // sample count
|
||||
// data offset: current pos + uint32 len + MDAT header len
|
||||
m.WriteUint32(uint32(len(m.b)) + 4 + 8)
|
||||
m.Skip(1) // version
|
||||
|
||||
if cts == 0 {
|
||||
m.WriteUint24(TrunDataOffset) // flags
|
||||
m.WriteUint32(1) // sample count
|
||||
|
||||
// data offset: current pos + uint32 len + MDAT header len
|
||||
m.WriteUint32(uint32(len(m.b)) + 4 + 8)
|
||||
} else {
|
||||
m.WriteUint24(TrunDataOffset | TrunSampleCTS)
|
||||
m.WriteUint32(1)
|
||||
|
||||
// data offset: current pos + uint32 len + CTS + MDAT header len
|
||||
m.WriteUint32(uint32(len(m.b)) + 4 + 4 + 8)
|
||||
m.WriteUint32(cts)
|
||||
}
|
||||
|
||||
m.EndAtom() // TRUN
|
||||
|
||||
m.EndAtom() // TRAF
|
||||
|
||||
@@ -33,3 +33,4 @@ https://ffmpeg.org/ffmpeg-formats.html#Options-13
|
||||
- https://jellyfin.org/docs/general/clients/codec-support.html
|
||||
- https://github.com/StaZhu/enable-chromium-hevc-hardware-decoding
|
||||
- https://developer.mozilla.org/ru/docs/Web/Media/Formats/codecs_parameter
|
||||
- https://gstreamer-devel.narkive.com/rhkUolp2/rtp-dts-pts-result-in-varying-mp4-frame-durations
|
||||
|
||||
+3
-2
@@ -152,11 +152,12 @@ func (m *Muxer) GetPayload(trackID byte, packet *rtp.Packet) []byte {
|
||||
|
||||
mv := iso.NewMovie(1024 + size)
|
||||
mv.WriteMovieFragment(
|
||||
m.index, uint32(trackID+1), duration, uint32(size), flags, m.dts[trackID],
|
||||
// ExtensionProfile - wrong place for CTS (supported by mpegts.Demuxer)
|
||||
m.index, uint32(trackID+1), duration, uint32(size), flags, m.dts[trackID], uint32(packet.ExtensionProfile),
|
||||
)
|
||||
mv.WriteData(packet.Payload)
|
||||
|
||||
//log.Printf("[MP4] track=%d ts=%6d dur=%5d idx=%3d len=%d", trackID+1, m.dts[trackID], duration, m.index, len(packet.Payload))
|
||||
//log.Printf("[MP4] idx:%3d trk:%d dts:%6d cts:%4d dur:%5d time:%10d len:%5d", m.index, trackID+1, m.dts[trackID], packet.SSRC, duration, packet.Timestamp, len(packet.Payload))
|
||||
|
||||
m.dts[trackID] += uint64(duration)
|
||||
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
## PTS/DTS/CTS
|
||||
|
||||
```
|
||||
if DTS == 0 {
|
||||
// for I and P frames
|
||||
packet.Timestamp = PTS (presentation time)
|
||||
} else {
|
||||
// for B frames
|
||||
packet.Timestamp = DTS (decode time)
|
||||
CTS = PTS-DTS (composition time)
|
||||
}
|
||||
```
|
||||
|
||||
- MPEG-TS container uses PTS and optional DTS.
|
||||
- MP4 container uses DTS and CTS
|
||||
- RTP container uses PTS
|
||||
|
||||
## MPEG-TS
|
||||
|
||||
FFmpeg:
|
||||
|
||||
+31
-20
@@ -196,30 +196,33 @@ func (d *Demuxer) readPES(pid uint16, start bool) *rtp.Packet {
|
||||
_ = d.readBit() // Copyright
|
||||
_ = d.readBit() // Original or Copy
|
||||
|
||||
pts := d.readBit() // PTS indicator
|
||||
dts := d.readBit() // DTS indicator
|
||||
_ = d.readBit() // ESCR flag
|
||||
_ = d.readBit() // ES rate flag
|
||||
_ = d.readBit() // DSM trick mode flag
|
||||
_ = d.readBit() // Additional copy info flag
|
||||
_ = d.readBit() // CRC flag
|
||||
_ = d.readBit() // extension flag
|
||||
ptsi := d.readBit() // PTS indicator
|
||||
dtsi := d.readBit() // DTS indicator
|
||||
_ = d.readBit() // ESCR flag
|
||||
_ = d.readBit() // ES rate flag
|
||||
_ = d.readBit() // DSM trick mode flag
|
||||
_ = d.readBit() // Additional copy info flag
|
||||
_ = d.readBit() // CRC flag
|
||||
_ = d.readBit() // extension flag
|
||||
|
||||
headerSize := d.readByte() // PES header length
|
||||
|
||||
//log.Printf("[mpegts] pes=%d size=%d header=%d", pes.StreamID, packetSize, headerSize)
|
||||
|
||||
if packetSize != 0 {
|
||||
packetSize -= uint16(3 + headerSize)
|
||||
}
|
||||
|
||||
if dts != 0 {
|
||||
d.skip(5) // skip PTSorDTS
|
||||
pes.PTSorDTS = d.readTime()
|
||||
headerSize -= 10
|
||||
} else if pts != 0 {
|
||||
pes.PTSorDTS = d.readTime()
|
||||
if ptsi != 0 {
|
||||
pes.PTS = d.readTime()
|
||||
headerSize -= 5
|
||||
} else {
|
||||
pes.PTS = 0
|
||||
}
|
||||
|
||||
if dtsi != 0 {
|
||||
pes.DTS = d.readTime()
|
||||
headerSize -= 5
|
||||
} else {
|
||||
pes.DTS = 0
|
||||
}
|
||||
|
||||
d.skip(headerSize)
|
||||
@@ -325,7 +328,8 @@ type PES struct {
|
||||
StreamType byte // from PMT table
|
||||
Sequence uint16 // manual
|
||||
Timestamp uint32 // manual
|
||||
PTSorDTS uint32 // from extra header, always 90000Hz
|
||||
PTS uint32 // from extra header, always 90000Hz
|
||||
DTS uint32
|
||||
Payload []byte // from PES body
|
||||
Size int // from PES header, can be 0
|
||||
|
||||
@@ -348,11 +352,18 @@ func (p *PES) GetPacket() (pkt *rtp.Packet) {
|
||||
pkt = &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
PayloadType: p.StreamType,
|
||||
Timestamp: p.PTSorDTS,
|
||||
},
|
||||
Payload: annexb.EncodeToAVCC(p.Payload, false),
|
||||
}
|
||||
|
||||
if p.DTS != 0 {
|
||||
pkt.Timestamp = p.DTS
|
||||
// wrong place for CTS, but we don't have another one
|
||||
pkt.ExtensionProfile = uint16(p.PTS - p.DTS)
|
||||
} else {
|
||||
pkt.Timestamp = p.PTS
|
||||
}
|
||||
|
||||
case StreamTypeAAC:
|
||||
p.Sequence++
|
||||
|
||||
@@ -362,7 +373,7 @@ func (p *PES) GetPacket() (pkt *rtp.Packet) {
|
||||
Marker: true,
|
||||
PayloadType: p.StreamType,
|
||||
SequenceNumber: p.Sequence,
|
||||
Timestamp: p.PTSorDTS,
|
||||
Timestamp: p.PTS,
|
||||
//Timestamp: p.Timestamp,
|
||||
},
|
||||
Payload: aac.ADTStoRTP(p.Payload),
|
||||
@@ -379,7 +390,7 @@ func (p *PES) GetPacket() (pkt *rtp.Packet) {
|
||||
Marker: true,
|
||||
PayloadType: p.StreamType,
|
||||
SequenceNumber: p.Sequence,
|
||||
Timestamp: p.PTSorDTS,
|
||||
Timestamp: p.PTS,
|
||||
//Timestamp: p.Timestamp,
|
||||
},
|
||||
Payload: p.Payload,
|
||||
|
||||
+2
-3
@@ -51,9 +51,8 @@ func (m *Muxer) GetPayload(pid uint16, timestamp uint32, payload []byte) []byte
|
||||
}
|
||||
|
||||
if pes.Timestamp != 0 {
|
||||
pes.PTSorDTS += timestamp - pes.Timestamp
|
||||
pes.PTS += timestamp - pes.Timestamp
|
||||
}
|
||||
//log.Print(pid, pes.PTSorDTS, timestamp, pes.Timestamp)
|
||||
pes.Timestamp = timestamp
|
||||
|
||||
// min header size (3 byte) + adv header size (PES)
|
||||
@@ -74,7 +73,7 @@ func (m *Muxer) GetPayload(pid uint16, timestamp uint32, payload []byte) []byte
|
||||
b[7] = 0x80 // PTS indicator
|
||||
b[8] = 5 // PES header length
|
||||
|
||||
WriteTime(b[9:], pes.PTSorDTS)
|
||||
WriteTime(b[9:], pes.PTS)
|
||||
|
||||
pes.Payload = append(b, payload...)
|
||||
pes.Size = 1 // set PUSI in first PES
|
||||
|
||||
Reference in New Issue
Block a user