mirror of
https://github.com/Monibuca/plugin-webrtc.git
synced 2025-09-27 03:06:29 +08:00
feat: add screenshare
feat: add camera selector desc: 增加屏幕分享和摄像头选择功能
This commit is contained in:
@@ -69,6 +69,13 @@ Response Body: `SDP`
|
|||||||
`/webrtc/test/publish`
|
`/webrtc/test/publish`
|
||||||
- `?streamPath=xxx` The streamPath to publish, default is `live/webrtc`
|
- `?streamPath=xxx` The streamPath to publish, default is `live/webrtc`
|
||||||
- you can add other query parameters to the URL
|
- you can add other query parameters to the URL
|
||||||
|
|
||||||
|
### ScreenShare Test Page
|
||||||
|
|
||||||
|
`/webrtc/test/screenshare`
|
||||||
|
- `?streamPath=xxx` The streamPath to publish, default is `live/webrtc`
|
||||||
|
- you can add other query parameters to the URL
|
||||||
|
|
||||||
### Play Test Page
|
### Play Test Page
|
||||||
|
|
||||||
`/webrtc/test/subscribe`
|
`/webrtc/test/subscribe`
|
||||||
|
@@ -66,6 +66,12 @@ Response Body: `SDP`
|
|||||||
`/webrtc/test/publish`
|
`/webrtc/test/publish`
|
||||||
- 可增加参数`?streamPath=xxx`指定推流地址,默认为`live/webrtc`
|
- 可增加参数`?streamPath=xxx`指定推流地址,默认为`live/webrtc`
|
||||||
- 可以增加其他推流参数
|
- 可以增加其他推流参数
|
||||||
|
|
||||||
|
### 屏幕分享测试
|
||||||
|
|
||||||
|
`/webrtc/test/screenshare`
|
||||||
|
- 可增加参数`?streamPath=xxx`指定推流地址,默认为`live/webrtc`
|
||||||
|
- 可以增加其他推流参数
|
||||||
### 播放测试页面
|
### 播放测试页面
|
||||||
|
|
||||||
`/webrtc/test/subscribe`
|
`/webrtc/test/subscribe`
|
||||||
|
12
batcher.go
12
batcher.go
@@ -73,13 +73,13 @@ func (suber *WebRTCBatcher) Signal(msg DataChannelMessage) {
|
|||||||
sub.WebRTCIO = suber.WebRTCIO
|
sub.WebRTCIO = suber.WebRTCIO
|
||||||
if err = WebRTCPlugin.SubscribeExist(streamPath, sub); err == nil {
|
if err = WebRTCPlugin.SubscribeExist(streamPath, sub); err == nil {
|
||||||
suber.subscribers = append(suber.subscribers, sub)
|
suber.subscribers = append(suber.subscribers, sub)
|
||||||
go func() {
|
go func(streamPath string) {
|
||||||
sub.PlayRTP()
|
sub.PlayRTP()
|
||||||
if sub.audioSender != nil {
|
if sub.audio.RTPSender != nil {
|
||||||
suber.RemoveTrack(sub.audioSender)
|
suber.RemoveTrack(sub.audio.RTPSender )
|
||||||
}
|
}
|
||||||
if sub.videoSender != nil {
|
if sub.video.RTPSender != nil {
|
||||||
suber.RemoveTrack(sub.videoSender)
|
suber.RemoveTrack(sub.video.RTPSender)
|
||||||
}
|
}
|
||||||
if sub.DC != nil {
|
if sub.DC != nil {
|
||||||
sub.DC.Close()
|
sub.DC.Close()
|
||||||
@@ -87,7 +87,7 @@ func (suber *WebRTCBatcher) Signal(msg DataChannelMessage) {
|
|||||||
removeMap["streamPath"] = streamPath
|
removeMap["streamPath"] = streamPath
|
||||||
b, _ := json.Marshal(removeMap)
|
b, _ := json.Marshal(removeMap)
|
||||||
suber.signalChannel.SendText(string(b))
|
suber.signalChannel.SendText(string(b))
|
||||||
}()
|
}(streamPath)
|
||||||
} else {
|
} else {
|
||||||
removeMap["streamPath"] = streamPath
|
removeMap["streamPath"] = streamPath
|
||||||
b, _ := json.Marshal(removeMap)
|
b, _ := json.Marshal(removeMap)
|
||||||
|
7
io.go
7
io.go
@@ -7,6 +7,7 @@ import (
|
|||||||
type WebRTCIO struct {
|
type WebRTCIO struct {
|
||||||
*PeerConnection
|
*PeerConnection
|
||||||
SDP string
|
SDP string
|
||||||
|
// LocalSDP *sdp.SessionDescription
|
||||||
}
|
}
|
||||||
|
|
||||||
func (IO *WebRTCIO) GetAnswer() (string, error) {
|
func (IO *WebRTCIO) GetAnswer() (string, error) {
|
||||||
@@ -15,10 +16,14 @@ func (IO *WebRTCIO) GetAnswer() (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
// IO.LocalSDP, err = answer.Unmarshal()
|
||||||
|
// if err != nil {
|
||||||
|
// return "", err
|
||||||
|
// }
|
||||||
gatherComplete := GatheringCompletePromise(IO.PeerConnection)
|
gatherComplete := GatheringCompletePromise(IO.PeerConnection)
|
||||||
if err := IO.SetLocalDescription(answer); err != nil {
|
if err := IO.SetLocalDescription(answer); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
<-gatherComplete
|
<-gatherComplete
|
||||||
return IO.LocalDescription().SDP, nil
|
return IO.LocalDescription().SDP, nil
|
||||||
}
|
}
|
||||||
|
3
main.go
3
main.go
@@ -230,6 +230,9 @@ func (conf *WebRTCConfig) Push_(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (conf *WebRTCConfig) Test_Publish(w http.ResponseWriter, r *http.Request) {
|
func (conf *WebRTCConfig) Test_Publish(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Write(publishHTML)
|
w.Write(publishHTML)
|
||||||
}
|
}
|
||||||
|
func (conf *WebRTCConfig) Test_ScreenShare(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write(publishHTML)
|
||||||
|
}
|
||||||
func (conf *WebRTCConfig) Test_Subscribe(w http.ResponseWriter, r *http.Request) {
|
func (conf *WebRTCConfig) Test_Subscribe(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Write(subscribeHTML)
|
w.Write(subscribeHTML)
|
||||||
}
|
}
|
||||||
|
25
publish.html
25
publish.html
@@ -12,6 +12,7 @@
|
|||||||
<video id="video" width="640" height="480" autoplay muted>
|
<video id="video" width="640" height="480" autoplay muted>
|
||||||
</video>
|
</video>
|
||||||
<!-- <button id="sw" onclick="action()" type="button" style="width:100px;height:30px;display: block;">unpublish</button> -->
|
<!-- <button id="sw" onclick="action()" type="button" style="width:100px;height:30px;display: block;">unpublish</button> -->
|
||||||
|
<div id="camera"></div>
|
||||||
<pre>
|
<pre>
|
||||||
<code id="remoteSdp">
|
<code id="remoteSdp">
|
||||||
|
|
||||||
@@ -20,16 +21,29 @@
|
|||||||
</body>
|
</body>
|
||||||
<script>
|
<script>
|
||||||
let action = () => { console.log('action not set'); };
|
let action = () => { console.log('action not set'); };
|
||||||
|
const screenshare = location.pathname.endsWith("screenshare");
|
||||||
(async () => {
|
(async () => {
|
||||||
|
const searchParams = new URLSearchParams(location.search);
|
||||||
|
const $camera = document.getElementById('camera');
|
||||||
navigator.mediaDevices.enumerateDevices().then((devices) => {
|
navigator.mediaDevices.enumerateDevices().then((devices) => {
|
||||||
devices.forEach((device) => {
|
devices.forEach((device) => {
|
||||||
console.log(device.kind + ": " + device.label + " id = " + device.deviceId);
|
console.log(device.kind + ": " + device.label + " id = " + device.deviceId);
|
||||||
|
if (device.kind == 'videoinput' && !screenshare) {
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = location.pathname + '?videoinput=' + device.deviceId;
|
||||||
|
a.innerHTML = device.label;
|
||||||
|
$camera.appendChild(a);
|
||||||
|
$camera.appendChild(document.createElement('br'));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const mediaStream = await navigator.mediaDevices.getUserMedia({
|
const mediaStream = await (screenshare ? navigator.mediaDevices.getDisplayMedia() : navigator.mediaDevices.getUserMedia({
|
||||||
video: true,
|
video: searchParams.get('videoinput') ? {
|
||||||
|
deviceId: searchParams.get('videoinput'),
|
||||||
|
} : true,
|
||||||
audio: true,
|
audio: true,
|
||||||
});
|
}));
|
||||||
|
searchParams.delete('videoinput');
|
||||||
document.getElementById('video').srcObject = mediaStream;
|
document.getElementById('video').srcObject = mediaStream;
|
||||||
const pc = new RTCPeerConnection();
|
const pc = new RTCPeerConnection();
|
||||||
|
|
||||||
@@ -39,9 +53,8 @@
|
|||||||
pc.onicecandidate = (e) => {
|
pc.onicecandidate = (e) => {
|
||||||
console.log('onicecandidate', e.candidate);
|
console.log('onicecandidate', e.candidate);
|
||||||
};
|
};
|
||||||
const searchParams = new URLSearchParams(location.search);
|
|
||||||
const streamPath = searchParams.get('streamPath') || 'live/webrtc';
|
const streamPath = searchParams.get('streamPath') || 'live/webrtc';
|
||||||
searchParams.delete('streamPath')
|
searchParams.delete('streamPath');
|
||||||
mediaStream.id = streamPath;
|
mediaStream.id = streamPath;
|
||||||
mediaStream.getTracks().forEach((t) => {
|
mediaStream.getTracks().forEach((t) => {
|
||||||
pc.addTrack(t, mediaStream);
|
pc.addTrack(t, mediaStream);
|
||||||
@@ -52,7 +65,7 @@
|
|||||||
const offer = await pc.createOffer();
|
const offer = await pc.createOffer();
|
||||||
await pc.setLocalDescription(offer);
|
await pc.setLocalDescription(offer);
|
||||||
const result = await fetch(
|
const result = await fetch(
|
||||||
`/webrtc/push/${streamPath}${location.search?`?${searchParams.toString()}`:''}`,
|
`/webrtc/push/${streamPath}${location.search ? `?${searchParams.toString()}` : ''}`,
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
|
@@ -68,6 +68,11 @@ func (puber *WebRTCPublisher) onTrack(track *TrackRemote, receiver *RTPReceiver)
|
|||||||
rtpItem := puber.VideoTrack.GetRTPFromPool()
|
rtpItem := puber.VideoTrack.GetRTPFromPool()
|
||||||
if i, _, err := track.Read(rtpItem.Value.Raw); err == nil {
|
if i, _, err := track.Read(rtpItem.Value.Raw); err == nil {
|
||||||
rtpItem.Value.Unmarshal(rtpItem.Value.Raw[:i])
|
rtpItem.Value.Unmarshal(rtpItem.Value.Raw[:i])
|
||||||
|
if rtpItem.Value.Extension {
|
||||||
|
for _, id := range rtpItem.Value.GetExtensionIDs() {
|
||||||
|
puber.Debug("extension", zap.Uint8("id", id), zap.Binary("value", rtpItem.Value.GetExtension(id)))
|
||||||
|
}
|
||||||
|
}
|
||||||
puber.VideoTrack.WriteRTP(rtpItem)
|
puber.VideoTrack.WriteRTP(rtpItem)
|
||||||
} else {
|
} else {
|
||||||
puber.Info("track stop", zap.String("kind", track.Kind().String()), zap.Error(err))
|
puber.Info("track stop", zap.String("kind", track.Kind().String()), zap.Error(err))
|
||||||
|
172
subscriber.go
172
subscriber.go
@@ -2,27 +2,35 @@ package webrtc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/pion/rtcp"
|
"github.com/pion/rtcp"
|
||||||
. "github.com/pion/webrtc/v3"
|
. "github.com/pion/webrtc/v3"
|
||||||
"go.uber.org/zap"
|
|
||||||
. "m7s.live/engine/v4"
|
. "m7s.live/engine/v4"
|
||||||
"m7s.live/engine/v4/codec"
|
"m7s.live/engine/v4/codec"
|
||||||
"m7s.live/engine/v4/track"
|
"m7s.live/engine/v4/track"
|
||||||
"m7s.live/engine/v4/util"
|
"m7s.live/engine/v4/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type trackSender struct {
|
||||||
|
*TrackLocalStaticRTP
|
||||||
|
*RTPSender
|
||||||
|
// seq uint32
|
||||||
|
}
|
||||||
|
|
||||||
type WebRTCSubscriber struct {
|
type WebRTCSubscriber struct {
|
||||||
Subscriber
|
Subscriber
|
||||||
WebRTCIO
|
WebRTCIO
|
||||||
videoTrack *TrackLocalStaticRTP
|
audio trackSender
|
||||||
audioTrack *TrackLocalStaticRTP
|
video trackSender
|
||||||
videoSender *RTPSender
|
DC *DataChannel
|
||||||
audioSender *RTPSender
|
// flvHeadCache []byte
|
||||||
DC *DataChannel
|
}
|
||||||
flvHeadCache []byte
|
|
||||||
|
func (suber *WebRTCSubscriber) queueDCData(data ...[]byte) {
|
||||||
|
for _, d := range data {
|
||||||
|
suber.DC.Send(d)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suber *WebRTCSubscriber) createDataChannel() {
|
func (suber *WebRTCSubscriber) createDataChannel() {
|
||||||
@@ -30,27 +38,27 @@ func (suber *WebRTCSubscriber) createDataChannel() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
suber.DC, _ = suber.PeerConnection.CreateDataChannel(suber.Subscriber.Stream.Path, nil)
|
suber.DC, _ = suber.PeerConnection.CreateDataChannel(suber.Subscriber.Stream.Path, nil)
|
||||||
suber.flvHeadCache = make([]byte, 15)
|
// suber.flvHeadCache = make([]byte, 15)
|
||||||
suber.DC.Send(codec.FLVHeader)
|
|
||||||
}
|
|
||||||
func (suber *WebRTCSubscriber) sendAvByDatachannel(t byte, reader *track.AVRingReader) {
|
|
||||||
suber.flvHeadCache[0] = t
|
|
||||||
frame := reader.Frame
|
|
||||||
dataSize := uint32(frame.AVCC.ByteLength)
|
|
||||||
result := net.Buffers{suber.flvHeadCache[:11]}
|
|
||||||
result = append(result, frame.AVCC.ToBuffers()...)
|
|
||||||
ts := reader.AbsTime
|
|
||||||
util.PutBE(suber.flvHeadCache[1:4], dataSize)
|
|
||||||
util.PutBE(suber.flvHeadCache[4:7], ts)
|
|
||||||
suber.flvHeadCache[7] = byte(ts >> 24)
|
|
||||||
result = append(result, util.PutBE(suber.flvHeadCache[11:15], dataSize+11))
|
|
||||||
for _, data := range util.SplitBuffers(result, 65535) {
|
|
||||||
for _, d := range data {
|
|
||||||
suber.DC.Send(d)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func (suber *WebRTCSubscriber) sendAvByDatachannel(t byte, reader *track.AVRingReader) {
|
||||||
|
// suber.flvHeadCache[0] = t
|
||||||
|
// frame := reader.Frame
|
||||||
|
// dataSize := uint32(frame.AVCC.ByteLength)
|
||||||
|
// result := net.Buffers{suber.flvHeadCache[:11]}
|
||||||
|
// result = append(result, frame.AVCC.ToBuffers()...)
|
||||||
|
// ts := reader.AbsTime
|
||||||
|
// util.PutBE(suber.flvHeadCache[1:4], dataSize)
|
||||||
|
// util.PutBE(suber.flvHeadCache[4:7], ts)
|
||||||
|
// suber.flvHeadCache[7] = byte(ts >> 24)
|
||||||
|
// result = append(result, util.PutBE(suber.flvHeadCache[11:15], dataSize+11))
|
||||||
|
// for _, data := range util.SplitBuffers(result, 65535) {
|
||||||
|
// for _, d := range data {
|
||||||
|
// suber.queueDCData(d)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
func (suber *WebRTCSubscriber) OnEvent(event any) {
|
func (suber *WebRTCSubscriber) OnEvent(event any) {
|
||||||
switch v := event.(type) {
|
switch v := event.(type) {
|
||||||
case *track.Video:
|
case *track.Video:
|
||||||
@@ -64,35 +72,13 @@ func (suber *WebRTCSubscriber) OnEvent(event any) {
|
|||||||
pli = list[0][1]
|
pli = list[0][1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
suber.videoTrack, _ = NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeH264, SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" + pli}, v.Name, suber.Subscriber.Stream.Path)
|
suber.video.TrackLocalStaticRTP, _ = NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeH264, SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" + pli}, v.Name, suber.Subscriber.Stream.Path)
|
||||||
case codec.CodecID_H265:
|
case codec.CodecID_H265:
|
||||||
|
suber.createDataChannel()
|
||||||
// suber.videoTrack, _ = NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeH265, SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" + pli}, "video", suber.Subscriber.Stream.Path)
|
// suber.videoTrack, _ = NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeH265, SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" + pli}, "video", suber.Subscriber.Stream.Path)
|
||||||
default:
|
default:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if suber.videoTrack == nil {
|
|
||||||
suber.createDataChannel()
|
|
||||||
} else {
|
|
||||||
suber.videoSender, _ = suber.PeerConnection.AddTrack(suber.videoTrack)
|
|
||||||
go func() {
|
|
||||||
rtcpBuf := make([]byte, 1500)
|
|
||||||
for {
|
|
||||||
if n, _, rtcpErr := suber.videoSender.Read(rtcpBuf); rtcpErr != nil {
|
|
||||||
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
if p, err := rtcp.Unmarshal(rtcpBuf[:n]); err == nil {
|
|
||||||
for _, pp := range p {
|
|
||||||
switch pp.(type) {
|
|
||||||
case *rtcp.PictureLossIndication:
|
|
||||||
// fmt.Println("PictureLossIndication")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
suber.Subscriber.AddTrack(v) //接受这个track
|
suber.Subscriber.AddTrack(v) //接受这个track
|
||||||
case *track.Audio:
|
case *track.Audio:
|
||||||
audioMimeType := MimeTypePCMA
|
audioMimeType := MimeTypePCMA
|
||||||
@@ -103,37 +89,77 @@ func (suber *WebRTCSubscriber) OnEvent(event any) {
|
|||||||
case codec.CodecID_AAC:
|
case codec.CodecID_AAC:
|
||||||
suber.createDataChannel()
|
suber.createDataChannel()
|
||||||
case codec.CodecID_PCMA, codec.CodecID_PCMU:
|
case codec.CodecID_PCMA, codec.CodecID_PCMU:
|
||||||
suber.audioTrack, _ = NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: audioMimeType}, v.Name, suber.Subscriber.Stream.Path)
|
suber.audio.TrackLocalStaticRTP, _ = NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: audioMimeType}, v.Name, suber.Subscriber.Stream.Path)
|
||||||
suber.audioSender, _ = suber.PeerConnection.AddTrack(suber.audioTrack)
|
//suber.audio.RTPSender, _ = suber.PeerConnection.AddTrack(suber.audio.TrackLocalStaticRTP)
|
||||||
suber.Subscriber.AddTrack(v) //接受这个track
|
|
||||||
}
|
|
||||||
case VideoDeConf:
|
|
||||||
if suber.DC != nil {
|
|
||||||
suber.DC.Send(util.ConcatBuffers(codec.VideoAVCC2FLV(0, v)))
|
|
||||||
}
|
|
||||||
case AudioDeConf:
|
|
||||||
if suber.DC != nil {
|
|
||||||
suber.DC.Send(util.ConcatBuffers(codec.AudioAVCC2FLV(0, v)))
|
|
||||||
}
|
}
|
||||||
|
suber.Subscriber.AddTrack(v) //接受这个track
|
||||||
|
// case VideoDeConf:
|
||||||
|
// if suber.DC != nil {
|
||||||
|
// suber.queueDCData(codec.VideoAVCC2FLV(0, v)...)
|
||||||
|
// }
|
||||||
|
// case AudioDeConf:
|
||||||
|
// if suber.DC != nil {
|
||||||
|
// suber.queueDCData(codec.AudioAVCC2FLV(0, v)...)
|
||||||
|
// }
|
||||||
case VideoRTP:
|
case VideoRTP:
|
||||||
if suber.videoTrack != nil {
|
// if suber.video.TrackLocalStaticRTP != nil {
|
||||||
suber.Trace("video rtp", zap.Any("packet", v.Packet.Header))
|
suber.video.WriteRTP(v.Packet)
|
||||||
suber.videoTrack.WriteRTP(v.Packet)
|
// } else if suber.DC != nil && suber.VideoReader.Frame.Sequence != suber.video.seq {
|
||||||
} else if suber.DC != nil {
|
// suber.video.seq = suber.VideoReader.Frame.Sequence
|
||||||
suber.sendAvByDatachannel(9, suber.VideoReader)
|
// suber.sendAvByDatachannel(9, suber.VideoReader)
|
||||||
}
|
// }
|
||||||
case AudioRTP:
|
case AudioRTP:
|
||||||
if suber.audioTrack != nil {
|
// if suber.audio.TrackLocalStaticRTP != nil {
|
||||||
suber.audioTrack.WriteRTP(v.Packet)
|
suber.audio.WriteRTP(v.Packet)
|
||||||
} else if suber.DC != nil {
|
// } else if suber.DC != nil && suber.AudioReader.Frame.Sequence != suber.audio.seq {
|
||||||
suber.sendAvByDatachannel(8, suber.AudioReader)
|
// suber.audio.seq = suber.AudioReader.Frame.Sequence
|
||||||
|
// suber.sendAvByDatachannel(8, suber.AudioReader)
|
||||||
|
// }
|
||||||
|
case FLVFrame:
|
||||||
|
for _, data := range util.SplitBuffers(v, 65535) {
|
||||||
|
suber.queueDCData(data...)
|
||||||
}
|
}
|
||||||
case ISubscriber:
|
case ISubscriber:
|
||||||
|
if suber.DC == nil {
|
||||||
|
if suber.audio.TrackLocalStaticRTP != nil {
|
||||||
|
suber.audio.RTPSender, _ = suber.PeerConnection.AddTrack(suber.audio.TrackLocalStaticRTP)
|
||||||
|
}
|
||||||
|
if suber.video.TrackLocalStaticRTP != nil {
|
||||||
|
suber.video.RTPSender, _ = suber.PeerConnection.AddTrack(suber.video.TrackLocalStaticRTP)
|
||||||
|
go func() {
|
||||||
|
rtcpBuf := make([]byte, 1500)
|
||||||
|
for {
|
||||||
|
if n, _, rtcpErr := suber.video.Read(rtcpBuf); rtcpErr != nil {
|
||||||
|
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
if p, err := rtcp.Unmarshal(rtcpBuf[:n]); err == nil {
|
||||||
|
for _, pp := range p {
|
||||||
|
switch pp.(type) {
|
||||||
|
case *rtcp.PictureLossIndication:
|
||||||
|
// fmt.Println("PictureLossIndication")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
suber.DC.OnOpen(func() {
|
||||||
|
suber.DC.Send(codec.FLVHeader)
|
||||||
|
go suber.PlayFLV()
|
||||||
|
})
|
||||||
|
}
|
||||||
suber.OnConnectionStateChange(func(pcs PeerConnectionState) {
|
suber.OnConnectionStateChange(func(pcs PeerConnectionState) {
|
||||||
suber.Info("Connection State has changed:" + pcs.String())
|
suber.Info("Connection State has changed:" + pcs.String())
|
||||||
switch pcs {
|
switch pcs {
|
||||||
case PeerConnectionStateConnected:
|
case PeerConnectionStateConnected:
|
||||||
go suber.PlayRTP()
|
if suber.DC != nil {
|
||||||
|
// go suber.PlayFLV()
|
||||||
|
} else {
|
||||||
|
go suber.PlayRTP()
|
||||||
|
}
|
||||||
case PeerConnectionStateDisconnected, PeerConnectionStateFailed:
|
case PeerConnectionStateDisconnected, PeerConnectionStateFailed:
|
||||||
suber.Stop()
|
suber.Stop()
|
||||||
suber.PeerConnection.Close()
|
suber.PeerConnection.Close()
|
||||||
|
Reference in New Issue
Block a user