-
Notifications
You must be signed in to change notification settings - Fork 119
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Changelog: * Better support for non-webrtc use cases * Enable multiple readers * Enhance codec selectors * Update APIs to reflect on the new v3 webrtc design * Cleaner APIs
- Loading branch information
1 parent
1720eee
commit 2f5e4ee
Showing
13 changed files
with
543 additions
and
493 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,117 @@ | ||
package mediadevices | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/pion/mediadevices/pkg/codec" | ||
"github.com/pion/mediadevices/pkg/io/audio" | ||
"github.com/pion/mediadevices/pkg/io/video" | ||
"github.com/pion/mediadevices/pkg/prop" | ||
"github.com/pion/webrtc/v2" | ||
) | ||
|
||
// CodecSelector is a container of video and audio encoder builders, which later will be used | ||
// for codec matching. | ||
type CodecSelector struct { | ||
videoEncoders []codec.VideoEncoderBuilder | ||
audioEncoders []codec.AudioEncoderBuilder | ||
} | ||
|
||
// CodecSelectorOption is a type for specifying CodecSelector options | ||
type CodecSelectorOption func(*CodecSelector) | ||
|
||
// WithVideoEncoders replace current video codecs with listed encoders | ||
func WithVideoEncoders(encoders ...codec.VideoEncoderBuilder) CodecSelectorOption { | ||
return func(t *CodecSelector) { | ||
t.videoEncoders = encoders | ||
} | ||
} | ||
|
||
// WithVideoEncoders replace current audio codecs with listed encoders | ||
func WithAudioEncoders(encoders ...codec.AudioEncoderBuilder) CodecSelectorOption { | ||
return func(t *CodecSelector) { | ||
t.audioEncoders = encoders | ||
} | ||
} | ||
|
||
// NewCodecSelector constructs CodecSelector with given variadic options | ||
func NewCodecSelector(opts ...CodecSelectorOption) *CodecSelector { | ||
var track CodecSelector | ||
|
||
for _, opt := range opts { | ||
opt(&track) | ||
} | ||
|
||
return &track | ||
} | ||
|
||
// Populate lets the webrtc engine be aware of supported codecs that are contained in CodecSelector | ||
func (selector *CodecSelector) Populate(setting *webrtc.MediaEngine) { | ||
for _, encoder := range selector.videoEncoders { | ||
setting.RegisterCodec(encoder.RTPCodec().RTPCodec) | ||
} | ||
|
||
for _, encoder := range selector.audioEncoders { | ||
setting.RegisterCodec(encoder.RTPCodec().RTPCodec) | ||
} | ||
} | ||
|
||
func (selector *CodecSelector) selectVideoCodec(wantCodecs []*webrtc.RTPCodec, reader video.Reader, inputProp prop.Media) (codec.ReadCloser, *codec.RTPCodec, error) { | ||
var selectedEncoder codec.VideoEncoderBuilder | ||
var encodedReader codec.ReadCloser | ||
var errReasons []string | ||
var err error | ||
|
||
outer: | ||
for _, wantCodec := range wantCodecs { | ||
name := wantCodec.Name | ||
for _, encoder := range selector.videoEncoders { | ||
if encoder.RTPCodec().Name == name { | ||
encodedReader, err = encoder.BuildVideoEncoder(reader, inputProp) | ||
if err == nil { | ||
selectedEncoder = encoder | ||
break outer | ||
} | ||
} | ||
|
||
errReasons = append(errReasons, fmt.Sprintf("%s: %s", encoder.RTPCodec().Name, err)) | ||
} | ||
} | ||
|
||
if selectedEncoder == nil { | ||
return nil, nil, errors.New(strings.Join(errReasons, "\n\n")) | ||
} | ||
|
||
return encodedReader, selectedEncoder.RTPCodec(), nil | ||
} | ||
|
||
func (selector *CodecSelector) selectAudioCodec(wantCodecs []*webrtc.RTPCodec, reader audio.Reader, inputProp prop.Media) (codec.ReadCloser, *codec.RTPCodec, error) { | ||
var selectedEncoder codec.AudioEncoderBuilder | ||
var encodedReader codec.ReadCloser | ||
var errReasons []string | ||
var err error | ||
|
||
outer: | ||
for _, wantCodec := range wantCodecs { | ||
name := wantCodec.Name | ||
for _, encoder := range selector.audioEncoders { | ||
if encoder.RTPCodec().Name == name { | ||
encodedReader, err = encoder.BuildAudioEncoder(reader, inputProp) | ||
if err == nil { | ||
selectedEncoder = encoder | ||
break outer | ||
} | ||
} | ||
|
||
errReasons = append(errReasons, fmt.Sprintf("%s: %s", encoder.RTPCodec().Name, err)) | ||
} | ||
} | ||
|
||
if selectedEncoder == nil { | ||
return nil, nil, errors.New(strings.Join(errReasons, "\n\n")) | ||
} | ||
|
||
return encodedReader, selectedEncoder.RTPCodec(), nil | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// This is an example of using mediadevices to broadcast your camera through http. | ||
// The example doesn't aim to be performant, but rather it strives to be simple. | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"image/jpeg" | ||
"io" | ||
"log" | ||
"mime/multipart" | ||
"net/http" | ||
"net/textproto" | ||
|
||
"github.com/pion/mediadevices" | ||
"github.com/pion/mediadevices/pkg/prop" | ||
|
||
// Note: If you don't have a camera or microphone or your adapters are not supported, | ||
// you can always swap your adapters with our dummy adapters below. | ||
// _ "github.com/pion/mediadevices/pkg/driver/videotest" | ||
_ "github.com/pion/mediadevices/pkg/driver/camera" // This is required to register camera adapter | ||
) | ||
|
||
func must(err error) { | ||
if err != nil { | ||
panic(err) | ||
} | ||
} | ||
|
||
func main() { | ||
s, err := mediadevices.GetUserMedia(mediadevices.MediaStreamConstraints{ | ||
Video: func(constraint *mediadevices.MediaTrackConstraints) { | ||
constraint.Width = prop.Int(600) | ||
constraint.Height = prop.Int(400) | ||
}, | ||
}) | ||
must(err) | ||
|
||
t := s.GetVideoTracks()[0] | ||
videoTrack := t.(*mediadevices.VideoTrack) | ||
|
||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | ||
var buf bytes.Buffer | ||
videoReader := videoTrack.NewReader(false) | ||
mimeWriter := multipart.NewWriter(w) | ||
|
||
contentType := fmt.Sprintf("multipart/x-mixed-replace;boundary=%s", mimeWriter.Boundary()) | ||
w.Header().Add("Content-Type", contentType) | ||
|
||
partHeader := make(textproto.MIMEHeader) | ||
partHeader.Add("Content-Type", "image/jpeg") | ||
|
||
for { | ||
frame, release, err := videoReader.Read() | ||
if err == io.EOF { | ||
return | ||
} | ||
must(err) | ||
|
||
err = jpeg.Encode(&buf, frame, nil) | ||
// Since we're done with img, we need to release img so that that the original owner can reuse | ||
// this memory. | ||
release() | ||
must(err) | ||
|
||
partWriter, err := mimeWriter.CreatePart(partHeader) | ||
must(err) | ||
|
||
_, err = partWriter.Write(buf.Bytes()) | ||
buf.Reset() | ||
must(err) | ||
} | ||
}) | ||
|
||
fmt.Println("listening on http://localhost:1313") | ||
log.Println(http.ListenAndServe("localhost:1313", nil)) | ||
} |
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.