mirror of
https://github.com/bluenviron/mediamtx.git
synced 2025-12-24 20:11:56 -08:00
parent
6663f7b474
commit
4aef466103
5 changed files with 173 additions and 160 deletions
|
|
@ -304,6 +304,7 @@ func (s *webRTCHTTPServer) onRequest(ctx *gin.Context) {
|
|||
videoCodec: ctx.Query("video_codec"),
|
||||
audioCodec: ctx.Query("audio_codec"),
|
||||
videoBitrate: ctx.Query("video_bitrate"),
|
||||
audioBitrate: ctx.Query("audio_bitrate"),
|
||||
})
|
||||
if res.err != nil {
|
||||
if res.errStatusCode != 0 {
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ type webRTCSessionNewReq struct {
|
|||
videoCodec string
|
||||
audioCodec string
|
||||
videoBitrate string
|
||||
audioBitrate string
|
||||
res chan webRTCSessionNewRes
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,82 @@ import (
|
|||
"github.com/bluenviron/mediamtx/internal/logger"
|
||||
)
|
||||
|
||||
var videoCodecs = map[string][]webrtc.RTPCodecParameters{
|
||||
"av1": {{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeAV1,
|
||||
ClockRate: 90000,
|
||||
},
|
||||
PayloadType: 96,
|
||||
}},
|
||||
"vp9": {
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeVP9,
|
||||
ClockRate: 90000,
|
||||
SDPFmtpLine: "profile-id=0",
|
||||
},
|
||||
PayloadType: 96,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeVP9,
|
||||
ClockRate: 90000,
|
||||
SDPFmtpLine: "profile-id=1",
|
||||
},
|
||||
PayloadType: 96,
|
||||
},
|
||||
},
|
||||
"vp8": {{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeVP8,
|
||||
ClockRate: 90000,
|
||||
},
|
||||
PayloadType: 96,
|
||||
}},
|
||||
"h264": {{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeH264,
|
||||
ClockRate: 90000,
|
||||
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f",
|
||||
},
|
||||
PayloadType: 96,
|
||||
}},
|
||||
}
|
||||
|
||||
var audioCodecs = map[string][]webrtc.RTPCodecParameters{
|
||||
"opus": {{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeOpus,
|
||||
ClockRate: 48000,
|
||||
Channels: 2,
|
||||
SDPFmtpLine: "minptime=10;useinbandfec=1",
|
||||
},
|
||||
PayloadType: 111,
|
||||
}},
|
||||
"g722": {{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeG722,
|
||||
ClockRate: 8000,
|
||||
},
|
||||
PayloadType: 9,
|
||||
}},
|
||||
"pcmu": {{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypePCMU,
|
||||
ClockRate: 8000,
|
||||
},
|
||||
PayloadType: 0,
|
||||
}},
|
||||
"pcma": {{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypePCMA,
|
||||
ClockRate: 8000,
|
||||
},
|
||||
PayloadType: 8,
|
||||
}},
|
||||
}
|
||||
|
||||
type peerConnection struct {
|
||||
*webrtc.PeerConnection
|
||||
stateChangeMutex sync.Mutex
|
||||
|
|
@ -49,156 +125,42 @@ func newPeerConnection(
|
|||
mediaEngine := &webrtc.MediaEngine{}
|
||||
|
||||
if videoCodec != "" || audioCodec != "" {
|
||||
switch videoCodec {
|
||||
case "av1":
|
||||
err := mediaEngine.RegisterCodec(
|
||||
webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeAV1,
|
||||
ClockRate: 90000,
|
||||
},
|
||||
PayloadType: 96,
|
||||
},
|
||||
webrtc.RTPCodecTypeVideo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case "vp9":
|
||||
err := mediaEngine.RegisterCodec(
|
||||
webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeVP9,
|
||||
ClockRate: 90000,
|
||||
SDPFmtpLine: "profile-id=0",
|
||||
},
|
||||
PayloadType: 96,
|
||||
},
|
||||
webrtc.RTPCodecTypeVideo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = mediaEngine.RegisterCodec(
|
||||
webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeVP9,
|
||||
ClockRate: 90000,
|
||||
SDPFmtpLine: "profile-id=1",
|
||||
},
|
||||
PayloadType: 96,
|
||||
},
|
||||
webrtc.RTPCodecTypeVideo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case "vp8":
|
||||
err := mediaEngine.RegisterCodec(
|
||||
webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeVP8,
|
||||
ClockRate: 90000,
|
||||
},
|
||||
PayloadType: 96,
|
||||
},
|
||||
webrtc.RTPCodecTypeVideo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case "h264":
|
||||
err := mediaEngine.RegisterCodec(
|
||||
webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeH264,
|
||||
ClockRate: 90000,
|
||||
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f",
|
||||
},
|
||||
PayloadType: 96,
|
||||
},
|
||||
webrtc.RTPCodecTypeVideo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
codec, ok := videoCodecs[videoCodec]
|
||||
if ok {
|
||||
for _, params := range codec {
|
||||
err := mediaEngine.RegisterCodec(params, webrtc.RTPCodecTypeVideo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch audioCodec {
|
||||
case "opus":
|
||||
err := mediaEngine.RegisterCodec(
|
||||
webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeOpus,
|
||||
ClockRate: 48000,
|
||||
Channels: 2,
|
||||
SDPFmtpLine: "minptime=10;useinbandfec=1",
|
||||
},
|
||||
PayloadType: 111,
|
||||
},
|
||||
webrtc.RTPCodecTypeAudio)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case "g722":
|
||||
err := mediaEngine.RegisterCodec(
|
||||
webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeG722,
|
||||
ClockRate: 8000,
|
||||
},
|
||||
PayloadType: 9,
|
||||
},
|
||||
webrtc.RTPCodecTypeAudio)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case "pcmu":
|
||||
err := mediaEngine.RegisterCodec(
|
||||
webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypePCMU,
|
||||
ClockRate: 8000,
|
||||
},
|
||||
PayloadType: 0,
|
||||
},
|
||||
webrtc.RTPCodecTypeAudio)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case "pcma":
|
||||
err := mediaEngine.RegisterCodec(
|
||||
webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypePCMA,
|
||||
ClockRate: 8000,
|
||||
},
|
||||
PayloadType: 8,
|
||||
},
|
||||
webrtc.RTPCodecTypeAudio)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
codec, ok = audioCodecs[audioCodec]
|
||||
if ok {
|
||||
for _, params := range codec {
|
||||
err := mediaEngine.RegisterCodec(params, webrtc.RTPCodecTypeAudio)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// register all codecs
|
||||
err := mediaEngine.RegisterDefaultCodecs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else { // register all codecs
|
||||
for _, codec := range videoCodecs {
|
||||
for _, params := range codec {
|
||||
err := mediaEngine.RegisterCodec(params, webrtc.RTPCodecTypeVideo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
err = mediaEngine.RegisterCodec(
|
||||
webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeAV1,
|
||||
ClockRate: 90000,
|
||||
},
|
||||
PayloadType: 105,
|
||||
},
|
||||
webrtc.RTPCodecTypeVideo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
for _, codec := range audioCodecs {
|
||||
for _, params := range codec {
|
||||
err := mediaEngine.RegisterCodec(params, webrtc.RTPCodecTypeAudio)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,9 @@ select {
|
|||
<div id="bitrate_line">
|
||||
video bitrate (kbps):
|
||||
<input id="video_bitrate" type="text" value="10000" />
|
||||
|
||||
audio bitrate (kbps):
|
||||
<input id="audio_bitrate" type="text" value="32" />
|
||||
</div>
|
||||
<div id="submit_line">
|
||||
<button id="publish_confirm">publish</button>
|
||||
|
|
@ -222,11 +225,13 @@ class Transmitter {
|
|||
const videoCodec = document.getElementById('video_codec').value;
|
||||
const audioCodec = document.getElementById('audio_codec').value;
|
||||
const videoBitrate = document.getElementById('video_bitrate').value;
|
||||
const audioBitrate = document.getElementById('audio_bitrate').value;
|
||||
|
||||
const p = new URLSearchParams(window.location.search);
|
||||
p.set('video_codec', videoCodec);
|
||||
p.set('audio_codec', audioCodec);
|
||||
p.set('video_bitrate', videoBitrate);
|
||||
p.set('audio_bitrate', audioBitrate);
|
||||
|
||||
fetch(new URL('whip', window.location.href) + '?' + p.toString(), {
|
||||
method: 'POST',
|
||||
|
|
|
|||
|
|
@ -48,28 +48,76 @@ func mediasOfIncomingTracks(tracks []*webRTCIncomingTrack) media.Medias {
|
|||
return ret
|
||||
}
|
||||
|
||||
func insertTias(offer *webrtc.SessionDescription, value uint64) {
|
||||
func findOpusPayloadFormat(attributes []sdp.Attribute) int {
|
||||
for _, attr := range attributes {
|
||||
if attr.Key == "rtpmap" && strings.Contains(attr.Value, "opus/") {
|
||||
parts := strings.SplitN(attr.Value, " ", 2)
|
||||
pl, err := strconv.ParseUint(parts[0], 10, 31)
|
||||
if err == nil {
|
||||
return int(pl)
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func editAnswer(offer *webrtc.SessionDescription, videoBitrateStr string, audioBitrateStr string) error {
|
||||
var sd sdp.SessionDescription
|
||||
err := sd.Unmarshal([]byte(offer.SDP))
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
for _, media := range sd.MediaDescriptions {
|
||||
if media.MediaName.Media == "video" {
|
||||
media.Bandwidth = []sdp.Bandwidth{{
|
||||
Type: "TIAS",
|
||||
Bandwidth: value,
|
||||
}}
|
||||
if videoBitrateStr != "" {
|
||||
videoBitrate, err := strconv.ParseUint(videoBitrateStr, 10, 31)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, media := range sd.MediaDescriptions {
|
||||
if media.MediaName.Media == "video" {
|
||||
media.Bandwidth = []sdp.Bandwidth{{
|
||||
Type: "TIAS",
|
||||
Bandwidth: videoBitrate * 1024,
|
||||
}}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if audioBitrateStr != "" {
|
||||
audioBitrate, err := strconv.ParseUint(audioBitrateStr, 10, 31)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, media := range sd.MediaDescriptions {
|
||||
if media.MediaName.Media == "audio" {
|
||||
pl := findOpusPayloadFormat(media.Attributes)
|
||||
if pl != 0 {
|
||||
for i, attr := range media.Attributes {
|
||||
if attr.Key == "fmtp" && strings.HasPrefix(attr.Value, strconv.FormatInt(int64(pl), 10)+" ") {
|
||||
media.Attributes[i] = sdp.Attribute{
|
||||
Key: "fmtp",
|
||||
Value: strconv.FormatInt(int64(pl), 10) + " stereo=1;sprop-stereo=1;maxaveragebitrate=" +
|
||||
strconv.FormatUint(audioBitrate*1024, 10),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enc, err := sd.Marshal()
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
offer.SDP = string(enc)
|
||||
return nil
|
||||
}
|
||||
|
||||
func gatherOutgoingTracks(medias media.Medias) ([]*webRTCOutgoingTrack, error) {
|
||||
|
|
@ -320,13 +368,9 @@ func (s *webRTCSession) runPublish() (int, error) {
|
|||
tmp := pc.LocalDescription()
|
||||
answer = *tmp
|
||||
|
||||
if s.req.videoBitrate != "" {
|
||||
tmp, err := strconv.ParseUint(s.req.videoBitrate, 10, 31)
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, err
|
||||
}
|
||||
|
||||
insertTias(&answer, tmp*1024)
|
||||
err = editAnswer(&answer, s.req.videoBitrate, s.req.audioBitrate)
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, err
|
||||
}
|
||||
|
||||
err = s.writeAnswer(&answer)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue