diff --git a/pkg/h264/README.md b/pkg/h264/README.md index 661741d0..ca8cb2fd 100644 --- a/pkg/h264/README.md +++ b/pkg/h264/README.md @@ -36,3 +36,4 @@ H.264/high | avc1.6400xx | FFmpeg superfast - [AVC levels](https://en.wikipedia.org/wiki/Advanced_Video_Coding#Levels) - [AVC profiles table](https://developer.mozilla.org/ru/docs/Web/Media/Formats/codecs_parameter) - [Supported Media for Google Cast](https://developers.google.com/cast/docs/media) +- [Two stream formats, Annex-B, AVCC (H.264) and HVCC (H.265)](https://www.programmersought.com/article/3901815022/) diff --git a/pkg/h265/rtp.go b/pkg/h265/rtp.go index b30ee303..47c0d9fd 100644 --- a/pkg/h265/rtp.go +++ b/pkg/h265/rtp.go @@ -21,10 +21,6 @@ func RTPDepay(track *streamer.Track) streamer.WrapperFunc { //) switch naluType { - case h265parser.NAL_UNIT_CODED_SLICE_TRAIL_R: - case h265parser.NAL_UNIT_VPS: - case h265parser.NAL_UNIT_SPS: - case h265parser.NAL_UNIT_PPS: case h265parser.NAL_UNIT_UNSPECIFIED_49: data := packet.Payload switch data[2] >> 6 { @@ -55,3 +51,69 @@ func RTPDepay(track *streamer.Track) streamer.WrapperFunc { } } } + +// SafariPay - generate Safari friendly payload for H265 +func SafariPay(mtu uint16) streamer.WrapperFunc { + sequencer := rtp.NewRandomSequencer() + size := int(mtu - 12) // rtp.Header size + + var buffer []byte + + return func(push streamer.WriterFunc) streamer.WriterFunc { + return func(packet *rtp.Packet) error { + if packet.Version != h264.RTPPacketVersionAVC { + return push(packet) + } + + data := packet.Payload + data[0] = 0 + data[1] = 0 + data[2] = 0 + data[3] = 1 + + var start byte + + nut := (data[4] >> 1) & 0b111111 + switch nut { + case h265parser.NAL_UNIT_VPS, h265parser.NAL_UNIT_SPS, h265parser.NAL_UNIT_PPS: + buffer = append(buffer, data...) + return nil + case h265parser.NAL_UNIT_CODED_SLICE_IDR_W_RADL: + buffer = append([]byte{3}, buffer...) + data = append(buffer, data...) + start = 1 + default: + data = append([]byte{2}, data...) + start = 0 + } + + for len(data) > size { + clone := rtp.Packet{ + Header: rtp.Header{ + Version: 2, + Marker: false, + SequenceNumber: sequencer.NextSequenceNumber(), + Timestamp: packet.Timestamp, + }, + Payload: data[:size], + } + if err := push(&clone); err != nil { + return err + } + + data = append([]byte{start}, data[size:]...) + } + + clone := rtp.Packet{ + Header: rtp.Header{ + Version: 2, + Marker: true, + SequenceNumber: sequencer.NextSequenceNumber(), + Timestamp: packet.Timestamp, + }, + Payload: data, + } + return push(&clone) + } + } +} diff --git a/pkg/webrtc/conn.go b/pkg/webrtc/conn.go index a80561d7..5cc50a51 100644 --- a/pkg/webrtc/conn.go +++ b/pkg/webrtc/conn.go @@ -59,7 +59,6 @@ func (c *Conn) Init() { } fmt.Printf("TODO: webrtc ontrack %+v\n", remote) - fmt.Printf("TODO: webrtc ontrack %#v\n", remote) }) // OK connection: diff --git a/pkg/webrtc/streamer.go b/pkg/webrtc/consumer.go similarity index 89% rename from pkg/webrtc/streamer.go rename to pkg/webrtc/consumer.go index ef4881b1..8625f5ac 100644 --- a/pkg/webrtc/streamer.go +++ b/pkg/webrtc/consumer.go @@ -3,6 +3,7 @@ package webrtc import ( "encoding/json" "github.com/AlexxIT/go2rtc/pkg/h264" + "github.com/AlexxIT/go2rtc/pkg/h265" "github.com/AlexxIT/go2rtc/pkg/streamer" "github.com/pion/rtp" "github.com/pion/webrtc/v3" @@ -51,7 +52,8 @@ func (c *Conn) AddTrack(media *streamer.Media, track *streamer.Track) *streamer. return trackLocal.WriteRTP(packet) } - if codec.Name == streamer.CodecH264 { + switch codec.Name { + case streamer.CodecH264: wrapper := h264.RTPPay(1200) push = wrapper(push) @@ -61,6 +63,15 @@ func (c *Conn) AddTrack(media *streamer.Media, track *streamer.Track) *streamer. wrapper = h264.RTPDepay(track) } push = wrapper(push) + + case streamer.CodecH265: + // SafariPay because it is the only browser in the world + // that supports WebRTC + H265 + wrapper := h265.SafariPay(1200) + push = wrapper(push) + + wrapper = h265.RTPDepay(track) + push = wrapper(push) } track = track.Bind(push)