mirror of
https://github.com/aler9/gortsplib
synced 2025-10-05 15:16:51 +08:00
client: turn ReadFrames into OnPacketRTP, OnPacketRTCP
This commit is contained in:
@@ -25,31 +25,6 @@ const (
|
||||
)
|
||||
|
||||
func main() {
|
||||
c := gortsplib.Client{}
|
||||
|
||||
// connect to the server and start reading all tracks
|
||||
err := c.DialRead(inputStream)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
// find the H264 track
|
||||
var h264TrackID int = -1
|
||||
var h264Conf *gortsplib.TrackConfigH264
|
||||
for i, track := range c.Tracks() {
|
||||
if track.IsH264() {
|
||||
h264TrackID = i
|
||||
h264Conf, err = track.ExtractConfigH264()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if h264TrackID < 0 {
|
||||
panic(fmt.Errorf("H264 track not found"))
|
||||
}
|
||||
|
||||
// open output file
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
@@ -73,100 +48,128 @@ func main() {
|
||||
})
|
||||
mux.SetPCRPID(256)
|
||||
|
||||
// read packets
|
||||
err = c.ReadFrames(func(trackID int, streamType gortsplib.StreamType, payload []byte) {
|
||||
if trackID != h264TrackID {
|
||||
return
|
||||
}
|
||||
var h264TrackID int = -1
|
||||
var h264Conf *gortsplib.TrackConfigH264
|
||||
|
||||
if streamType != gortsplib.StreamTypeRTP {
|
||||
return
|
||||
}
|
||||
|
||||
// parse RTP packets
|
||||
var pkt rtp.Packet
|
||||
err := pkt.Unmarshal(payload)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// decode H264 NALUs from RTP packets
|
||||
nalus, pts, err := dec.DecodeUntilMarker(&pkt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !firstPacketWritten {
|
||||
firstPacketWritten = true
|
||||
startPTS = pts
|
||||
}
|
||||
|
||||
// check whether there's an IDR
|
||||
idrPresent := func() bool {
|
||||
for _, nalu := range nalus {
|
||||
typ := h264.NALUType(nalu[0] & 0x1F)
|
||||
if typ == h264.NALUTypeIDR {
|
||||
return true
|
||||
c := gortsplib.Client{
|
||||
// called before sending a PLAY request
|
||||
OnPlay: func(c *gortsplib.Client) {
|
||||
// find the H264 track
|
||||
for i, track := range c.Tracks() {
|
||||
if track.IsH264() {
|
||||
h264TrackID = i
|
||||
var err error
|
||||
h264Conf, err = track.ExtractConfigH264()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}()
|
||||
|
||||
// prepend an AUD. This is required by some players
|
||||
filteredNALUs := [][]byte{
|
||||
{byte(h264.NALUTypeAccessUnitDelimiter), 240},
|
||||
}
|
||||
|
||||
for _, nalu := range nalus {
|
||||
// remove existing SPS, PPS, AUD
|
||||
typ := h264.NALUType(nalu[0] & 0x1F)
|
||||
switch typ {
|
||||
case h264.NALUTypeSPS, h264.NALUTypePPS, h264.NALUTypeAccessUnitDelimiter:
|
||||
continue
|
||||
if h264TrackID < 0 {
|
||||
panic(fmt.Errorf("H264 track not found"))
|
||||
}
|
||||
},
|
||||
// called when a RTP packet arrives
|
||||
OnPacketRTP: func(c *gortsplib.Client, trackID int, payload []byte) {
|
||||
if trackID != h264TrackID {
|
||||
return
|
||||
}
|
||||
|
||||
// add SPS and PPS before every IDR
|
||||
if typ == h264.NALUTypeIDR {
|
||||
filteredNALUs = append(filteredNALUs, h264Conf.SPS)
|
||||
filteredNALUs = append(filteredNALUs, h264Conf.PPS)
|
||||
// parse RTP packets
|
||||
var pkt rtp.Packet
|
||||
err := pkt.Unmarshal(payload)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
filteredNALUs = append(filteredNALUs, nalu)
|
||||
}
|
||||
// decode H264 NALUs from RTP packets
|
||||
nalus, pts, err := dec.DecodeUntilMarker(&pkt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// encode into Annex-B
|
||||
enc, err := h264.EncodeAnnexB(filteredNALUs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if !firstPacketWritten {
|
||||
firstPacketWritten = true
|
||||
startPTS = pts
|
||||
}
|
||||
|
||||
dts := dtsEst.Feed(pts - startPTS)
|
||||
pts = pts - startPTS
|
||||
// check whether there's an IDR
|
||||
idrPresent := func() bool {
|
||||
for _, nalu := range nalus {
|
||||
typ := h264.NALUType(nalu[0] & 0x1F)
|
||||
if typ == h264.NALUTypeIDR {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}()
|
||||
|
||||
// write TS packet
|
||||
_, err = mux.WriteData(&astits.MuxerData{
|
||||
PID: 256,
|
||||
AdaptationField: &astits.PacketAdaptationField{
|
||||
RandomAccessIndicator: idrPresent,
|
||||
},
|
||||
PES: &astits.PESData{
|
||||
Header: &astits.PESHeader{
|
||||
OptionalHeader: &astits.PESOptionalHeader{
|
||||
MarkerBits: 2,
|
||||
PTSDTSIndicator: astits.PTSDTSIndicatorBothPresent,
|
||||
DTS: &astits.ClockReference{Base: int64(dts.Seconds() * 90000)},
|
||||
PTS: &astits.ClockReference{Base: int64(pts.Seconds() * 90000)},
|
||||
},
|
||||
StreamID: 224, // video
|
||||
// prepend an AUD. This is required by some players
|
||||
filteredNALUs := [][]byte{
|
||||
{byte(h264.NALUTypeAccessUnitDelimiter), 240},
|
||||
}
|
||||
|
||||
for _, nalu := range nalus {
|
||||
// remove existing SPS, PPS, AUD
|
||||
typ := h264.NALUType(nalu[0] & 0x1F)
|
||||
switch typ {
|
||||
case h264.NALUTypeSPS, h264.NALUTypePPS, h264.NALUTypeAccessUnitDelimiter:
|
||||
continue
|
||||
}
|
||||
|
||||
// add SPS and PPS before every IDR
|
||||
if typ == h264.NALUTypeIDR {
|
||||
filteredNALUs = append(filteredNALUs, h264Conf.SPS)
|
||||
filteredNALUs = append(filteredNALUs, h264Conf.PPS)
|
||||
}
|
||||
|
||||
filteredNALUs = append(filteredNALUs, nalu)
|
||||
}
|
||||
|
||||
// encode into Annex-B
|
||||
enc, err := h264.EncodeAnnexB(filteredNALUs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
dts := dtsEst.Feed(pts - startPTS)
|
||||
pts = pts - startPTS
|
||||
|
||||
// write TS packet
|
||||
_, err = mux.WriteData(&astits.MuxerData{
|
||||
PID: 256,
|
||||
AdaptationField: &astits.PacketAdaptationField{
|
||||
RandomAccessIndicator: idrPresent,
|
||||
},
|
||||
Data: enc,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
PES: &astits.PESData{
|
||||
Header: &astits.PESHeader{
|
||||
OptionalHeader: &astits.PESOptionalHeader{
|
||||
MarkerBits: 2,
|
||||
PTSDTSIndicator: astits.PTSDTSIndicatorBothPresent,
|
||||
DTS: &astits.ClockReference{Base: int64(dts.Seconds() * 90000)},
|
||||
PTS: &astits.ClockReference{Base: int64(pts.Seconds() * 90000)},
|
||||
},
|
||||
StreamID: 224, // video
|
||||
},
|
||||
Data: enc,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("wrote ts packet")
|
||||
})
|
||||
fmt.Println("wrote ts packet")
|
||||
},
|
||||
}
|
||||
|
||||
// connect to the server and start reading all tracks
|
||||
err = c.DialRead(inputStream)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
// read packets
|
||||
err = c.ReadFrames()
|
||||
panic(err)
|
||||
}
|
||||
|
Reference in New Issue
Block a user