-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Also include example Resolves #1670
- Loading branch information
Showing
7 changed files
with
332 additions
and
40 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# save-to-disk-av1 | ||
save-to-disk-av1 is a simple application that shows how to save a video to disk using AV1. | ||
|
||
If you wish to save VP8 and Opus instead of AV1 see [save-to-disk](https://github.com/pion/webrtc/tree/master/examples/save-to-disk) | ||
|
||
If you wish to save VP8/Opus inside the same file see [save-to-webm](https://github.com/pion/example-webrtc-applications/tree/master/save-to-webm) | ||
|
||
## Instructions | ||
### Download save-to-disk-av1 | ||
``` | ||
export GO111MODULE=on | ||
go get github.com/pion/webrtc/v3/examples/save-to-disk-av1 | ||
``` | ||
|
||
### Open save-to-disk-av1 example page | ||
[jsfiddle.net](https://jsfiddle.net/xjcve6d3/) you should see your Webcam, two text-areas and two buttons: `Copy browser SDP to clipboard`, `Start Session`. | ||
|
||
### Run save-to-disk-av1, with your browsers SessionDescription as stdin | ||
In the jsfiddle the top textarea is your browser's Session Description. Press `Copy browser SDP to clipboard` or copy the base64 string manually. | ||
We will use this value in the next step. | ||
|
||
#### Linux/macOS | ||
Run `echo $BROWSER_SDP | save-to-disk-av1` | ||
#### Windows | ||
1. Paste the SessionDescription into a file. | ||
1. Run `save-to-disk-av1 < my_file` | ||
|
||
### Input save-to-disk-av1's SessionDescription into your browser | ||
Copy the text that `save-to-disk-av1` just emitted and copy into second text area | ||
|
||
### Hit 'Start Session' in jsfiddle, wait, close jsfiddle, enjoy your video! | ||
In the folder you ran `save-to-disk-av1` you should now have a file `output.ivf` play with your video player of choice! | ||
> Note: In order to correctly create the files, the remote client (JSFiddle) should be closed. The Go example will automatically close itself. | ||
Congrats, you have used Pion WebRTC! Now start building something cool |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
//go:build !js | ||
// +build !js | ||
|
||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"strings" | ||
"time" | ||
|
||
"github.com/pion/interceptor" | ||
"github.com/pion/rtcp" | ||
"github.com/pion/webrtc/v3" | ||
"github.com/pion/webrtc/v3/examples/internal/signal" | ||
"github.com/pion/webrtc/v3/pkg/media" | ||
"github.com/pion/webrtc/v3/pkg/media/ivfwriter" | ||
) | ||
|
||
func saveToDisk(i media.Writer, track *webrtc.TrackRemote) { | ||
defer func() { | ||
if err := i.Close(); err != nil { | ||
panic(err) | ||
} | ||
}() | ||
|
||
for { | ||
rtpPacket, _, err := track.ReadRTP() | ||
if err != nil { | ||
panic(err) | ||
} | ||
if err := i.WriteRTP(rtpPacket); err != nil { | ||
panic(err) | ||
} | ||
} | ||
} | ||
|
||
func main() { | ||
// Everything below is the Pion WebRTC API! Thanks for using it ❤️. | ||
|
||
// Create a MediaEngine object to configure the supported codec | ||
m := &webrtc.MediaEngine{} | ||
|
||
// Setup the codecs you want to use. | ||
// We'll use a VP8 and Opus but you can also define your own | ||
if err := m.RegisterCodec(webrtc.RTPCodecParameters{ | ||
RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeAV1, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, | ||
PayloadType: 96, | ||
}, webrtc.RTPCodecTypeVideo); err != nil { | ||
panic(err) | ||
} | ||
|
||
// Create a InterceptorRegistry. This is the user configurable RTP/RTCP Pipeline. | ||
// This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection` | ||
// this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry | ||
// for each PeerConnection. | ||
i := &interceptor.Registry{} | ||
|
||
// Use the default set of Interceptors | ||
if err := webrtc.RegisterDefaultInterceptors(m, i); err != nil { | ||
panic(err) | ||
} | ||
|
||
// Create the API object with the MediaEngine | ||
api := webrtc.NewAPI(webrtc.WithMediaEngine(m), webrtc.WithInterceptorRegistry(i)) | ||
|
||
// Prepare the configuration | ||
config := webrtc.Configuration{} | ||
|
||
// Create a new RTCPeerConnection | ||
peerConnection, err := api.NewPeerConnection(config) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
// Allow us to receive 1 video track | ||
if _, err = peerConnection.AddTransceiverFromKind(webrtc.RTPCodecTypeVideo); err != nil { | ||
panic(err) | ||
} | ||
|
||
ivfFile, err := ivfwriter.New("output.ivf", ivfwriter.WithCodec(webrtc.MimeTypeAV1)) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
// Set a handler for when a new remote track starts, this handler saves buffers to disk as | ||
// an ivf file, since we could have multiple video tracks we provide a counter. | ||
// In your application this is where you would handle/process video | ||
peerConnection.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) { | ||
// Send a PLI on an interval so that the publisher is pushing a keyframe every rtcpPLIInterval | ||
go func() { | ||
ticker := time.NewTicker(time.Second * 3) | ||
for range ticker.C { | ||
errSend := peerConnection.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: uint32(track.SSRC())}}) | ||
if errSend != nil { | ||
fmt.Println(errSend) | ||
} | ||
} | ||
}() | ||
|
||
if strings.EqualFold(track.Codec().MimeType, webrtc.MimeTypeAV1) { | ||
fmt.Println("Got AV1 track, saving to disk as output.ivf") | ||
saveToDisk(ivfFile, track) | ||
} | ||
}) | ||
|
||
// 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()) | ||
|
||
if connectionState == webrtc.ICEConnectionStateConnected { | ||
fmt.Println("Ctrl+C the remote client to stop the demo") | ||
} else if connectionState == webrtc.ICEConnectionStateFailed { | ||
if closeErr := ivfFile.Close(); closeErr != nil { | ||
panic(closeErr) | ||
} | ||
|
||
fmt.Println("Done writing media files") | ||
|
||
// Gracefully shutdown the peer connection | ||
if closeErr := peerConnection.Close(); closeErr != nil { | ||
panic(closeErr) | ||
} | ||
|
||
os.Exit(0) | ||
} | ||
}) | ||
|
||
// 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 answer | ||
answer, err := peerConnection.CreateAnswer(nil) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
// Create channel that is blocked until ICE Gathering is complete | ||
gatherComplete := webrtc.GatheringCompletePromise(peerConnection) | ||
|
||
// Sets the LocalDescription, and starts our UDP listeners | ||
err = peerConnection.SetLocalDescription(answer) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
// Block until ICE Gathering is complete, disabling trickle ICE | ||
// we do this because we only can exchange one signaling message | ||
// in a production application you should exchange ICE Candidates via OnICECandidate | ||
<-gatherComplete | ||
|
||
// Output the answer in base64 so we can paste it in browser | ||
fmt.Println(signal.Encode(*peerConnection.LocalDescription())) | ||
|
||
// Block forever | ||
select {} | ||
} |
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
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
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
Oops, something went wrong.