mirror of
https://github.com/pion/mediadevices.git
synced 2025-09-26 20:41:46 +08:00
Add video RTP reader
This commit is contained in:
30
examples/rtp/README.md
Normal file
30
examples/rtp/README.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
## Instructions
|
||||||
|
|
||||||
|
### Download rtp-send example
|
||||||
|
|
||||||
|
```
|
||||||
|
go get github.com/pion/mediadevices/examples/rtp-send
|
||||||
|
```
|
||||||
|
|
||||||
|
### Listen RTP
|
||||||
|
|
||||||
|
Install GStreamer and run:
|
||||||
|
```
|
||||||
|
gst-launch-1.0 udpsrc port=5000 caps=application/x-rtp,encode-name=VP8 \
|
||||||
|
! rtpvp8depay ! vp8dec ! videoconvert ! autovideosink
|
||||||
|
```
|
||||||
|
|
||||||
|
Or run VLC media plyer:
|
||||||
|
```
|
||||||
|
vlc ./vp8.sdp
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run rtp-send
|
||||||
|
|
||||||
|
Run `rtp-send localhost:5000`
|
||||||
|
|
||||||
|
A video should start playing in your GStreamer or VLC window.
|
||||||
|
It's not WebRTC, but pure RTP.
|
||||||
|
|
||||||
|
Congrats, you have used pion-MediaDevices! Now start building something cool
|
||||||
|
|
76
examples/rtp/main.go
Normal file
76
examples/rtp/main.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/pion/mediadevices"
|
||||||
|
"github.com/pion/mediadevices/pkg/codec/vpx" // This is required to use VP8/VP9 video encoder
|
||||||
|
_ "github.com/pion/mediadevices/pkg/driver/camera" // This is required to register camera adapter
|
||||||
|
"github.com/pion/mediadevices/pkg/frame"
|
||||||
|
"github.com/pion/mediadevices/pkg/prop"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
mtu = 1000
|
||||||
|
)
|
||||||
|
|
||||||
|
func must(err error) {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) != 2 {
|
||||||
|
fmt.Printf("usage: %s host:port\n", os.Args[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dest := os.Args[1]
|
||||||
|
|
||||||
|
vp8Params, err := vpx.NewVP8Params()
|
||||||
|
must(err)
|
||||||
|
vp8Params.BitRate = 100000 // 100kbps
|
||||||
|
|
||||||
|
codecSelector := mediadevices.NewCodecSelector(
|
||||||
|
mediadevices.WithVideoEncoders(&vp8Params),
|
||||||
|
)
|
||||||
|
|
||||||
|
mediaStream, err := mediadevices.GetUserMedia(mediadevices.MediaStreamConstraints{
|
||||||
|
Video: func(c *mediadevices.MediaTrackConstraints) {
|
||||||
|
c.FrameFormat = prop.FrameFormat(frame.FormatYUY2)
|
||||||
|
c.Width = prop.Int(640)
|
||||||
|
c.Height = prop.Int(480)
|
||||||
|
},
|
||||||
|
Codec: codecSelector,
|
||||||
|
})
|
||||||
|
must(err)
|
||||||
|
|
||||||
|
videoTrack := mediaStream.GetVideoTracks()[0].(*mediadevices.VideoTrack)
|
||||||
|
defer videoTrack.Close()
|
||||||
|
|
||||||
|
rtpReader, err := videoTrack.NewRTPReader(vp8Params.RTPCodec().Name, mtu)
|
||||||
|
must(err)
|
||||||
|
|
||||||
|
addr, err := net.ResolveUDPAddr("udp", dest)
|
||||||
|
must(err)
|
||||||
|
conn, err := net.DialUDP("udp", nil, addr)
|
||||||
|
must(err)
|
||||||
|
|
||||||
|
buff := make([]byte, mtu)
|
||||||
|
for {
|
||||||
|
pkts, release, err := rtpReader.Read()
|
||||||
|
must(err)
|
||||||
|
|
||||||
|
for _, pkt := range pkts {
|
||||||
|
n, err := pkt.MarshalTo(buff)
|
||||||
|
must(err)
|
||||||
|
|
||||||
|
_, err = conn.Write(buff[:n])
|
||||||
|
must(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
release()
|
||||||
|
}
|
||||||
|
}
|
9
examples/rtp/vp8.sdp
Normal file
9
examples/rtp/vp8.sdp
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
v=0
|
||||||
|
o=- 1234567890 1234567890 IN IP4 0.0.0.0
|
||||||
|
s=RTP-Send Example
|
||||||
|
i=Example
|
||||||
|
c=IN IP4 0.0.0.0
|
||||||
|
t=0 0
|
||||||
|
a=recvonly
|
||||||
|
m=video 5000 RTP/AVP 100
|
||||||
|
a=rtpmap:100 VP8/90000
|
21
rtpreader.go
Normal file
21
rtpreader.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package mediadevices
|
||||||
|
|
||||||
|
import "github.com/pion/rtp"
|
||||||
|
|
||||||
|
type RTPReader interface {
|
||||||
|
Read() (pkts []*rtp.Packet, release func(), err error)
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type rtpReaderImpl struct {
|
||||||
|
readFn func() ([]*rtp.Packet, func(), error)
|
||||||
|
closeFn func() error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rtpReaderImpl) Read() ([]*rtp.Packet, func(), error) {
|
||||||
|
return r.readFn()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rtpReaderImpl) Close() error {
|
||||||
|
return r.closeFn()
|
||||||
|
}
|
36
track.go
36
track.go
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/pion/mediadevices/pkg/io/audio"
|
"github.com/pion/mediadevices/pkg/io/audio"
|
||||||
"github.com/pion/mediadevices/pkg/io/video"
|
"github.com/pion/mediadevices/pkg/io/video"
|
||||||
"github.com/pion/mediadevices/pkg/wave"
|
"github.com/pion/mediadevices/pkg/wave"
|
||||||
|
"github.com/pion/rtp"
|
||||||
"github.com/pion/webrtc/v2"
|
"github.com/pion/webrtc/v2"
|
||||||
"github.com/pion/webrtc/v2/pkg/media"
|
"github.com/pion/webrtc/v2/pkg/media"
|
||||||
)
|
)
|
||||||
@@ -259,6 +260,41 @@ func (track *VideoTrack) Unbind(pc *webrtc.PeerConnection) error {
|
|||||||
return track.unbind(pc)
|
return track.unbind(pc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (track *VideoTrack) NewRTPReader(codecName string, mtu int) (RTPReader, error) {
|
||||||
|
reader := track.NewReader(false)
|
||||||
|
inputProp, err := detectCurrentVideoProp(track.Broadcaster)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
encodedReader, selectedCodec, err := track.selector.selectVideoCodecByNames(reader, inputProp, codecName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sample := newVideoSampler(selectedCodec.ClockRate)
|
||||||
|
|
||||||
|
// FIXME: not sure the best way to get unique ssrc. We probably should have a global keeper that can generate a random ID and does book keeping?
|
||||||
|
packetizer := rtp.NewPacketizer(mtu, selectedCodec.PayloadType, rand.Uint32(), selectedCodec.Payloader, rtp.NewRandomSequencer(), selectedCodec.ClockRate)
|
||||||
|
|
||||||
|
return &rtpReaderImpl{
|
||||||
|
readFn: func() ([]*rtp.Packet, func(), error) {
|
||||||
|
encoded, release, err := encodedReader.Read()
|
||||||
|
if err != nil {
|
||||||
|
encodedReader.Close()
|
||||||
|
track.onError(err)
|
||||||
|
return nil, func() {}, err
|
||||||
|
}
|
||||||
|
defer release()
|
||||||
|
|
||||||
|
samples := sample()
|
||||||
|
pkts := packetizer.Packetize(encoded, samples)
|
||||||
|
return pkts, release, err
|
||||||
|
},
|
||||||
|
closeFn: encodedReader.Close,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// AudioTrack is a specific track type that contains audio source which allows multiple readers to access, and
|
// AudioTrack is a specific track type that contains audio source which allows multiple readers to access, and
|
||||||
// manipulate.
|
// manipulate.
|
||||||
type AudioTrack struct {
|
type AudioTrack struct {
|
||||||
|
Reference in New Issue
Block a user