Created
May 20, 2019 00:36
-
-
Save Sean-Der/12155aed115380dcbaacece5bf04df6f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"image" | |
"image/color" | |
"io" | |
"os/exec" | |
"strconv" | |
"time" | |
"github.com/pion/example-webrtc-applications/internal/signal" | |
"github.com/pion/rtcp" | |
"github.com/pion/webrtc" | |
"github.com/pion/webrtc/pkg/media/ivfwriter" | |
"gocv.io/x/gocv" | |
) | |
const ( | |
frameX = 960 | |
frameY = 720 | |
frameSize = frameX * frameY * 3 | |
minimumArea = 3000 | |
) | |
func main() { | |
ffmpeg := exec.Command("ffmpeg", "-i", "pipe:0", "-pix_fmt", "bgr24", "-s", strconv.Itoa(frameX)+"x"+strconv.Itoa(frameY), "-f", "rawvideo", "pipe:1") //nolint | |
ffmpegIn, _ := ffmpeg.StdinPipe() | |
ffmpegOut, _ := ffmpeg.StdoutPipe() | |
if err := ffmpeg.Start(); err != nil { | |
panic(err) | |
} | |
createWebRTCConn(ffmpegIn) | |
startGoCVMotionDetect(ffmpegOut) | |
} | |
// This was taken from the GoCV examples, the only change is we are taking a buffer from ffmpeg instead of webcam | |
// https://github.com/hybridgroup/gocv/blob/master/cmd/motion-detect/main.go | |
func startGoCVMotionDetect(ffmpegOut io.Reader) { | |
window := gocv.NewWindow("Motion Window") | |
defer window.Close() //nolint | |
img := gocv.NewMat() | |
defer img.Close() //nolint | |
imgDelta := gocv.NewMat() | |
defer imgDelta.Close() //nolint | |
imgThresh := gocv.NewMat() | |
defer imgThresh.Close() //nolint | |
mog2 := gocv.NewBackgroundSubtractorMOG2() | |
defer mog2.Close() //nolint | |
for { | |
buf := make([]byte, frameSize) | |
if _, err := io.ReadFull(ffmpegOut, buf); err != nil { | |
fmt.Println(err) | |
continue | |
} | |
img, _ := gocv.NewMatFromBytes(frameY, frameX, gocv.MatTypeCV8UC3, buf) | |
if img.Empty() { | |
continue | |
} | |
status := "Ready" | |
statusColor := color.RGBA{0, 255, 0, 0} | |
// first phase of cleaning up image, obtain foreground only | |
mog2.Apply(img, &imgDelta) | |
// remaining cleanup of the image to use for finding contours. | |
// first use threshold | |
gocv.Threshold(imgDelta, &imgThresh, 25, 255, gocv.ThresholdBinary) | |
// then dilate | |
kernel := gocv.GetStructuringElement(gocv.MorphRect, image.Pt(3, 3)) | |
defer kernel.Close() //nolint | |
gocv.Dilate(imgThresh, &imgThresh, kernel) | |
// now find contours | |
contours := gocv.FindContours(imgThresh, gocv.RetrievalExternal, gocv.ChainApproxSimple) | |
for i, c := range contours { | |
area := gocv.ContourArea(c) | |
if area < minimumArea { | |
continue | |
} | |
status = "Motion detected" | |
statusColor = color.RGBA{255, 0, 0, 0} | |
gocv.DrawContours(&img, contours, i, statusColor, 2) | |
rect := gocv.BoundingRect(c) | |
gocv.Rectangle(&img, rect, color.RGBA{0, 0, 255, 0}, 2) | |
} | |
gocv.PutText(&img, status, image.Pt(10, 20), gocv.FontHersheyPlain, 1.2, statusColor, 2) | |
window.IMShow(img) | |
if window.WaitKey(1) == 27 { | |
break | |
} | |
} | |
} | |
func createWebRTCConn(ffmpegIn io.Writer) { | |
ivfWriter, err := ivfwriter.NewWith(ffmpegIn) | |
if err != nil { | |
panic(err) | |
} | |
// Everything below is the pion-WebRTC API! Thanks for using it ❤️. | |
// Prepare the configuration | |
config := webrtc.Configuration{ | |
ICEServers: []webrtc.ICEServer{ | |
{ | |
URLs: []string{"stun:stun.l.google.com:19302"}, | |
}, | |
}, | |
} | |
// Create a new RTCPeerConnection | |
peerConnection, err := webrtc.NewPeerConnection(config) | |
if err != nil { | |
panic(err) | |
} | |
if _, err = peerConnection.AddTransceiver(webrtc.RTPCodecTypeVideo); err != nil { | |
panic(err) | |
} | |
// Set a handler for when a new remote track starts, this handler copies inbound RTP packets, | |
// replaces the SSRC and sends them back | |
peerConnection.OnTrack(func(track *webrtc.Track, receiver *webrtc.RTPReceiver) { | |
// Send a PLI on an interval so that the publisher is pushing a keyframe every rtcpPLIInterval | |
// This is a temporary fix until we implement incoming RTCP events, then we would push a PLI only when a viewer requests it | |
go func() { | |
ticker := time.NewTicker(time.Second * 3) | |
for range ticker.C { | |
errSend := peerConnection.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: track.SSRC()}}) | |
if errSend != nil { | |
fmt.Println(errSend) | |
} | |
} | |
}() | |
fmt.Printf("Track has started, of type %d: %s \n", track.PayloadType(), track.Codec().Name) | |
for { | |
// Read RTP packets being sent to Pion | |
rtp, readErr := track.ReadRTP() | |
if readErr != nil { | |
panic(readErr) | |
} | |
if ivfWriterErr := ivfWriter.WriteRTP(rtp); ivfWriterErr != nil { | |
panic(ivfWriterErr) | |
} | |
} | |
}) | |
// Set the handler for ICE connection state | |
// This will notify you when the peer has connected/disconnected | |
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) { | |
fmt.Printf("Connection State has changed %s \n", connectionState.String()) | |
}) | |
// Wait for the offer to be pasted | |
offer := webrtc.SessionDescription{} | |
signal.Decode(signal.MustReadStdin(), &offer) | |
// Set the remote SessionDescription | |
err = peerConnection.SetRemoteDescription(offer) | |
if err != nil { | |
panic(err) | |
} | |
// Create an answer | |
answer, err := peerConnection.CreateAnswer(nil) | |
if err != nil { | |
panic(err) | |
} | |
// Sets the LocalDescription, and starts our UDP listeners | |
err = peerConnection.SetLocalDescription(answer) | |
if err != nil { | |
panic(err) | |
} | |
// Output the answer in base64 so we can paste it in browser | |
fmt.Println(signal.Encode(answer)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Open example page
jsfiddle.net you should see your Webcam, two text-areas and a 'Start Session' button
Run main.go with your browsers SessionDescription as stdin
In the jsfiddle the top textarea is your browser, copy that and:
Linux/macOS
Run
echo $BROWSER_SDP | go run main.go
Windows
go run main.go < my_file
Input main.go's SessionDescription into your browser
Copy the text that
go run main.go
just emitted and copy into second text areaHit 'Start Session' in jsfiddle, enjoy your media!
Your video and/or audio should popup automatically, and will continue playing until you close the application