Fix H265 for WebRTC in Safari

This commit is contained in:
Alexey Khit
2022-12-03 11:44:26 +03:00
parent 98c5366ba9
commit a692ecd7c1
2 changed files with 47 additions and 36 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ Ultimate camera streaming application with support RTSP, WebRTC, HomeKit, FFmpeg
- streaming from [RTSP](#source-rtsp), [RTMP](#source-rtmp), [MJPEG](#source-ffmpeg), [HLS/HTTP](#source-ffmpeg), [USB Cameras](#source-ffmpeg-device) and [other sources](#module-streams) - streaming from [RTSP](#source-rtsp), [RTMP](#source-rtmp), [MJPEG](#source-ffmpeg), [HLS/HTTP](#source-ffmpeg), [USB Cameras](#source-ffmpeg-device) and [other sources](#module-streams)
- streaming to [RTSP](#module-rtsp), [WebRTC](#module-webrtc), [MSE/MP4](#module-mp4) or [MJPEG](#module-mjpeg) - streaming to [RTSP](#module-rtsp), [WebRTC](#module-webrtc), [MSE/MP4](#module-mp4) or [MJPEG](#module-mjpeg)
- first project in the World with support streaming from [HomeKit Cameras](#source-homekit) - first project in the World with support streaming from [HomeKit Cameras](#source-homekit)
- first project in the World with support H265 for WebRTC in browser (only Safari) - first project in the World with support H265 for WebRTC in browser ([read more](https://github.com/AlexxIT/Blog/issues/5))
- on the fly transcoding for unsupported codecs via [FFmpeg](#source-ffmpeg) - on the fly transcoding for unsupported codecs via [FFmpeg](#source-ffmpeg)
- multi-source 2-way [codecs negotiation](#codecs-negotiation) - multi-source 2-way [codecs negotiation](#codecs-negotiation)
- mixing tracks from different sources to single stream - mixing tracks from different sources to single stream
+46 -35
View File
@@ -4,7 +4,6 @@ import (
"encoding/binary" "encoding/binary"
"github.com/AlexxIT/go2rtc/pkg/h264" "github.com/AlexxIT/go2rtc/pkg/h264"
"github.com/AlexxIT/go2rtc/pkg/streamer" "github.com/AlexxIT/go2rtc/pkg/streamer"
"github.com/deepch/vdk/codec/h265parser"
"github.com/pion/rtp" "github.com/pion/rtp"
) )
@@ -69,68 +68,80 @@ func RTPDepay(track *streamer.Track) streamer.WrapperFunc {
} }
// SafariPay - generate Safari friendly payload for H265 // SafariPay - generate Safari friendly payload for H265
// https://github.com/AlexxIT/Blog/issues/5
func SafariPay(mtu uint16) streamer.WrapperFunc { func SafariPay(mtu uint16) streamer.WrapperFunc {
sequencer := rtp.NewRandomSequencer() sequencer := rtp.NewRandomSequencer()
size := int(mtu - 12) // rtp.Header size size := int(mtu - 12) // rtp.Header size
var buffer []byte
return func(push streamer.WriterFunc) streamer.WriterFunc { return func(push streamer.WriterFunc) streamer.WriterFunc {
return func(packet *rtp.Packet) error { return func(packet *rtp.Packet) error {
if packet.Version != h264.RTPPacketVersionAVC { if packet.Version != h264.RTPPacketVersionAVC {
return push(packet) return push(packet)
} }
data := packet.Payload // protect original packets from modification
data[0] = 0 au := make([]byte, len(packet.Payload))
data[1] = 0 copy(au, packet.Payload)
data[2] = 0
data[3] = 1
var start byte var start byte
nuType := (data[4] >> 1) & 0b111111 for i := 0; i < len(au); {
//fmt.Printf("[H265] nut: %2d, size: %6d, data: %16x\n", nut, len(data), data[4:20]) size := int(binary.BigEndian.Uint32(au[i:])) + 4
switch {
case nuType >= h265parser.NAL_UNIT_VPS && nuType <= h265parser.NAL_UNIT_PPS: // convert AVC to Annex-B
buffer = append(buffer, data...) au[i] = 0
return nil au[i+1] = 0
case nuType >= h265parser.NAL_UNIT_CODED_SLICE_BLA_W_LP && nuType <= h265parser.NAL_UNIT_CODED_SLICE_CRA: au[i+2] = 0
buffer = append([]byte{3}, buffer...) au[i+3] = 1
data = append(buffer, data...)
start = 1 switch NALUType(au[i:]) {
default: case NALUTypeIFrame, NALUTypeIFrame2, NALUTypeIFrame3:
data = append([]byte{2}, data...) start = 3
start = 0 default:
if start == 0 {
start = 2
}
}
i += size
} }
for len(data) > size { // rtp.Packet payload
b := make([]byte, 1, size)
size-- // minus header byte
for au != nil {
b[0] = start
if start > 1 {
start -= 2
}
if len(au) > size {
b = append(b, au[:size]...)
au = au[size:]
} else {
b = append(b, au...)
au = nil
}
clone := rtp.Packet{ clone := rtp.Packet{
Header: rtp.Header{ Header: rtp.Header{
Version: 2, Version: 2,
Marker: false, Marker: au == nil,
SequenceNumber: sequencer.NextSequenceNumber(), SequenceNumber: sequencer.NextSequenceNumber(),
Timestamp: packet.Timestamp, Timestamp: packet.Timestamp,
}, },
Payload: data[:size], Payload: b,
} }
if err := push(&clone); err != nil { if err := push(&clone); err != nil {
return err return err
} }
data = append([]byte{start}, data[size:]...) b = b[:1] // clear buffer
} }
clone := rtp.Packet{ return nil
Header: rtp.Header{
Version: 2,
Marker: true,
SequenceNumber: sequencer.NextSequenceNumber(),
Timestamp: packet.Timestamp,
},
Payload: data,
}
return push(&clone)
} }
} }
} }