snapshot of https://github.com/pion/webrtc.git (main, 2026-09-15; blobs > 131072B omitted)
Some checks failed
REUSE Compliance Check / lint (push) Failing after 0s
Browser E2E / Test (push) Failing after 3s
Test / test (1.24) (push) Failing after 0s
Test / test-i386 (1.25) (push) Failing after 0s
Test / test-i386 (1.24) (push) Failing after 0s
Test / test-windows (1.25) (push) Failing after 0s
Test / test-windows (1.24) (push) Failing after 0s
Test / test (1.25) (push) Failing after 0s
Examples Tests / Test (push) Failing after 3s
Test / test-macos (1.25) (push) Failing after 0s
Test / test-macos (1.24) (push) Failing after 0s
Go mod tidy / tidy (push) Failing after 0s
Test / test-wasm (push) Failing after 0s
CodeQL / analyze (push) Failing after 1s
Fuzz / fuzz (push) Failing after 0s

This commit is contained in:
upstream-mirror
2026-09-15 20:06:07 +05:00
commit 8acb1a3a29
394 changed files with 78746 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
# repacketize
repacketize demonstrates how many video codecs can be received, depacketized and packetized by Pion over RTP.
## Instructions
### Download and run repacketize
```
go install github.com/pion/webrtc/v4/examples/repacketize@latest
```
### Open repacketize local page
[localhost:8080](http://localhost:8080/) you should see a dropdown selector and a "Start" button.
### Select one of the video codecs
Availability of codecs depends on the browser you are accessing the page from; Safari and Google Chrome on Windows should support all of them.
### Hit 'Start', enjoy your video
Your browser should send video to Pion, and then it will be relayed right back to you.
Congrats, you have used Pion WebRTC! Now start building something cool.

View File

@@ -0,0 +1,28 @@
<!--
SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
SPDX-License-Identifier: MIT
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Pion - Repacketize Example</title>
<script src="index.js" defer></script>
</head>
<body>
<label for="codec">Codec: </label>
<select id="codec">
<option value="H264" selected>H.264</option>
<option value="H265">H.265</option>
<option value="VP8">VP8</option>
<option value="VP9">VP9</option>
<option value="AV1">AV1</option>
</select>
<button id="start">Start</button>
<br />
<video id="screen" autoplay hidden></video>
<video id="received" autoplay hidden></video>
<br />
<div id="logs"></div>
</body>
</html>

View File

@@ -0,0 +1,92 @@
/* eslint-env browser */
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] })
function log (value) {
console.log(value)
const line = document.createElement('pre')
line.innerText = value.toString()
document.getElementById('logs').appendChild(line)
}
// Reorders available codecs so that the selected codec is first on the list
function codecs () {
const option = document.getElementById('codec').value.toLowerCase()
const caps = RTCRtpSender.getCapabilities('video')?.codecs ?? []
const primary = caps.filter((c) => c.mimeType.toLowerCase() === `video/${option}`)
if (primary.length === 0) {
alert('Unsupported codec selected')
throw new DOMException('Unsupported codec')
}
const primaryPayloads = new Set(primary.map((c) => c.preferredPayloadType))
const pairedRtx = caps.filter(
(c) => c.mimeType.toLowerCase() === 'video/rtx' && c.sdpFmtpLine?.includes('apt=') && primaryPayloads.has(Number(c.sdpFmtpLine.split('apt=')[1]))
)
const rest = caps.filter((c) => !primary.includes(c) && !pairedRtx.includes(c))
const ordered = [...primary, ...pairedRtx, ...rest]
console.log('Codec preferences', ordered)
return ordered
}
async function start () {
log('Starting...')
const screenSrc = await navigator.mediaDevices.getDisplayMedia({ video: { frameRate: 30, height: 360 } })
const video = document.getElementById('screen')
video.hidden = false
video.srcObject = screenSrc
const trans = pc.addTransceiver(screenSrc.getVideoTracks()[0], { direction: 'sendrecv' })
trans.setCodecPreferences(codecs())
const offer = await pc.createOffer()
await pc.setLocalDescription(offer)
log('Gathering ICE candidates')
await new Promise((resolve) => {
pc.onicecandidate = (ev) => {
if (ev.candidate == null) {
resolve()
}
}
if (pc.iceGatheringState === 'complete') {
resolve()
}
})
log('Done gathering ICE candidates')
log('Sending SDP')
const resp = await fetch('/sdp', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(pc.localDescription)
})
const sdp = await resp.json()
pc.setRemoteDescription(sdp)
}
pc.addEventListener('iceconnectionstatechange', () => {
console.log('ICE connection state', pc.iceConnectionState)
})
pc.ontrack = (t) => {
log('New track received')
const received = document.getElementById('received')
received.hidden = false
received.srcObject = t.streams[0]
pc.getStats().then((stats) => {
const codec = Array.from(stats.values()).find((e) => e.type === 'codec')
log(`New track codec: ${codec.mimeType}`)
})
}
document.getElementById('start').onclick = start

View File

@@ -0,0 +1,217 @@
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build !js
// repacketize demonstrates how many video codecs can be received, depacketized
// and packetized by Pion over RTP.
package main
import (
"embed"
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/pion/interceptor"
"github.com/pion/rtcp"
"github.com/pion/rtp"
"github.com/pion/rtp/codecs"
"github.com/pion/webrtc/v4"
"github.com/pion/webrtc/v4/pkg/media"
"github.com/pion/webrtc/v4/pkg/media/samplebuilder"
)
//go:embed index.html index.js
var web embed.FS
func main() {
fs := http.FileServer(http.FS(web))
// Serve web files
http.Handle("/", fs)
// Receive SDP offer from browser and send the answer back. This should ideally
// be done with WHIP/WHEP but for the purposes of this example, this is good enough.
// Check out the whip-whep example to see how to do that instead.
http.HandleFunc("/sdp", func(w http.ResponseWriter, r *http.Request) { //nolint
w.Header().Set("Access-Control-Allow-Headers", "*")
w.Header().Set("Access-Control-Allow-Origin", "*")
switch r.Method {
case "POST":
pc := createRTCConn()
sdp := &webrtc.SessionDescription{}
if err := json.NewDecoder(r.Body).Decode(sdp); err != nil {
panic(err)
}
if err := pc.SetRemoteDescription(*sdp); err != nil {
panic(err)
}
gather := webrtc.GatheringCompletePromise(pc)
answer, err := pc.CreateAnswer(nil)
if err != nil {
panic(err)
}
err = pc.SetLocalDescription(answer)
if err != nil {
panic(err)
}
<-gather
resp, err := json.Marshal(pc.LocalDescription())
if err != nil {
panic(err)
}
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write(resp); err != nil {
panic(err)
}
return
case "OPTIONS":
w.WriteHeader(http.StatusOK)
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
})
fmt.Println("Open http://localhost:8080 to access this example")
panic(http.ListenAndServe(":8080", nil)) //nolint:gosec // example
}
func createRTCConn() *webrtc.PeerConnection { //nolint:cyclop
config := webrtc.Configuration{
ICEServers: []webrtc.ICEServer{
{URLs: []string{"stun:stun.l.google.com:19302"}},
},
}
mediaEngine := webrtc.MediaEngine{}
if err := mediaEngine.RegisterDefaultCodecs(); err != nil {
panic(err)
}
// Create a InterceptorRegistry. This is the user configurable RTP/RTCP Pipeline.
// This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection`
// this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry
// for each PeerConnection.
ir := &interceptor.Registry{}
// Use the default set of Interceptors
if err := webrtc.RegisterDefaultInterceptors(&mediaEngine, ir); err != nil {
panic(err)
}
api := webrtc.NewAPI(
webrtc.WithMediaEngine(&mediaEngine),
webrtc.WithInterceptorRegistry(ir),
)
pc, err := api.NewPeerConnection(config)
if err != nil {
panic(err)
}
// Create Transceiver that we send and receive video to/from browser
trans, err := pc.AddTransceiverFromKind(
webrtc.RTPCodecTypeVideo,
webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendrecv},
)
if err != nil {
panic(err)
}
go func() {
buf := make([]byte, 0)
for {
_, _, err := trans.Sender().Read(buf)
if err != nil {
return
}
}
}()
pc.OnTrack(func(tr *webrtc.TrackRemote, r *webrtc.RTPReceiver) {
var depacketizer rtp.Depacketizer
switch tr.Codec().MimeType {
case webrtc.MimeTypeAV1:
depacketizer = &codecs.AV1Depacketizer{}
case webrtc.MimeTypeVP8:
depacketizer = &codecs.VP8Packet{}
case webrtc.MimeTypeVP9:
depacketizer = &codecs.VP9Packet{}
case webrtc.MimeTypeH264:
depacketizer = &codecs.H264Packet{}
case webrtc.MimeTypeH265:
depacketizer = &codecs.H265Depacketizer{}
default:
return
}
// Request a new I-frame every 500ms
go func() {
t := time.NewTicker(time.Millisecond * 500)
defer t.Stop()
for range t.C {
if err := pc.WriteRTCP([]rtcp.Packet{
&rtcp.PictureLossIndication{MediaSSRC: uint32(tr.SSRC())},
}); err != nil {
panic(err)
}
}
}()
// New track with the same codec as the received track
newTrack, err := webrtc.NewTrackLocalStaticSample(
tr.Codec().RTPCodecCapability,
"restream",
"pion",
)
if err != nil {
panic(err)
}
// Add it to the transceiver
if err := trans.Sender().ReplaceTrack(newTrack); err != nil {
panic(err)
}
fmt.Println("New track:", tr.Codec().MimeType)
// SampleBuilder reorders and depacketizes incoming RTP packets
sb := samplebuilder.New(100, depacketizer, tr.Codec().ClockRate)
for rtp, _, readErr := tr.ReadRTP(); readErr == nil; rtp, _, readErr = tr.ReadRTP() {
sb.Push(rtp)
for sample := sb.Pop(); sample != nil; sample = sb.Pop() {
// WriteSample takes sample.Data and packetizes it according to the track's codec
err := newTrack.WriteSample(media.Sample{
Data: sample.Data,
Duration: sample.Duration,
})
if err != nil {
panic(err)
}
}
}
fmt.Println("Track ended")
})
pc.OnICEConnectionStateChange(func(is webrtc.ICEConnectionState) {
fmt.Println("ICE connection state changed:", is)
})
pc.OnConnectionStateChange(func(pcs webrtc.PeerConnectionState) {
fmt.Println("Connection state changed:", pcs)
})
return pc
}