直播:后台 JWT 推流、前台画中画;WebRTC 服务与 Nginx WebSocket 代理

Made-with: Cursor
This commit is contained in:
whm
2026-03-25 15:00:14 +08:00
parent b83ec91b1a
commit 7811adca66
1050 changed files with 146524 additions and 37 deletions

View File

@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package fmtp
type av1FMTP struct {
parameters map[string]string
}
func (h *av1FMTP) MimeType() string {
return "video/av1"
}
func (h *av1FMTP) Match(b FMTP) bool {
c, ok := b.(*av1FMTP)
if !ok {
return false
}
// RTP Payload Format For AV1 (v1.0)
// https://aomediacodec.github.io/av1-rtp-spec/
// If the profile parameter is not present, it MUST be inferred to be 0 (“Main” profile).
hProfile, ok := h.parameters["profile"]
if !ok {
hProfile = "0"
}
cProfile, ok := c.parameters["profile"]
if !ok {
cProfile = "0"
}
if hProfile != cProfile {
return false
}
return true
}
func (h *av1FMTP) Parameter(key string) (string, bool) {
v, ok := h.parameters[key]
return v, ok
}

View File

@@ -0,0 +1,112 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// Package fmtp implements per codec parsing of fmtp lines
package fmtp
import (
"strings"
)
func parseParameters(line string) map[string]string {
parameters := make(map[string]string)
for _, p := range strings.Split(line, ";") {
pp := strings.SplitN(strings.TrimSpace(p), "=", 2)
key := strings.ToLower(pp[0])
var value string
if len(pp) > 1 {
value = pp[1]
}
parameters[key] = value
}
return parameters
}
// FMTP interface for implementing custom
// FMTP parsers based on MimeType
type FMTP interface {
// MimeType returns the MimeType associated with
// the fmtp
MimeType() string
// Match compares two fmtp descriptions for
// compatibility based on the MimeType
Match(f FMTP) bool
// Parameter returns a value for the associated key
// if contained in the parsed fmtp string
Parameter(key string) (string, bool)
}
// Parse parses an fmtp string based on the MimeType
func Parse(mimeType, line string) FMTP {
var f FMTP
parameters := parseParameters(line)
switch {
case strings.EqualFold(mimeType, "video/h264"):
f = &h264FMTP{
parameters: parameters,
}
case strings.EqualFold(mimeType, "video/vp9"):
f = &vp9FMTP{
parameters: parameters,
}
case strings.EqualFold(mimeType, "video/av1"):
f = &av1FMTP{
parameters: parameters,
}
default:
f = &genericFMTP{
mimeType: mimeType,
parameters: parameters,
}
}
return f
}
type genericFMTP struct {
mimeType string
parameters map[string]string
}
func (g *genericFMTP) MimeType() string {
return g.mimeType
}
// Match returns true if g and b are compatible fmtp descriptions
// The generic implementation is used for MimeTypes that are not defined
func (g *genericFMTP) Match(b FMTP) bool {
c, ok := b.(*genericFMTP)
if !ok {
return false
}
if !strings.EqualFold(g.mimeType, c.MimeType()) {
return false
}
for k, v := range g.parameters {
if vb, ok := c.parameters[k]; ok && !strings.EqualFold(vb, v) {
return false
}
}
for k, v := range c.parameters {
if va, ok := g.parameters[k]; ok && !strings.EqualFold(va, v) {
return false
}
}
return true
}
func (g *genericFMTP) Parameter(key string) (string, bool) {
v, ok := g.parameters[key]
return v, ok
}

View File

@@ -0,0 +1,84 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package fmtp
import (
"encoding/hex"
)
func profileLevelIDMatches(a, b string) bool {
aa, err := hex.DecodeString(a)
if err != nil || len(aa) < 2 {
return false
}
bb, err := hex.DecodeString(b)
if err != nil || len(bb) < 2 {
return false
}
return aa[0] == bb[0] && aa[1] == bb[1]
}
type h264FMTP struct {
parameters map[string]string
}
func (h *h264FMTP) MimeType() string {
return "video/h264"
}
// Match returns true if h and b are compatible fmtp descriptions
// Based on RFC6184 Section 8.2.2:
//
// The parameters identifying a media format configuration for H.264
// are profile-level-id and packetization-mode. These media format
// configuration parameters (except for the level part of profile-
// level-id) MUST be used symmetrically; that is, the answerer MUST
// either maintain all configuration parameters or remove the media
// format (payload type) completely if one or more of the parameter
// values are not supported.
// Informative note: The requirement for symmetric use does not
// apply for the level part of profile-level-id and does not apply
// for the other stream properties and capability parameters.
func (h *h264FMTP) Match(b FMTP) bool {
c, ok := b.(*h264FMTP)
if !ok {
return false
}
// test packetization-mode
hpmode, hok := h.parameters["packetization-mode"]
if !hok {
return false
}
cpmode, cok := c.parameters["packetization-mode"]
if !cok {
return false
}
if hpmode != cpmode {
return false
}
// test profile-level-id
hplid, hok := h.parameters["profile-level-id"]
if !hok {
return false
}
cplid, cok := c.parameters["profile-level-id"]
if !cok {
return false
}
if !profileLevelIDMatches(hplid, cplid) {
return false
}
return true
}
func (h *h264FMTP) Parameter(key string) (string, bool) {
v, ok := h.parameters[key]
return v, ok
}

View File

@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package fmtp
type vp9FMTP struct {
parameters map[string]string
}
func (h *vp9FMTP) MimeType() string {
return "video/vp9"
}
func (h *vp9FMTP) Match(b FMTP) bool {
c, ok := b.(*vp9FMTP)
if !ok {
return false
}
// RTP Payload Format for VP9 Video - draft-ietf-payload-vp9-16
// https://datatracker.ietf.org/doc/html/draft-ietf-payload-vp9-16
// If no profile-id is present, Profile 0 MUST be inferred
hProfileID, ok := h.parameters["profile-id"]
if !ok {
hProfileID = "0"
}
cProfileID, ok := c.parameters["profile-id"]
if !ok {
cProfileID = "0"
}
if hProfileID != cProfileID {
return false
}
return true
}
func (h *vp9FMTP) Parameter(key string) (string, bool) {
v, ok := h.parameters[key]
return v, ok
}