Add support B-frames for MP4 consumer

This commit is contained in:
Alexey Khit
2023-08-22 15:55:20 +03:00
parent 90f2a9e106
commit 8cd977f7ad
7 changed files with 77 additions and 35 deletions
+2 -1
View File
@@ -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
View File
@@ -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
+1
View File
@@ -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
View File
@@ -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)
+17
View File
@@ -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
View File
@@ -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
View File
@@ -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