main.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
  2. // SPDX-License-Identifier: MIT
  3. //go:build !js
  4. // +build !js
  5. // insertable-streams demonstrates how to use insertable streams with Pion
  6. package main
  7. import (
  8. "context"
  9. "errors"
  10. "fmt"
  11. "io"
  12. "os"
  13. "time"
  14. "github.com/pion/webrtc/v3"
  15. "github.com/pion/webrtc/v3/examples/internal/signal"
  16. "github.com/pion/webrtc/v3/pkg/media"
  17. "github.com/pion/webrtc/v3/pkg/media/ivfreader"
  18. )
  19. const cipherKey = 0xAA
  20. // nolint:gocognit
  21. func main() {
  22. peerConnection, err := webrtc.NewPeerConnection(webrtc.Configuration{
  23. ICEServers: []webrtc.ICEServer{
  24. {
  25. URLs: []string{"stun:stun.l.google.com:19302"},
  26. },
  27. },
  28. })
  29. if err != nil {
  30. panic(err)
  31. }
  32. defer func() {
  33. if cErr := peerConnection.Close(); cErr != nil {
  34. fmt.Printf("cannot close peerConnection: %v\n", cErr)
  35. }
  36. }()
  37. // Create a video track
  38. videoTrack, err := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion")
  39. if err != nil {
  40. panic(err)
  41. }
  42. rtpSender, err := peerConnection.AddTrack(videoTrack)
  43. if err != nil {
  44. panic(err)
  45. }
  46. // Read incoming RTCP packets
  47. // Before these packets are returned they are processed by interceptors. For things
  48. // like NACK this needs to be called.
  49. go func() {
  50. rtcpBuf := make([]byte, 1500)
  51. for {
  52. if _, _, rtcpErr := rtpSender.Read(rtcpBuf); rtcpErr != nil {
  53. return
  54. }
  55. }
  56. }()
  57. iceConnectedCtx, iceConnectedCtxCancel := context.WithCancel(context.Background())
  58. go func() {
  59. // Open a IVF file and start reading using our IVFReader
  60. file, ivfErr := os.Open("output.ivf")
  61. if ivfErr != nil {
  62. panic(ivfErr)
  63. }
  64. ivf, header, ivfErr := ivfreader.NewWith(file)
  65. if ivfErr != nil {
  66. panic(ivfErr)
  67. }
  68. // Wait for connection established
  69. <-iceConnectedCtx.Done()
  70. // Send our video file frame at a time. Pace our sending so we send it at the same speed it should be played back as.
  71. // This isn't required since the video is timestamped, but we will such much higher loss if we send all at once.
  72. sleepTime := time.Millisecond * time.Duration((float32(header.TimebaseNumerator)/float32(header.TimebaseDenominator))*1000)
  73. for {
  74. frame, _, ivfErr := ivf.ParseNextFrame()
  75. if errors.Is(ivfErr, io.EOF) {
  76. fmt.Printf("All frames parsed and sent")
  77. os.Exit(0)
  78. }
  79. if ivfErr != nil {
  80. panic(ivfErr)
  81. }
  82. // Encrypt video using XOR Cipher
  83. for i := range frame {
  84. frame[i] ^= cipherKey
  85. }
  86. time.Sleep(sleepTime)
  87. if ivfErr = videoTrack.WriteSample(media.Sample{Data: frame, Duration: time.Second}); ivfErr != nil {
  88. panic(ivfErr)
  89. }
  90. }
  91. }()
  92. // Set the handler for ICE connection state
  93. // This will notify you when the peer has connected/disconnected
  94. peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
  95. fmt.Printf("Connection State has changed %s \n", connectionState.String())
  96. if connectionState == webrtc.ICEConnectionStateConnected {
  97. iceConnectedCtxCancel()
  98. }
  99. })
  100. // Set the handler for Peer connection state
  101. // This will notify you when the peer has connected/disconnected
  102. peerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) {
  103. fmt.Printf("Peer Connection State has changed: %s\n", s.String())
  104. if s == webrtc.PeerConnectionStateFailed {
  105. // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart.
  106. // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout.
  107. // Note that the PeerConnection may come back from PeerConnectionStateDisconnected.
  108. fmt.Println("Peer Connection has gone to failed exiting")
  109. os.Exit(0)
  110. }
  111. })
  112. // Wait for the offer to be pasted
  113. offer := webrtc.SessionDescription{}
  114. signal.Decode(signal.MustReadStdin(), &offer)
  115. // Set the remote SessionDescription
  116. if err = peerConnection.SetRemoteDescription(offer); err != nil {
  117. panic(err)
  118. }
  119. // Create answer
  120. answer, err := peerConnection.CreateAnswer(nil)
  121. if err != nil {
  122. panic(err)
  123. }
  124. // Create channel that is blocked until ICE Gathering is complete
  125. gatherComplete := webrtc.GatheringCompletePromise(peerConnection)
  126. // Sets the LocalDescription, and starts our UDP listeners
  127. if err = peerConnection.SetLocalDescription(answer); err != nil {
  128. panic(err)
  129. }
  130. // Block until ICE Gathering is complete, disabling trickle ICE
  131. // we do this because we only can exchange one signaling message
  132. // in a production application you should exchange ICE Candidates via OnICECandidate
  133. <-gatherComplete
  134. // Output the answer in base64 so we can paste it in browser
  135. fmt.Println(signal.Encode(*peerConnection.LocalDescription()))
  136. // Block forever
  137. select {}
  138. }