mirror of
https://github.com/pion/mediadevices.git
synced 2025-09-28 05:12:15 +08:00
Compare commits
60 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
be5f684ea6 | ||
![]() |
a88c2daf89 | ||
![]() |
1f313a9d61 | ||
![]() |
19eaf375ff | ||
![]() |
b3c94a1f7b | ||
![]() |
86cb9f8ce8 | ||
![]() |
f8d1f974cf | ||
![]() |
809f74cafc | ||
![]() |
eb2db82766 | ||
![]() |
b4c6eb5409 | ||
![]() |
b263026d52 | ||
![]() |
070ab924f9 | ||
![]() |
23177a5d75 | ||
![]() |
3a04686875 | ||
![]() |
5f95b84719 | ||
![]() |
8919ba4fe5 | ||
![]() |
153c36e461 | ||
![]() |
10769b702e | ||
![]() |
2948735964 | ||
![]() |
ba848b3416 | ||
![]() |
94c6b66e46 | ||
![]() |
96fd92142c | ||
![]() |
d71b72c64d | ||
![]() |
8c2c8a9b27 | ||
![]() |
1e03f61b4b | ||
![]() |
b863c105c8 | ||
![]() |
6411b00e93 | ||
![]() |
acd2cb992b | ||
![]() |
dafd208de7 | ||
![]() |
fcec5a9149 | ||
![]() |
3d3830f7ff | ||
![]() |
655b513810 | ||
![]() |
eaaaacfc6b | ||
![]() |
fa95e47bad | ||
![]() |
020de77bc9 | ||
![]() |
33b6412c26 | ||
![]() |
f29d08ae6b | ||
![]() |
b5b0653697 | ||
![]() |
a1087f7f4e | ||
![]() |
217e634f7e | ||
![]() |
60b8e3ae1b | ||
![]() |
7df3114cdc | ||
![]() |
9741508d2b | ||
![]() |
7a569f0901 | ||
![]() |
ee40fcd070 | ||
![]() |
499c08d513 | ||
![]() |
5a1bd11087 | ||
![]() |
7ce935eac8 | ||
![]() |
a359005a7d | ||
![]() |
ca4116b5ce | ||
![]() |
8a4e0779d7 | ||
![]() |
d222ff3d74 | ||
![]() |
b9bb4fdc34 | ||
![]() |
cc823958e1 | ||
![]() |
64f39187b8 | ||
![]() |
c56cc487a3 | ||
![]() |
d86fc4a3e9 | ||
![]() |
2f21d9e738 | ||
![]() |
3316476b30 | ||
![]() |
0b1a19f343 |
42
.github/workflows/ci.yaml
vendored
42
.github/workflows/ci.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
go: [ '1.15', '1.14' ]
|
go: [ '1.16', '1.15' ]
|
||||||
name: Linux Go ${{ matrix.go }}
|
name: Linux Go ${{ matrix.go }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -30,28 +30,16 @@ jobs:
|
|||||||
libva-dev \
|
libva-dev \
|
||||||
libvpx-dev \
|
libvpx-dev \
|
||||||
libx264-dev
|
libx264-dev
|
||||||
- name: go vet
|
- name: Run Test Suite
|
||||||
run: go vet $(go list ./... | grep -v mmal)
|
run: make test
|
||||||
- name: go build
|
- uses: codecov/codecov-action@v2
|
||||||
run: go build $(go list ./... | grep -v mmal)
|
if: matrix.go == '1.16'
|
||||||
- name: go build without CGO
|
|
||||||
run: go build . pkg/...
|
|
||||||
env:
|
|
||||||
CGO_ENABLED: 0
|
|
||||||
- name: go test
|
|
||||||
run: go test -v -race -coverprofile=coverage.txt -covermode=atomic $(go list ./... | grep -v mmal)
|
|
||||||
- uses: codecov/codecov-action@v1
|
|
||||||
if: matrix.go == '1.15'
|
|
||||||
- name: go test without CGO
|
|
||||||
run: go test . pkg/... -v
|
|
||||||
env:
|
|
||||||
CGO_ENABLED: 0
|
|
||||||
build-darwin:
|
build-darwin:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
go: [ '1.15', '1.14' ]
|
go: [ '1.16', '1.15' ]
|
||||||
name: Darwin Go ${{ matrix.go }}
|
name: Darwin Go ${{ matrix.go }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -67,20 +55,8 @@ jobs:
|
|||||||
opus \
|
opus \
|
||||||
libvpx \
|
libvpx \
|
||||||
x264
|
x264
|
||||||
- name: go vet
|
- name: Run Test Suite
|
||||||
run: go vet $(go list ./... | grep -v mmal)
|
run: make test
|
||||||
- name: go build
|
|
||||||
run: go build $(go list ./... | grep -v mmal)
|
|
||||||
- name: go build without CGO
|
|
||||||
run: go build . pkg/...
|
|
||||||
env:
|
|
||||||
CGO_ENABLED: 0
|
|
||||||
- name: go test
|
|
||||||
run: go test -v -race $(go list ./... | grep -v mmal)
|
|
||||||
- name: go test without CGO
|
|
||||||
run: go test . pkg/... -v
|
|
||||||
env:
|
|
||||||
CGO_ENABLED: 0
|
|
||||||
check-licenses:
|
check-licenses:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Check Licenses
|
name: Check Licenses
|
||||||
@@ -90,7 +66,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: '1.15'
|
go-version: '1.16'
|
||||||
- name: Installing go-licenses
|
- name: Installing go-licenses
|
||||||
run: go get github.com/google/go-licenses
|
run: go get github.com/google/go-licenses
|
||||||
- name: Checking licenses
|
- name: Checking licenses
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,3 +12,4 @@
|
|||||||
*.out
|
*.out
|
||||||
|
|
||||||
scripts/cross
|
scripts/cross
|
||||||
|
coverage.txt
|
||||||
|
82
Makefile
Normal file
82
Makefile
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
docker_owner := lherman
|
||||||
|
docker_prefix := cross
|
||||||
|
toolchain_dockerfiles := dockerfiles
|
||||||
|
script_path := $(realpath scripts)
|
||||||
|
toolchain_path := $(script_path)/$(docker_prefix)
|
||||||
|
os_list := \
|
||||||
|
linux \
|
||||||
|
windows \
|
||||||
|
darwin
|
||||||
|
arch_list := \
|
||||||
|
armv7 \
|
||||||
|
arm64 \
|
||||||
|
x64
|
||||||
|
supported_platforms := \
|
||||||
|
linux-armv7 \
|
||||||
|
linux-arm64 \
|
||||||
|
linux-x64 \
|
||||||
|
windows-x64 \
|
||||||
|
darwin-x64
|
||||||
|
cmd_build := build
|
||||||
|
cmd_test := test
|
||||||
|
examples_dir := examples
|
||||||
|
codec_dir := pkg/codec
|
||||||
|
codec_list := $(shell ls $(codec_dir)/*/Makefile)
|
||||||
|
codec_list := $(codec_list:$(codec_dir)/%/Makefile=%)
|
||||||
|
targets := $(foreach codec, $(codec_list), $(addprefix $(cmd_build)-$(codec)-, $(supported_platforms)))
|
||||||
|
pkgs_without_mmal := $(shell go list ./... | grep -v mmal)
|
||||||
|
pkgs_without_cgo := $(shell go list ./... | grep -v pkg/codec | grep -v pkg/driver | grep -v pkg/avfoundation)
|
||||||
|
|
||||||
|
define BUILD_TEMPLATE
|
||||||
|
ifneq (,$$(findstring $(2)-$(3),$$(supported_platforms)))
|
||||||
|
$$(cmd_build)-$(1)-$(2)-$(3): toolchain-$(2)-$(3)
|
||||||
|
$$(MAKE) --directory=$$(codec_dir)/$(1) \
|
||||||
|
MEDIADEVICES_TOOLCHAIN_BIN=$$(toolchain_path)/$(docker_prefix)-$(2)-$(3) \
|
||||||
|
MEDIADEVICES_TARGET_PLATFORM=$(2)-$(3) \
|
||||||
|
MEDIADEVICES_TARGET_OS=$(2) \
|
||||||
|
MEDIADEVICES_TARGET_ARCH=$(3)
|
||||||
|
endif
|
||||||
|
endef
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: $(cmd_test) $(cmd_build)
|
||||||
|
|
||||||
|
# Subcommand:
|
||||||
|
# make build[-<codec_name>-<os>-<arch>]
|
||||||
|
#
|
||||||
|
# Description:
|
||||||
|
# Build codec dependencies to multiple platforms.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# * make build: build all codecs for all supported platforms
|
||||||
|
# * make build-opus-darwin-x64: only build opus for darwin-x64 platform
|
||||||
|
$(cmd_build): $(targets)
|
||||||
|
|
||||||
|
toolchain-%: $(toolchain_dockerfiles)
|
||||||
|
$(MAKE) --directory=$< "$*" \
|
||||||
|
MEDIADEVICES_DOCKER_OWNER=$(docker_owner) \
|
||||||
|
MEDIADEVICES_DOCKER_PREFIX=$(docker_prefix)
|
||||||
|
@mkdir -p $(toolchain_path)
|
||||||
|
@docker run $(docker_owner)/$(docker_prefix)-$* > \
|
||||||
|
$(toolchain_path)/$(docker_prefix)-$*
|
||||||
|
@chmod +x $(toolchain_path)/$(docker_prefix)-$*
|
||||||
|
|
||||||
|
$(foreach codec, $(codec_list), \
|
||||||
|
$(foreach os, $(os_list), \
|
||||||
|
$(foreach arch, $(arch_list), \
|
||||||
|
$(eval $(call BUILD_TEMPLATE,$(codec),$(os),$(arch))))))
|
||||||
|
|
||||||
|
# Subcommand:
|
||||||
|
# make test
|
||||||
|
#
|
||||||
|
# Description:
|
||||||
|
# Run a series of tests
|
||||||
|
$(cmd_test):
|
||||||
|
go vet $(pkgs_without_mmal)
|
||||||
|
go build $(pkgs_without_mmal)
|
||||||
|
# go build without CGO
|
||||||
|
CGO_ENABLED=0 go build $(pkgs_without_cgo)
|
||||||
|
# go build with CGO
|
||||||
|
CGO_ENABLED=1 go build $(pkgs_without_mmal)
|
||||||
|
$(MAKE) --directory=$(examples_dir)
|
||||||
|
go test -v -race -coverprofile=coverage.txt -covermode=atomic $(pkgs_without_mmal)
|
@@ -87,7 +87,7 @@ func main() {
|
|||||||
| Microphone | ✔️ | ✔️ | ✔️ |
|
| Microphone | ✔️ | ✔️ | ✔️ |
|
||||||
| Screen | ✔️ | ✔️ | ✔️ |
|
| Screen | ✔️ | ✔️ | ✔️ |
|
||||||
|
|
||||||
By default, there's no media input registered. This decision was made to allow you to pay what you need. Therefore, you need to import the associated packages for the media inputs. For example, if you want to use a camera, you need to import the camera package as a side effect:
|
By default, there's no media input registered. This decision was made to allow you to play only what you need. Therefore, you need to import the associated packages for the media inputs. For example, if you want to use a camera, you need to import the camera package as a side effect:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
|
70
build.sh
70
build.sh
@@ -1,70 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
MEDIADEVICES_TOOLCHAIN_OWNER=lherman
|
|
||||||
MEDIADEVICES_TOOLCHAIN_PREFIX=cross
|
|
||||||
MEDIADEVICES_SCRIPT_PATH=$(realpath ./scripts)
|
|
||||||
MEDIADEVICES_TOOLCHAIN_PATH=${MEDIADEVICES_SCRIPT_PATH}/${MEDIADEVICES_TOOLCHAIN_PREFIX}
|
|
||||||
MEDIADEVICES_DOCKERFILES_PATH=dockerfiles
|
|
||||||
|
|
||||||
# Reference: https://github.com/dockcross/dockcross#cross-compilers
|
|
||||||
MEDIADEVICES_TARGET_PLATFORMS=(
|
|
||||||
linux-armv7
|
|
||||||
linux-arm64
|
|
||||||
linux-x64
|
|
||||||
windows-x64
|
|
||||||
darwin-x64
|
|
||||||
)
|
|
||||||
|
|
||||||
if [[ -z ${VERBOSE} ]]; then
|
|
||||||
MEDIADEVICES_OUTPUT=/dev/null
|
|
||||||
else
|
|
||||||
MEDIADEVICES_OUTPUT=/dev/stdout
|
|
||||||
fi
|
|
||||||
|
|
||||||
install_toolchains() {
|
|
||||||
bash ${MEDIADEVICES_DOCKERFILES_PATH}/build.sh &> ${MEDIADEVICES_OUTPUT}
|
|
||||||
for platform in ${MEDIADEVICES_TARGET_PLATFORMS[@]}
|
|
||||||
do
|
|
||||||
mkdir -p ${MEDIADEVICES_TOOLCHAIN_PATH}
|
|
||||||
image=${MEDIADEVICES_TOOLCHAIN_OWNER}/${MEDIADEVICES_TOOLCHAIN_PREFIX}-${platform}
|
|
||||||
bin_path=${MEDIADEVICES_TOOLCHAIN_PATH}/${MEDIADEVICES_TOOLCHAIN_PREFIX}-${platform}
|
|
||||||
docker run ${image} > ${bin_path}
|
|
||||||
chmod +x ${bin_path}
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
build() {
|
|
||||||
sub_builds=$(find pkg -type f -name "build.sh")
|
|
||||||
for sub_build in ${sub_builds[@]}
|
|
||||||
do
|
|
||||||
sub_build=$(realpath ${sub_build})
|
|
||||||
sub_build_dir=$(dirname ${sub_build})
|
|
||||||
current_dir=${PWD}
|
|
||||||
cd $sub_build_dir
|
|
||||||
for platform in ${MEDIADEVICES_TARGET_PLATFORMS[@]}
|
|
||||||
do
|
|
||||||
export MEDIADEVICES_TOOLCHAIN_BIN=${MEDIADEVICES_TOOLCHAIN_PATH}/${MEDIADEVICES_TOOLCHAIN_PREFIX}-${platform}
|
|
||||||
# convert '-' to '_' since '_' is more common in library names
|
|
||||||
export MEDIADEVICES_TARGET_PLATFORM=${platform//-/_}
|
|
||||||
export MEDIADEVICES_TARGET_OS=$(echo $MEDIADEVICES_TARGET_PLATFORM | cut -d'_' -f1)
|
|
||||||
export MEDIADEVICES_TARGET_ARCH=${platform//${MEDIADEVICES_TARGET_OS}-/}
|
|
||||||
|
|
||||||
echo "Building ${sub_build_dir}:"
|
|
||||||
echo " PLATFORM : ${MEDIADEVICES_TARGET_PLATFORM}"
|
|
||||||
echo " OS : ${MEDIADEVICES_TARGET_OS}"
|
|
||||||
echo " ARCH : ${MEDIADEVICES_TARGET_ARCH}"
|
|
||||||
echo " TOOLCHAIN_BIN : ${MEDIADEVICES_TOOLCHAIN_BIN}"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
${sub_build} &> ${MEDIADEVICES_OUTPUT}
|
|
||||||
done
|
|
||||||
cd ${current_dir}
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
if [[ $# > 0 && $1 != "all" ]]; then
|
|
||||||
MEDIADEVICES_TARGET_PLATFORMS=($1)
|
|
||||||
fi
|
|
||||||
|
|
||||||
install_toolchains
|
|
||||||
build
|
|
11
dockerfiles/Makefile
Normal file
11
dockerfiles/Makefile
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
dockerfiles := $(wildcard *.Dockerfile)
|
||||||
|
supported_platforms := $(dockerfiles:.Dockerfile=)
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: $(supported_platforms)
|
||||||
|
|
||||||
|
%: %.Dockerfile guard-MEDIADEVICES_DOCKER_OWNER guard-MEDIADEVICES_DOCKER_PREFIX
|
||||||
|
docker build -t "$(MEDIADEVICES_DOCKER_OWNER)/$(MEDIADEVICES_DOCKER_PREFIX)-$@" -f "$<" .
|
||||||
|
|
||||||
|
guard-%:
|
||||||
|
@if [ -z ${$*} ]; then echo "$* is a required environment variable"; exit 1; fi
|
@@ -1,13 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
cd $(dirname $0)
|
|
||||||
|
|
||||||
OWNER=lherman
|
|
||||||
PREFIX=cross
|
|
||||||
IMAGES=$(ls *.Dockerfile)
|
|
||||||
|
|
||||||
for image in ${IMAGES[@]}
|
|
||||||
do
|
|
||||||
tag=${OWNER}/cross-${image//.Dockerfile/}
|
|
||||||
docker build -t "${tag}" -f "$image" .
|
|
||||||
done
|
|
1
examples/.gitignore
vendored
1
examples/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
go.sum
|
|
8
examples/Makefile
Normal file
8
examples/Makefile
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
examples := $(shell find * -maxdepth 0 -type d)
|
||||||
|
examples := $(filter-out internal,$(examples))
|
||||||
|
|
||||||
|
.PHONY: all $(examples)
|
||||||
|
all: $(examples)
|
||||||
|
|
||||||
|
$(examples):
|
||||||
|
cd $@ && go build -mod=mod
|
@@ -43,7 +43,7 @@ func main() {
|
|||||||
|
|
||||||
mediaStream, err := mediadevices.GetUserMedia(mediadevices.MediaStreamConstraints{
|
mediaStream, err := mediadevices.GetUserMedia(mediadevices.MediaStreamConstraints{
|
||||||
Video: func(c *mediadevices.MediaTrackConstraints) {
|
Video: func(c *mediadevices.MediaTrackConstraints) {
|
||||||
c.FrameFormat = prop.FrameFormat(frame.FormatYUY2)
|
c.FrameFormat = prop.FrameFormat(frame.FormatI420)
|
||||||
c.Width = prop.Int(640)
|
c.Width = prop.Int(640)
|
||||||
c.Height = prop.Int(480)
|
c.Height = prop.Int(480)
|
||||||
},
|
},
|
||||||
|
@@ -8,8 +8,8 @@ import (
|
|||||||
|
|
||||||
pigo "github.com/esimov/pigo/core"
|
pigo "github.com/esimov/pigo/core"
|
||||||
"github.com/pion/mediadevices"
|
"github.com/pion/mediadevices"
|
||||||
_ "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/frame"
|
||||||
|
_ "github.com/pion/mediadevices/pkg/driver/camera" // This is required to register camera adapter
|
||||||
"github.com/pion/mediadevices/pkg/prop"
|
"github.com/pion/mediadevices/pkg/prop"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ func main() {
|
|||||||
|
|
||||||
mediaStream, err := mediadevices.GetUserMedia(mediadevices.MediaStreamConstraints{
|
mediaStream, err := mediadevices.GetUserMedia(mediadevices.MediaStreamConstraints{
|
||||||
Video: func(c *mediadevices.MediaTrackConstraints) {
|
Video: func(c *mediadevices.MediaTrackConstraints) {
|
||||||
c.FrameFormat = prop.FrameFormatExact(frame.FormatUYVY)
|
c.FrameFormat = prop.FrameFormatOneOf{frame.FormatI420, frame.FormatYUY2}
|
||||||
c.Width = prop.Int(640)
|
c.Width = prop.Int(640)
|
||||||
c.Height = prop.Int(480)
|
c.Height = prop.Int(480)
|
||||||
},
|
},
|
||||||
@@ -97,7 +97,7 @@ func main() {
|
|||||||
frame, release, err := videoReader.Read()
|
frame, release, err := videoReader.Read()
|
||||||
must(err)
|
must(err)
|
||||||
|
|
||||||
// Since we asked the frame format to be exactly YUY2 in GetUserMedia, we can guarantee that it must be YCbCr
|
// Since we asked the frame format to be exactly I420/YUY2 in GetUserMedia, we can guarantee that it must be YCbCr
|
||||||
if detectFace(frame.(*image.YCbCr)) {
|
if detectFace(frame.(*image.YCbCr)) {
|
||||||
log.Println("Detect a face")
|
log.Println("Detect a face")
|
||||||
}
|
}
|
||||||
|
@@ -2,8 +2,10 @@ module github.com/pion/mediadevices/examples
|
|||||||
|
|
||||||
go 1.14
|
go 1.14
|
||||||
|
|
||||||
// Please don't commit require entries of examples.
|
require (
|
||||||
// `git checkout master examples/go.mod` to revert this file.
|
github.com/esimov/pigo v1.4.3
|
||||||
require github.com/pion/mediadevices v0.0.0
|
github.com/pion/mediadevices v0.0.0
|
||||||
|
github.com/pion/webrtc/v3 v3.0.20
|
||||||
|
)
|
||||||
|
|
||||||
replace github.com/pion/mediadevices v0.0.0 => ../
|
replace github.com/pion/mediadevices v0.0.0 => ../
|
||||||
|
151
examples/go.sum
Normal file
151
examples/go.sum
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/blackjack/webcam v0.0.0-20200313125108-10ed912a8539 h1:1aIqYfg9s9RETAJHGfVKZW4ok0b22p4QTwk8MsdRtPs=
|
||||||
|
github.com/blackjack/webcam v0.0.0-20200313125108-10ed912a8539/go.mod h1:G0X+rEqYPWSq0dG8OMf8M446MtKytzpPjgS3HbdOJZ4=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/disintegration/imaging v1.6.1/go.mod h1:xuIt+sRxDFrHS0drzXUlCJthkJ8k7lkkUojDSR247MQ=
|
||||||
|
github.com/esimov/pigo v1.4.3 h1:xl098Z9CHmouywvyRZepuKx8aSWHBs/0lZtp7Yt5g28=
|
||||||
|
github.com/esimov/pigo v1.4.3/go.mod h1:aOTYpOWsqniACzXKdSOGkqI6CnWQpP8tFjgtUOARoEs=
|
||||||
|
github.com/fogleman/gg v1.0.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
|
github.com/gen2brain/malgo v0.10.29 h1:bTYiUTUKJsEomNby+W0hgyLrOttUXIk4lTEnKA54iqM=
|
||||||
|
github.com/gen2brain/malgo v0.10.29/go.mod h1:zHSUNZAXfCeNsZou0RtQ6Zk7gDYLIcKOrUWtAdksnEs=
|
||||||
|
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo=
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||||
|
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/kbinani/screenshot v0.0.0-20210326165202-b96eb3309bb0/go.mod h1:ZceVWGtzUZmxyN+/1I+oG31oOm1dOA2QUNbua9TLVdE=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
|
||||||
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
|
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||||
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
|
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
||||||
|
github.com/pion/datachannel v1.4.21 h1:3ZvhNyfmxsAqltQrApLPQMhSFNA+aT87RqyCq4OXmf0=
|
||||||
|
github.com/pion/datachannel v1.4.21/go.mod h1:oiNyP4gHx2DIwRzX/MFyH0Rz/Gz05OgBlayAI2hAWjg=
|
||||||
|
github.com/pion/dtls/v2 v2.0.8 h1:reGe8rNIMfO/UAeFLqO61tl64t154Qfkr4U3Gzu1tsg=
|
||||||
|
github.com/pion/dtls/v2 v2.0.8/go.mod h1:QuDII+8FVvk9Dp5t5vYIMTo7hh7uBkra+8QIm7QGm10=
|
||||||
|
github.com/pion/ice/v2 v2.0.16 h1:K6bzD8ef9vMKbGMTHaUweHXEyuNGnvr2zdqKoLKZPn0=
|
||||||
|
github.com/pion/ice/v2 v2.0.16/go.mod h1:SJNJzC27gDZoOW0UoxIoC8Hf2PDxG28hQyNdSexDu38=
|
||||||
|
github.com/pion/interceptor v0.0.12 h1:eC1iVneBIAQJEfaNAfDqAncJWhMDAnaXPRCJsltdokE=
|
||||||
|
github.com/pion/interceptor v0.0.12/go.mod h1:qzeuWuD/ZXvPqOnxNcnhWfkCZ2e1kwwslicyyPnhoK4=
|
||||||
|
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||||
|
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||||
|
github.com/pion/mdns v0.0.5 h1:Q2oj/JB3NqfzY9xGZ1fPzZzK7sDSD8rZPOvcIQ10BCw=
|
||||||
|
github.com/pion/mdns v0.0.5/go.mod h1:UgssrvdD3mxpi8tMxAXbsppL3vJ4Jipw1mTCW+al01g=
|
||||||
|
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||||
|
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||||
|
github.com/pion/rtcp v1.2.6 h1:1zvwBbyd0TeEuuWftrd/4d++m+/kZSeiguxU61LFWpo=
|
||||||
|
github.com/pion/rtcp v1.2.6/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0=
|
||||||
|
github.com/pion/rtp v1.6.2 h1:iGBerLX6JiDjB9NXuaPzHyxHFG9JsIEdgwTC0lp5n/U=
|
||||||
|
github.com/pion/rtp v1.6.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
|
||||||
|
github.com/pion/sctp v1.7.10/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
|
||||||
|
github.com/pion/sctp v1.7.12 h1:GsatLufywVruXbZZT1CKg+Jr8ZTkwiPnmUC/oO9+uuY=
|
||||||
|
github.com/pion/sctp v1.7.12/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s=
|
||||||
|
github.com/pion/sdp/v3 v3.0.4 h1:2Kf+dgrzJflNCSw3TV5v2VLeI0s/qkzy2r5jlR0wzf8=
|
||||||
|
github.com/pion/sdp/v3 v3.0.4/go.mod h1:bNiSknmJE0HYBprTHXKPQ3+JjacTv5uap92ueJZKsRk=
|
||||||
|
github.com/pion/srtp/v2 v2.0.2 h1:664iGzVmaY7KYS5M0gleY0DscRo9ReDfTxQrq4UgGoU=
|
||||||
|
github.com/pion/srtp/v2 v2.0.2/go.mod h1:VEyLv4CuxrwGY8cxM+Ng3bmVy8ckz/1t6A0q/msKOw0=
|
||||||
|
github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg=
|
||||||
|
github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA=
|
||||||
|
github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE=
|
||||||
|
github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A=
|
||||||
|
github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
|
||||||
|
github.com/pion/transport v0.12.3 h1:vdBfvfU/0Wq8kd2yhUMSDB/x+O4Z9MYVl2fJ5BT4JZw=
|
||||||
|
github.com/pion/transport v0.12.3/go.mod h1:OViWW9SP2peE/HbwBvARicmAVnesphkNkCVZIWJ6q9A=
|
||||||
|
github.com/pion/turn/v2 v2.0.5 h1:iwMHqDfPEDEOFzwWKT56eFmh6DYC6o/+xnLAEzgISbA=
|
||||||
|
github.com/pion/turn/v2 v2.0.5/go.mod h1:APg43CFyt/14Uy7heYUOGWdkem/Wu4PhCO/bjyrTqMw=
|
||||||
|
github.com/pion/udp v0.1.0 h1:uGxQsNyrqG3GLINv36Ff60covYmfrLoxzwnCsIYspXI=
|
||||||
|
github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths=
|
||||||
|
github.com/pion/webrtc/v3 v3.0.20 h1:Jj0sk45MqQdkR24E1wbFRmOzb1Lv258ot9zd2fYB/Pw=
|
||||||
|
github.com/pion/webrtc/v3 v3.0.20/go.mod h1:0eJnCpQrUMpRnvyonw4ZiWClToerpixrZ2KcoTxvX9M=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||||
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
|
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||||
|
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb h1:fqpd0EBDzlHRCjiphRR5Zo/RSWWQlWv34418dnEixWk=
|
||||||
|
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 h1:b0LrWgu8+q7z4J+0Y3Umo5q1dL7NXBkKBWkaVkAq17E=
|
||||||
|
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005 h1:pDMpM2zh2MT0kHy037cKlSby2nEhD50SYqwQk76Nm40=
|
||||||
|
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/term v0.0.0-20191110171634-ad39bd3f0407/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
@@ -41,7 +41,7 @@ func main() {
|
|||||||
|
|
||||||
mediaStream, err := mediadevices.GetUserMedia(mediadevices.MediaStreamConstraints{
|
mediaStream, err := mediadevices.GetUserMedia(mediadevices.MediaStreamConstraints{
|
||||||
Video: func(c *mediadevices.MediaTrackConstraints) {
|
Video: func(c *mediadevices.MediaTrackConstraints) {
|
||||||
c.FrameFormat = prop.FrameFormat(frame.FormatYUY2)
|
c.FrameFormat = prop.FrameFormat(frame.FormatI420)
|
||||||
c.Width = prop.Int(640)
|
c.Width = prop.Int(640)
|
||||||
c.Height = prop.Int(480)
|
c.Height = prop.Int(480)
|
||||||
},
|
},
|
||||||
|
@@ -71,7 +71,7 @@ func main() {
|
|||||||
|
|
||||||
s, err := mediadevices.GetUserMedia(mediadevices.MediaStreamConstraints{
|
s, err := mediadevices.GetUserMedia(mediadevices.MediaStreamConstraints{
|
||||||
Video: func(c *mediadevices.MediaTrackConstraints) {
|
Video: func(c *mediadevices.MediaTrackConstraints) {
|
||||||
c.FrameFormat = prop.FrameFormat(frame.FormatYUY2)
|
c.FrameFormat = prop.FrameFormat(frame.FormatI420)
|
||||||
c.Width = prop.Int(640)
|
c.Width = prop.Int(640)
|
||||||
c.Height = prop.Int(480)
|
c.Height = prop.Int(480)
|
||||||
},
|
},
|
||||||
@@ -111,13 +111,23 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create channel that is blocked until ICE Gathering is complete
|
||||||
|
gatherComplete := webrtc.GatheringCompletePromise(peerConnection)
|
||||||
|
|
||||||
// Sets the LocalDescription, and starts our UDP listeners
|
// Sets the LocalDescription, and starts our UDP listeners
|
||||||
err = peerConnection.SetLocalDescription(answer)
|
err = peerConnection.SetLocalDescription(answer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
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
|
// Output the answer in base64 so we can paste it in browser
|
||||||
fmt.Println(signal.Encode(answer))
|
fmt.Println(signal.Encode(*peerConnection.LocalDescription()))
|
||||||
|
|
||||||
|
// Block forever
|
||||||
select {}
|
select {}
|
||||||
}
|
}
|
||||||
|
17
go.mod
17
go.mod
@@ -3,18 +3,13 @@ module github.com/pion/mediadevices
|
|||||||
go 1.13
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/BurntSushi/xgb v0.0.0-20201008132610-5f9e7b3c49cd // indirect
|
|
||||||
github.com/blackjack/webcam v0.0.0-20200313125108-10ed912a8539
|
github.com/blackjack/webcam v0.0.0-20200313125108-10ed912a8539
|
||||||
github.com/gen2brain/malgo v0.10.27
|
github.com/gen2brain/malgo v0.10.35
|
||||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
|
github.com/google/uuid v1.3.0
|
||||||
github.com/google/uuid v1.1.2
|
github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329
|
||||||
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
|
|
||||||
github.com/lxn/win v0.0.0-20201111105847-2a20daff6a55 // indirect
|
|
||||||
github.com/pion/logging v0.2.2
|
github.com/pion/logging v0.2.2
|
||||||
github.com/pion/rtp v1.6.2
|
github.com/pion/rtp v1.7.4
|
||||||
github.com/pion/webrtc/v3 v3.0.0
|
github.com/pion/webrtc/v3 v3.1.10
|
||||||
github.com/satori/go.uuid v1.2.0
|
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d
|
||||||
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5
|
|
||||||
golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 // indirect
|
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||||
)
|
)
|
||||||
|
143
go.sum
143
go.sum
@@ -1,5 +1,3 @@
|
|||||||
github.com/BurntSushi/xgb v0.0.0-20201008132610-5f9e7b3c49cd h1:u7K2oMFMd8APDV3fM1j2rO3U/XJf1g1qC3DDTKou8iM=
|
|
||||||
github.com/BurntSushi/xgb v0.0.0-20201008132610-5f9e7b3c49cd/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
|
||||||
github.com/blackjack/webcam v0.0.0-20200313125108-10ed912a8539 h1:1aIqYfg9s9RETAJHGfVKZW4ok0b22p4QTwk8MsdRtPs=
|
github.com/blackjack/webcam v0.0.0-20200313125108-10ed912a8539 h1:1aIqYfg9s9RETAJHGfVKZW4ok0b22p4QTwk8MsdRtPs=
|
||||||
github.com/blackjack/webcam v0.0.0-20200313125108-10ed912a8539/go.mod h1:G0X+rEqYPWSq0dG8OMf8M446MtKytzpPjgS3HbdOJZ4=
|
github.com/blackjack/webcam v0.0.0-20200313125108-10ed912a8539/go.mod h1:G0X+rEqYPWSq0dG8OMf8M446MtKytzpPjgS3HbdOJZ4=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -7,10 +5,11 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/gen2brain/malgo v0.10.27 h1:KlNitZIO8V4W2VnjtTM8AGMy/XBb2pN+fnIB5bEps8E=
|
github.com/gen2brain/malgo v0.10.35 h1:D6aNo/Q0SnzQLHomTydTXxj4AJFdGJcVoE7I8JxPoUo=
|
||||||
github.com/gen2brain/malgo v0.10.27/go.mod h1:zHSUNZAXfCeNsZou0RtQ6Zk7gDYLIcKOrUWtAdksnEs=
|
github.com/gen2brain/malgo v0.10.35/go.mod h1:zHSUNZAXfCeNsZou0RtQ6Zk7gDYLIcKOrUWtAdksnEs=
|
||||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 h1:Y5Q2mEwfzjMt5+3u70Gtw93ZOu2UuPeeeTBDntF7FoY=
|
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 h1:Y5Q2mEwfzjMt5+3u70Gtw93ZOu2UuPeeeTBDntF7FoY=
|
||||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo=
|
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo=
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
@@ -18,98 +17,110 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
|
|||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f h1:5hWo+DzJQSOBl6X+TDac0SPWffRonuRJ2///OYtYRT8=
|
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240 h1:dy+DS31tGEGCsZzB45HmJJNHjur8GDgtRNX9U7HnSX4=
|
||||||
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f/go.mod h1:f8GY5V3lRzakvEyr49P7hHRYoHtPr8zvj/7JodCoRzw=
|
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240/go.mod h1:3P4UH/k22rXyHIJD2w4h2XMqPX4Of/eySEZq9L6wqc4=
|
||||||
|
github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329 h1:qq2nCpSrXrmvDGRxW0ruW9BVEV1CN2a9YDOExdt+U0o=
|
||||||
|
github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329/go.mod h1:2VPVQDR4wO7KXHwP+DAypEy67rXf+okUx2zjgpCxZw4=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/lxn/win v0.0.0-20201111105847-2a20daff6a55 h1:4BxFx5XCtXc+nFtXDGDW+Uu5sPtsAbvPh6RObj3fG9o=
|
github.com/lxn/win v0.0.0-20210218163916-a377121e959e h1:H+t6A/QJMbhCSEH5rAuRxh+CtW96g0Or0Fxa9IKr4uc=
|
||||||
github.com/lxn/win v0.0.0-20201111105847-2a20daff6a55/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
|
github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
github.com/onsi/ginkgo v1.16.1/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg=
|
||||||
github.com/pion/datachannel v1.4.21 h1:3ZvhNyfmxsAqltQrApLPQMhSFNA+aT87RqyCq4OXmf0=
|
github.com/pion/datachannel v1.5.2 h1:piB93s8LGmbECrpO84DnkIVWasRMk3IimbcXkTQLE6E=
|
||||||
github.com/pion/datachannel v1.4.21/go.mod h1:oiNyP4gHx2DIwRzX/MFyH0Rz/Gz05OgBlayAI2hAWjg=
|
github.com/pion/datachannel v1.5.2/go.mod h1:FTGQWaHrdCwIJ1rw6xBIfZVkslikjShim5yr05XFuCQ=
|
||||||
github.com/pion/dtls/v2 v2.0.4 h1:WuUcqi6oYMu/noNTz92QrF1DaFj4eXbhQ6dzaaAwOiI=
|
github.com/pion/dtls/v2 v2.0.9/go.mod h1:O0Wr7si/Zj5/EBFlDzDd6UtVxx25CE1r7XM7BQKYQho=
|
||||||
github.com/pion/dtls/v2 v2.0.4/go.mod h1:qAkFscX0ZHoI1E07RfYPoRw3manThveu+mlTDdOxoGI=
|
github.com/pion/dtls/v2 v2.0.10 h1:wgys7gPR1NMbWjmjJ3CW7lkUGaun8djgH8nahpNLnxI=
|
||||||
github.com/pion/ice/v2 v2.0.14 h1:FxXxauyykf89SWAtkQCfnHkno6G8+bhRkNguSh9zU+4=
|
github.com/pion/dtls/v2 v2.0.10/go.mod h1:00OxfeCRWHShcqT9jx8pKKmBWuTt0NCZoVPCaC4VKvU=
|
||||||
github.com/pion/ice/v2 v2.0.14/go.mod h1:wqaUbOq5ObDNU5ox1hRsEst0rWfsKuH1zXjQFEWiZwM=
|
github.com/pion/ice/v2 v2.1.14 h1:nD9GZs3MiR1/dPa5EiMRMe8hLBG3/qqCdx/hTS2g8VE=
|
||||||
github.com/pion/interceptor v0.0.8 h1:qsVJv9RF7mPq/RUnUV5iZCzxwGizO880FuiFKkEGQaE=
|
github.com/pion/ice/v2 v2.1.14/go.mod h1:ovgYHUmwYLlRvcCLI67PnQ5YGe+upXZbGgllBDG/ktU=
|
||||||
github.com/pion/interceptor v0.0.8/go.mod h1:dHgEP5dtxOTf21MObuBAjJeAayPxLUAZjerGH8Xr07c=
|
github.com/pion/interceptor v0.1.0 h1:SlXKaDlEvSl7cr4j8fJykzVz4UdH+7UDtcvx+u01wLU=
|
||||||
|
github.com/pion/interceptor v0.1.0/go.mod h1:j5NIl3tJJPB3u8+Z2Xz8MZs/VV6rc+If9mXEKNuFmEM=
|
||||||
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||||
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||||
github.com/pion/mdns v0.0.4 h1:O4vvVqr4DGX63vzmO6Fw9vpy3lfztVWHGCQfyw0ZLSY=
|
github.com/pion/mdns v0.0.5 h1:Q2oj/JB3NqfzY9xGZ1fPzZzK7sDSD8rZPOvcIQ10BCw=
|
||||||
github.com/pion/mdns v0.0.4/go.mod h1:R1sL0p50l42S5lJs91oNdUL58nm0QHrhxnSegr++qC0=
|
github.com/pion/mdns v0.0.5/go.mod h1:UgssrvdD3mxpi8tMxAXbsppL3vJ4Jipw1mTCW+al01g=
|
||||||
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||||
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||||
github.com/pion/rtcp v1.2.4/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0=
|
|
||||||
github.com/pion/rtcp v1.2.6 h1:1zvwBbyd0TeEuuWftrd/4d++m+/kZSeiguxU61LFWpo=
|
|
||||||
github.com/pion/rtcp v1.2.6/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0=
|
github.com/pion/rtcp v1.2.6/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0=
|
||||||
github.com/pion/rtp v1.6.1/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
|
github.com/pion/rtcp v1.2.8/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo=
|
||||||
github.com/pion/rtp v1.6.2 h1:iGBerLX6JiDjB9NXuaPzHyxHFG9JsIEdgwTC0lp5n/U=
|
github.com/pion/rtcp v1.2.9 h1:1ujStwg++IOLIEoOiIQ2s+qBuJ1VN81KW+9pMPsif+U=
|
||||||
github.com/pion/rtp v1.6.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
|
github.com/pion/rtcp v1.2.9/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo=
|
||||||
github.com/pion/sctp v1.7.10/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
|
github.com/pion/rtp v1.7.0/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
|
||||||
github.com/pion/sctp v1.7.11 h1:UCnj7MsobLKLuP/Hh+JMiI/6W5Bs/VF45lWKgHFjSIE=
|
github.com/pion/rtp v1.7.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
|
||||||
github.com/pion/sctp v1.7.11/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
|
github.com/pion/rtp v1.7.4 h1:4dMbjb1SuynU5OpA3kz1zHK+u+eOCQjW3MAeVHf1ODA=
|
||||||
github.com/pion/sdp/v3 v3.0.3 h1:gJK9hk+JFD2NGIM1nXmqNCq1DkVaIZ9dlA3u3otnkaw=
|
github.com/pion/rtp v1.7.4/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
|
||||||
github.com/pion/sdp/v3 v3.0.3/go.mod h1:bNiSknmJE0HYBprTHXKPQ3+JjacTv5uap92ueJZKsRk=
|
github.com/pion/sctp v1.8.0 h1:6erMF2qmQwXr+0iB1lm0AUSmDr9LdmpaBzgSVAEgehw=
|
||||||
github.com/pion/srtp/v2 v2.0.0-rc.3 h1:1fPiK1nJlNyh235tSGgBnXrPc99wK1/D707f6ntb3qY=
|
github.com/pion/sctp v1.8.0/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s=
|
||||||
github.com/pion/srtp/v2 v2.0.0-rc.3/go.mod h1:S6J9oY6ahAXdU3ni4nUwhWTJuBfssFjPxoB0u41TBpY=
|
github.com/pion/sdp/v3 v3.0.4 h1:2Kf+dgrzJflNCSw3TV5v2VLeI0s/qkzy2r5jlR0wzf8=
|
||||||
|
github.com/pion/sdp/v3 v3.0.4/go.mod h1:bNiSknmJE0HYBprTHXKPQ3+JjacTv5uap92ueJZKsRk=
|
||||||
|
github.com/pion/srtp/v2 v2.0.5 h1:ks3wcTvIUE/GHndO3FAvROQ9opy0uLELpwHJaQ1yqhQ=
|
||||||
|
github.com/pion/srtp/v2 v2.0.5/go.mod h1:8k6AJlal740mrZ6WYxc4Dg6qDqqhxoRG2GSjlUhDF0A=
|
||||||
github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg=
|
github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg=
|
||||||
github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA=
|
github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA=
|
||||||
github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8=
|
|
||||||
github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE=
|
|
||||||
github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A=
|
github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A=
|
||||||
github.com/pion/transport v0.12.0 h1:UFmOBBZkTZ3LgvLRf/NGrfWdZEubcU6zkLU3PsA9YvU=
|
github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
|
||||||
github.com/pion/transport v0.12.0/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
|
github.com/pion/transport v0.12.3 h1:vdBfvfU/0Wq8kd2yhUMSDB/x+O4Z9MYVl2fJ5BT4JZw=
|
||||||
|
github.com/pion/transport v0.12.3/go.mod h1:OViWW9SP2peE/HbwBvARicmAVnesphkNkCVZIWJ6q9A=
|
||||||
github.com/pion/turn/v2 v2.0.5 h1:iwMHqDfPEDEOFzwWKT56eFmh6DYC6o/+xnLAEzgISbA=
|
github.com/pion/turn/v2 v2.0.5 h1:iwMHqDfPEDEOFzwWKT56eFmh6DYC6o/+xnLAEzgISbA=
|
||||||
github.com/pion/turn/v2 v2.0.5/go.mod h1:APg43CFyt/14Uy7heYUOGWdkem/Wu4PhCO/bjyrTqMw=
|
github.com/pion/turn/v2 v2.0.5/go.mod h1:APg43CFyt/14Uy7heYUOGWdkem/Wu4PhCO/bjyrTqMw=
|
||||||
github.com/pion/udp v0.1.0 h1:uGxQsNyrqG3GLINv36Ff60covYmfrLoxzwnCsIYspXI=
|
github.com/pion/udp v0.1.1 h1:8UAPvyqmsxK8oOjloDk4wUt63TzFe9WEJkg5lChlj7o=
|
||||||
github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths=
|
github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M=
|
||||||
github.com/pion/webrtc/v3 v3.0.0 h1:/eTiY3NbfpKj5op8cqtCZlpTv9/yumd17YRinDNOUX0=
|
github.com/pion/webrtc/v3 v3.1.10 h1:DO99F6/X1HrQho3LxTWHPjI3c388btBf56lR5UNRNNk=
|
||||||
github.com/pion/webrtc/v3 v3.0.0/go.mod h1:/xwKHOAk1Y8dspJcxMwuTtxpi8t/Gzks37iB3W6hNuM=
|
github.com/pion/webrtc/v3 v3.1.10/go.mod h1:eL2HHZOvX+W+Q+lenuidTrWfMD9gG3aobtGcCqJ5G48=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
|
||||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
|
||||||
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs=
|
||||||
|
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7 h1:3uJsdck53FDIpWwLeAXlia9p4C8j0BO2xZrqzKpL0D8=
|
|
||||||
golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
|
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20211005001312-d4b1ae081e3b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20211020060615-d418f374d309 h1:A0lJIi+hcTR6aajJH4YqKWwohY4aW9RO7oRMcdv+HKI=
|
||||||
|
golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -117,15 +128,24 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 h1:HlFl4V6pEMziuLXyRkm5BIYq1y1GAbb02pRlWvI54OM=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik=
|
||||||
|
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -144,5 +164,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
|
|||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
@@ -45,6 +45,7 @@ typedef enum AVBindMediaType {
|
|||||||
typedef enum AVBindFrameFormat {
|
typedef enum AVBindFrameFormat {
|
||||||
AVBindFrameFormatI420,
|
AVBindFrameFormatI420,
|
||||||
AVBindFrameFormatNV21,
|
AVBindFrameFormatNV21,
|
||||||
|
AVBindFrameFormatNV12,
|
||||||
AVBindFrameFormatYUY2,
|
AVBindFrameFormatYUY2,
|
||||||
AVBindFrameFormatUYVY,
|
AVBindFrameFormatUYVY,
|
||||||
} AVBindFrameFormat;
|
} AVBindFrameFormat;
|
||||||
|
@@ -89,15 +89,9 @@ didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t heightY = CVPixelBufferGetHeightOfPlane(imageBuffer, 0);
|
|
||||||
size_t bytesPerRowY = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0);
|
|
||||||
|
|
||||||
size_t heightUV = CVPixelBufferGetHeightOfPlane(imageBuffer, 1);
|
|
||||||
size_t bytesPerRowUV = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 1);
|
|
||||||
|
|
||||||
int len = (int)((heightY * bytesPerRowY) + (2 * heightUV * bytesPerRowUV));
|
|
||||||
void *buf = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);
|
void *buf = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);
|
||||||
_mCallback(_mPUserData, buf, len);
|
size_t dataSize = CVPixelBufferGetDataSize(imageBuffer);
|
||||||
|
_mCallback(_mPUserData, buf, (int)dataSize);
|
||||||
|
|
||||||
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
|
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
|
||||||
CVBufferRelease(imageBuffer);
|
CVBufferRelease(imageBuffer);
|
||||||
@@ -133,13 +127,21 @@ didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
|
|||||||
|
|
||||||
STATUS frameFormatToFourCC(AVBindFrameFormat format, FourCharCode *pFourCC) {
|
STATUS frameFormatToFourCC(AVBindFrameFormat format, FourCharCode *pFourCC) {
|
||||||
STATUS retStatus = STATUS_OK;
|
STATUS retStatus = STATUS_OK;
|
||||||
|
// Useful mapping reference from ffmpeg:
|
||||||
|
// https://github.com/FFmpeg/FFmpeg/blob/c810a9502cebe32e1dd08ee3d0d17053dde44aa9/libavdevice/avfoundation.m#L53-L80
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case AVBindFrameFormatNV21:
|
case AVBindFrameFormatI420:
|
||||||
*pFourCC = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange;
|
*pFourCC = kCVPixelFormatType_420YpCbCr8Planar;
|
||||||
|
break;
|
||||||
|
case AVBindFrameFormatNV12:
|
||||||
|
*pFourCC = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
|
||||||
break;
|
break;
|
||||||
case AVBindFrameFormatUYVY:
|
case AVBindFrameFormatUYVY:
|
||||||
*pFourCC = kCVPixelFormatType_422YpCbCr8;
|
*pFourCC = kCVPixelFormatType_422YpCbCr8;
|
||||||
break;
|
break;
|
||||||
|
case AVBindFrameFormatYUY2:
|
||||||
|
*pFourCC = kCVPixelFormatType_422YpCbCr8_yuvs;
|
||||||
|
break;
|
||||||
// TODO: Add the rest of frame formats
|
// TODO: Add the rest of frame formats
|
||||||
default:
|
default:
|
||||||
retStatus = STATUS_UNSUPPORTED_FRAME_FORMAT;
|
retStatus = STATUS_UNSUPPORTED_FRAME_FORMAT;
|
||||||
@@ -150,12 +152,19 @@ STATUS frameFormatToFourCC(AVBindFrameFormat format, FourCharCode *pFourCC) {
|
|||||||
STATUS frameFormatFromFourCC(FourCharCode fourCC, AVBindFrameFormat *pFormat) {
|
STATUS frameFormatFromFourCC(FourCharCode fourCC, AVBindFrameFormat *pFormat) {
|
||||||
STATUS retStatus = STATUS_OK;
|
STATUS retStatus = STATUS_OK;
|
||||||
switch (fourCC) {
|
switch (fourCC) {
|
||||||
|
case kCVPixelFormatType_420YpCbCr8Planar:
|
||||||
|
*pFormat = AVBindFrameFormatI420;
|
||||||
|
break;
|
||||||
case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
|
case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
|
||||||
*pFormat = AVBindFrameFormatNV21;
|
case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
|
||||||
|
*pFormat = AVBindFrameFormatNV12;
|
||||||
break;
|
break;
|
||||||
case kCVPixelFormatType_422YpCbCr8:
|
case kCVPixelFormatType_422YpCbCr8:
|
||||||
*pFormat = AVBindFrameFormatUYVY;
|
*pFormat = AVBindFrameFormatUYVY;
|
||||||
break;
|
break;
|
||||||
|
case kCVPixelFormatType_422YpCbCr8_yuvs:
|
||||||
|
*pFormat = AVBindFrameFormatYUY2;
|
||||||
|
break;
|
||||||
// TODO: Add the rest of frame formats
|
// TODO: Add the rest of frame formats
|
||||||
default:
|
default:
|
||||||
retStatus = STATUS_UNSUPPORTED_FRAME_FORMAT;
|
retStatus = STATUS_UNSUPPORTED_FRAME_FORMAT;
|
||||||
@@ -302,6 +311,16 @@ cleanup:
|
|||||||
return retStatus;
|
return retStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static NSString* FourCCString(FourCharCode code) {
|
||||||
|
NSString *result = [NSString stringWithFormat:@"%c%c%c%c",
|
||||||
|
(code >> 24) & 0xff,
|
||||||
|
(code >> 16) & 0xff,
|
||||||
|
(code >> 8) & 0xff,
|
||||||
|
code & 0xff];
|
||||||
|
NSCharacterSet *characterSet = [NSCharacterSet whitespaceCharacterSet];
|
||||||
|
return [result stringByTrimmingCharactersInSet:characterSet];
|
||||||
|
}
|
||||||
|
|
||||||
STATUS AVBindSessionProperties(PAVBindSession pSession, PAVBindMediaProperty *ppProperties, int *pLen) {
|
STATUS AVBindSessionProperties(PAVBindSession pSession, PAVBindMediaProperty *ppProperties, int *pLen) {
|
||||||
STATUS retStatus = STATUS_OK;
|
STATUS retStatus = STATUS_OK;
|
||||||
NSAutoreleasePool *refPool = [[NSAutoreleasePool alloc] init];
|
NSAutoreleasePool *refPool = [[NSAutoreleasePool alloc] init];
|
||||||
@@ -319,12 +338,14 @@ STATUS AVBindSessionProperties(PAVBindSession pSession, PAVBindMediaProperty *pp
|
|||||||
for (AVCaptureDeviceFormat *refFormat in refDevice.formats) {
|
for (AVCaptureDeviceFormat *refFormat in refDevice.formats) {
|
||||||
// TODO: Probably gives a warn to the user
|
// TODO: Probably gives a warn to the user
|
||||||
if (len >= MAX_PROPERTIES) {
|
if (len >= MAX_PROPERTIES) {
|
||||||
|
NSLog(@"[WARNING] skipping the rest of properties due to MAX_PROPERTIES");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([refFormat.mediaType isEqual:AVMediaTypeVideo]) {
|
if ([refFormat.mediaType isEqual:AVMediaTypeVideo]) {
|
||||||
fourCC = CMFormatDescriptionGetMediaSubType(refFormat.formatDescription);
|
fourCC = CMFormatDescriptionGetMediaSubType(refFormat.formatDescription);
|
||||||
if (frameFormatFromFourCC(fourCC, &pProperty->frameFormat) != STATUS_OK) {
|
if (frameFormatFromFourCC(fourCC, &pProperty->frameFormat) != STATUS_OK) {
|
||||||
|
NSLog(@"[WARNING] skipping %@ %dx%d since it's not supported", FourCCString(fourCC), videoDimensions.width, videoDimensions.height);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
package avfoundation
|
package avfoundation
|
||||||
|
|
||||||
// extern void onData(void*, void*, int);
|
|
||||||
import "C"
|
import "C"
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
@@ -18,11 +17,10 @@ type handleID int
|
|||||||
|
|
||||||
//export onData
|
//export onData
|
||||||
func onData(userData unsafe.Pointer, buf unsafe.Pointer, length C.int) {
|
func onData(userData unsafe.Pointer, buf unsafe.Pointer, length C.int) {
|
||||||
data := C.GoBytes(buf, length)
|
|
||||||
|
|
||||||
handleNum := (*C.int)(userData)
|
handleNum := (*C.int)(userData)
|
||||||
cb, ok := lookup(handleID(*handleNum))
|
cb, ok := lookup(handleID(*handleNum))
|
||||||
if ok {
|
if ok {
|
||||||
|
data := C.GoBytes(buf, length)
|
||||||
cb(data)
|
cb(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -40,6 +40,8 @@ func frameFormatToAVBind(f frame.Format) (C.AVBindFrameFormat, bool) {
|
|||||||
return C.AVBindFrameFormatI420, true
|
return C.AVBindFrameFormatI420, true
|
||||||
case frame.FormatNV21:
|
case frame.FormatNV21:
|
||||||
return C.AVBindFrameFormatNV21, true
|
return C.AVBindFrameFormatNV21, true
|
||||||
|
case frame.FormatNV12:
|
||||||
|
return C.AVBindFrameFormatNV12, true
|
||||||
case frame.FormatYUY2:
|
case frame.FormatYUY2:
|
||||||
return C.AVBindFrameFormatYUY2, true
|
return C.AVBindFrameFormatYUY2, true
|
||||||
case frame.FormatUYVY:
|
case frame.FormatUYVY:
|
||||||
@@ -55,6 +57,8 @@ func frameFormatFromAVBind(f C.AVBindFrameFormat) (frame.Format, bool) {
|
|||||||
return frame.FormatI420, true
|
return frame.FormatI420, true
|
||||||
case C.AVBindFrameFormatNV21:
|
case C.AVBindFrameFormatNV21:
|
||||||
return frame.FormatNV21, true
|
return frame.FormatNV21, true
|
||||||
|
case C.AVBindFrameFormatNV12:
|
||||||
|
return frame.FormatNV12, true
|
||||||
case C.AVBindFrameFormatYUY2:
|
case C.AVBindFrameFormatYUY2:
|
||||||
return frame.FormatYUY2, true
|
return frame.FormatYUY2, true
|
||||||
case C.AVBindFrameFormatUYVY:
|
case C.AVBindFrameFormatUYVY:
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
package codec
|
package codec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"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/prop"
|
"github.com/pion/mediadevices/pkg/prop"
|
||||||
@@ -13,6 +15,9 @@ import (
|
|||||||
type RTPCodec struct {
|
type RTPCodec struct {
|
||||||
webrtc.RTPCodecParameters
|
webrtc.RTPCodecParameters
|
||||||
rtp.Payloader
|
rtp.Payloader
|
||||||
|
|
||||||
|
// Latency of static frame size codec.
|
||||||
|
Latency time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRTPH264Codec is a helper to create an H264 codec
|
// NewRTPH264Codec is a helper to create an H264 codec
|
||||||
|
121
pkg/codec/internal/codectest/codectest.go
Normal file
121
pkg/codec/internal/codectest/codectest.go
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
// Package codectest provides shared test for codec implementations.
|
||||||
|
package codectest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"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/mediadevices/pkg/wave"
|
||||||
|
)
|
||||||
|
|
||||||
|
func assertNoPanic(t *testing.T, fn func() error, msg string) error {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("panic: %v: %s", r, msg)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
func AudioEncoderSimpleReadTest(t *testing.T, c codec.AudioEncoderBuilder, p prop.Media, w wave.Audio) {
|
||||||
|
var eof bool
|
||||||
|
enc, err := c.BuildAudioEncoder(audio.ReaderFunc(func() (wave.Audio, func(), error) {
|
||||||
|
if eof {
|
||||||
|
return nil, nil, io.EOF
|
||||||
|
}
|
||||||
|
return w, nil, nil
|
||||||
|
}), p)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
b, release, err := enc.Read()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(b) == 0 {
|
||||||
|
t.Fatal("Encoded frame is empty")
|
||||||
|
}
|
||||||
|
release()
|
||||||
|
}
|
||||||
|
|
||||||
|
eof = true
|
||||||
|
if _, _, err := enc.Read(); err != io.EOF {
|
||||||
|
t.Fatalf("Expected EOF, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := enc.Close(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func VideoEncoderSimpleReadTest(t *testing.T, c codec.VideoEncoderBuilder, p prop.Media, img image.Image) {
|
||||||
|
var eof bool
|
||||||
|
enc, err := c.BuildVideoEncoder(video.ReaderFunc(func() (image.Image, func(), error) {
|
||||||
|
if eof {
|
||||||
|
return nil, nil, io.EOF
|
||||||
|
}
|
||||||
|
return img, nil, nil
|
||||||
|
}), p)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
b, release, err := enc.Read()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(b) == 0 {
|
||||||
|
t.Errorf("Encoded frame is empty (%d)", i)
|
||||||
|
}
|
||||||
|
release()
|
||||||
|
}
|
||||||
|
|
||||||
|
eof = true
|
||||||
|
if _, _, err := enc.Read(); err != io.EOF {
|
||||||
|
t.Fatalf("Expected EOF, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := enc.Close(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AudioEncoderCloseTwiceTest(t *testing.T, c codec.AudioEncoderBuilder, p prop.Media) {
|
||||||
|
enc, err := c.BuildAudioEncoder(audio.ReaderFunc(func() (wave.Audio, func(), error) {
|
||||||
|
return nil, nil, io.EOF
|
||||||
|
}), p)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := assertNoPanic(t, enc.Close, "on first Close()"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := assertNoPanic(t, enc.Close, "on second Close()"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func VideoEncoderCloseTwiceTest(t *testing.T, c codec.VideoEncoderBuilder, p prop.Media) {
|
||||||
|
enc, err := c.BuildVideoEncoder(video.ReaderFunc(func() (image.Image, func(), error) {
|
||||||
|
return nil, nil, io.EOF
|
||||||
|
}), p)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := assertNoPanic(t, enc.Close, "on first Close()"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := assertNoPanic(t, enc.Close, "on second Close()"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
46
pkg/codec/mmal/mmal_test.go
Normal file
46
pkg/codec/mmal/mmal_test.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package mmal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pion/mediadevices/pkg/codec/internal/codectest"
|
||||||
|
"github.com/pion/mediadevices/pkg/frame"
|
||||||
|
"github.com/pion/mediadevices/pkg/prop"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEncoder(t *testing.T) {
|
||||||
|
t.Run("SimpleRead", func(t *testing.T) {
|
||||||
|
p, err := NewParams()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
codectest.VideoEncoderSimpleReadTest(t, &p,
|
||||||
|
prop.Media{
|
||||||
|
Video: prop.Video{
|
||||||
|
Width: 256,
|
||||||
|
Height: 144,
|
||||||
|
FrameFormat: frame.FormatI420,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
image.NewYCbCr(
|
||||||
|
image.Rect(0, 0, 256, 144),
|
||||||
|
image.YCbCrSubsampleRatio420,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
t.Run("CloseTwice", func(t *testing.T) {
|
||||||
|
p, err := NewParams()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
codectest.VideoEncoderCloseTwiceTest(t, &p, prop.Media{
|
||||||
|
Video: prop.Video{
|
||||||
|
Width: 640,
|
||||||
|
Height: 480,
|
||||||
|
FrameRate: 30,
|
||||||
|
FrameFormat: frame.FormatI420,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
47
pkg/codec/openh264/Makefile
Normal file
47
pkg/codec/openh264/Makefile
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
git_url := https://github.com/cisco/openh264.git
|
||||||
|
version := v2.1.1
|
||||||
|
src_root_dir := src
|
||||||
|
lib_dir := lib
|
||||||
|
include_dir := include/openh264
|
||||||
|
lib_prefix := libopenh264
|
||||||
|
src_dir := $(src_root_dir)/$(MEDIADEVICES_TARGET_PLATFORM)
|
||||||
|
output_path := $(lib_dir)/$(lib_prefix)-$(MEDIADEVICES_TARGET_PLATFORM).a
|
||||||
|
|
||||||
|
# OS and Arch mapping to OpenH264 parameters
|
||||||
|
ifeq (windows,$(MEDIADEVICES_TARGET_OS))
|
||||||
|
os := mingw_nt
|
||||||
|
else
|
||||||
|
os := $(MEDIADEVICES_TARGET_OS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(findstring $(MEDIADEVICES_TARGET_ARCH),armv6 armv7 armv8))
|
||||||
|
arch := arm
|
||||||
|
else ifeq (x64,$(MEDIADEVICES_TARGET_ARCH))
|
||||||
|
arch := x86_64
|
||||||
|
else
|
||||||
|
arch := $(MEDIADEVICES_TARGET_ARCH)
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: guard-MEDIADEVICES_TARGET_PLATFORM guard-MEDIADEVICES_TARGET_PLATFORM \
|
||||||
|
guard-MEDIADEVICES_TARGET_OS guard-MEDIADEVICES_TARGET_ARCH \
|
||||||
|
$(output_path) headers
|
||||||
|
|
||||||
|
headers: | $(src_dir) $(include_dir)
|
||||||
|
@cp $(src_dir)/codec/api/svc/*.h $(include_dir)
|
||||||
|
|
||||||
|
$(output_path): $(src_dir)/$(lib_prefix).a | $(lib_dir)
|
||||||
|
@cp $< $@
|
||||||
|
|
||||||
|
$(src_dir)/$(lib_prefix).a: | $(src_dir)
|
||||||
|
$(MEDIADEVICES_TOOLCHAIN_BIN) make --directory=$(src_dir) $(lib_prefix).a \
|
||||||
|
OS=$(os) ARCH=$(arch)
|
||||||
|
|
||||||
|
$(src_dir): | $(src_root_dir)
|
||||||
|
git clone --depth=1 --branch=$(version) $(git_url) $@
|
||||||
|
|
||||||
|
$(src_root_dir) $(lib_dir) $(include_dir):
|
||||||
|
@mkdir -p $@
|
||||||
|
|
||||||
|
guard-%:
|
||||||
|
@if [ -z ${$*} ]; then echo "$* is a required environment variable"; exit 1; fi
|
@@ -80,6 +80,11 @@ Slice enc_encode(Encoder *e, Frame f, int *eresult) {
|
|||||||
SFrameBSInfo info = {0};
|
SFrameBSInfo info = {0};
|
||||||
Slice payload = {0};
|
Slice payload = {0};
|
||||||
|
|
||||||
|
if(e->force_key_frame == 1) {
|
||||||
|
info.eFrameType = videoFrameTypeI;
|
||||||
|
e->force_key_frame = 0;
|
||||||
|
}
|
||||||
|
|
||||||
pic.iPicWidth = f.width;
|
pic.iPicWidth = f.width;
|
||||||
pic.iPicHeight = f.height;
|
pic.iPicHeight = f.height;
|
||||||
pic.iColorFormat = videoFormatI420;
|
pic.iColorFormat = videoFormatI420;
|
||||||
|
@@ -27,6 +27,7 @@ typedef struct Encoder {
|
|||||||
ISVCEncoder *engine;
|
ISVCEncoder *engine;
|
||||||
unsigned char *buff;
|
unsigned char *buff;
|
||||||
int buff_size;
|
int buff_size;
|
||||||
|
int force_key_frame;
|
||||||
} Encoder;
|
} Encoder;
|
||||||
|
|
||||||
Encoder *enc_new(const EncoderOptions params, int *eresult);
|
Encoder *enc_new(const EncoderOptions params, int *eresult);
|
||||||
|
@@ -1,36 +0,0 @@
|
|||||||
GIT_URL=https://github.com/cisco/openh264.git
|
|
||||||
VERSION=v2.1.1
|
|
||||||
SRC_DIR=src
|
|
||||||
LIB_DIR=lib
|
|
||||||
INCLUDE_DIR=include/openh264
|
|
||||||
ROOT_DIR=${PWD}
|
|
||||||
LIB_PREFIX=libopenh264
|
|
||||||
|
|
||||||
OS=${MEDIADEVICES_TARGET_OS}
|
|
||||||
ARCH=${MEDIADEVICES_TARGET_ARCH}
|
|
||||||
|
|
||||||
case ${MEDIADEVICES_TARGET_OS} in
|
|
||||||
windows)
|
|
||||||
OS=mingw_nt
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
case ${MEDIADEVICES_TARGET_ARCH} in
|
|
||||||
armv6 | armv7 | armv8)
|
|
||||||
ARCH=arm
|
|
||||||
;;
|
|
||||||
x64)
|
|
||||||
ARCH=x86_64
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
mkdir -p ${LIB_DIR} ${INCLUDE_DIR}
|
|
||||||
|
|
||||||
git clone --depth=1 --branch=${VERSION} ${GIT_URL} ${SRC_DIR}
|
|
||||||
cd ${SRC_DIR}
|
|
||||||
${MEDIADEVICES_TOOLCHAIN_BIN} make -j2 ${LIB_PREFIX}.a OS=${OS} ARCH=${ARCH}
|
|
||||||
${MEDIADEVICES_TOOLCHAIN_BIN} echo $PATH
|
|
||||||
mv ${LIB_PREFIX}.a ${ROOT_DIR}/${LIB_DIR}/${LIB_PREFIX}_${MEDIADEVICES_TARGET_PLATFORM}.a
|
|
||||||
mkdir -p ${ROOT_DIR}/${INCLUDE_DIR}
|
|
||||||
cp codec/api/svc/*.h ${ROOT_DIR}/${INCLUDE_DIR}
|
|
||||||
make clean
|
|
@@ -4,12 +4,12 @@
|
|||||||
|
|
||||||
#include "codec_app_def.h"
|
#include "codec_app_def.h"
|
||||||
|
|
||||||
static const OpenH264Version g_stCodecVersion = {2, 1, 0, 2002};
|
static const OpenH264Version g_stCodecVersion = {2, 1, 1, 2005};
|
||||||
static const char* const g_strCodecVer = "OpenH264 version:2.1.0.2002";
|
static const char* const g_strCodecVer = "OpenH264 version:2.1.1.2005";
|
||||||
|
|
||||||
#define OPENH264_MAJOR (2)
|
#define OPENH264_MAJOR (2)
|
||||||
#define OPENH264_MINOR (1)
|
#define OPENH264_MINOR (1)
|
||||||
#define OPENH264_REVISION (0)
|
#define OPENH264_REVISION (1)
|
||||||
#define OPENH264_RESERVED (2002)
|
#define OPENH264_RESERVED (2005)
|
||||||
|
|
||||||
#endif // CODEC_VER_H
|
#endif // CODEC_VER_H
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -84,13 +84,18 @@ func (e *encoder) SetBitRate(b int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *encoder) ForceKeyFrame() error {
|
func (e *encoder) ForceKeyFrame() error {
|
||||||
panic("ForceKeyFrame is not implemented")
|
e.engine.force_key_frame = C.int(1)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *encoder) Close() error {
|
func (e *encoder) Close() error {
|
||||||
e.mu.Lock()
|
e.mu.Lock()
|
||||||
defer e.mu.Unlock()
|
defer e.mu.Unlock()
|
||||||
|
|
||||||
|
if e.closed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
e.closed = true
|
e.closed = true
|
||||||
|
|
||||||
var rv C.int
|
var rv C.int
|
||||||
|
@@ -4,9 +4,9 @@ package openh264
|
|||||||
|
|
||||||
//#cgo CFLAGS: -I${SRCDIR}/include
|
//#cgo CFLAGS: -I${SRCDIR}/include
|
||||||
//#cgo CXXFLAGS: -I${SRCDIR}/include
|
//#cgo CXXFLAGS: -I${SRCDIR}/include
|
||||||
//#cgo linux,arm LDFLAGS: ${SRCDIR}/lib/libopenh264_linux_armv7.a
|
//#cgo linux,arm LDFLAGS: ${SRCDIR}/lib/libopenh264-linux-armv7.a
|
||||||
//#cgo linux,arm64 LDFLAGS: ${SRCDIR}/lib/libopenh264_linux_arm64.a
|
//#cgo linux,arm64 LDFLAGS: ${SRCDIR}/lib/libopenh264-linux-arm64.a
|
||||||
//#cgo linux,amd64 LDFLAGS: ${SRCDIR}/lib/libopenh264_linux_x64.a
|
//#cgo linux,amd64 LDFLAGS: ${SRCDIR}/lib/libopenh264-linux-x64.a
|
||||||
//#cgo darwin,amd64 LDFLAGS: ${SRCDIR}/lib/libopenh264_darwin_x64.a
|
//#cgo darwin,amd64 LDFLAGS: ${SRCDIR}/lib/libopenh264-darwin-x64.a
|
||||||
//#cgo windows,amd64 LDFLAGS: ${SRCDIR}/lib/libopenh264_windows_x64.a -lssp
|
//#cgo windows,amd64 LDFLAGS: ${SRCDIR}/lib/libopenh264-windows-x64.a -lssp
|
||||||
import "C"
|
import "C"
|
||||||
|
46
pkg/codec/openh264/openh264_test.go
Normal file
46
pkg/codec/openh264/openh264_test.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package openh264
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pion/mediadevices/pkg/codec/internal/codectest"
|
||||||
|
"github.com/pion/mediadevices/pkg/frame"
|
||||||
|
"github.com/pion/mediadevices/pkg/prop"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEncoder(t *testing.T) {
|
||||||
|
t.Run("SimpleRead", func(t *testing.T) {
|
||||||
|
p, err := NewParams()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
codectest.VideoEncoderSimpleReadTest(t, &p,
|
||||||
|
prop.Media{
|
||||||
|
Video: prop.Video{
|
||||||
|
Width: 256,
|
||||||
|
Height: 144,
|
||||||
|
FrameFormat: frame.FormatI420,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
image.NewYCbCr(
|
||||||
|
image.Rect(0, 0, 256, 144),
|
||||||
|
image.YCbCrSubsampleRatio420,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
t.Run("CloseTwice", func(t *testing.T) {
|
||||||
|
p, err := NewParams()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
codectest.VideoEncoderCloseTwiceTest(t, &p, prop.Media{
|
||||||
|
Video: prop.Video{
|
||||||
|
Width: 640,
|
||||||
|
Height: 480,
|
||||||
|
FrameRate: 30,
|
||||||
|
FrameFormat: frame.FormatI420,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
31
pkg/codec/opus/Makefile
Normal file
31
pkg/codec/opus/Makefile
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
git_url := https://github.com/xiph/opus.git
|
||||||
|
version := v1.3.1
|
||||||
|
src_root_dir := src
|
||||||
|
lib_dir := lib
|
||||||
|
include_dir := include
|
||||||
|
lib_prefix := libopus
|
||||||
|
src_dir := $(src_root_dir)/$(MEDIADEVICES_TARGET_PLATFORM)
|
||||||
|
output_path := $(lib_dir)/$(lib_prefix)-$(MEDIADEVICES_TARGET_PLATFORM).a
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: guard-MEDIADEVICES_TARGET_PLATFORM guard-MEDIADEVICES_TOOLCHAIN_BIN $(output_path) headers
|
||||||
|
|
||||||
|
headers: | $(src_dir) $(include_dir)
|
||||||
|
@cp $(src_dir)/include/*.h $(include_dir)
|
||||||
|
|
||||||
|
$(output_path): $(src_dir)/$(lib_prefix).a | $(lib_dir)
|
||||||
|
@cp $< $@
|
||||||
|
|
||||||
|
$(src_dir)/$(lib_prefix).a: | $(src_dir)
|
||||||
|
cd $(src_dir) && \
|
||||||
|
$(MEDIADEVICES_TOOLCHAIN_BIN) cmake -DOPUS_STACK_PROTECTOR=OFF -DCMAKE_C_FLAGS="-fpic" && \
|
||||||
|
$(MEDIADEVICES_TOOLCHAIN_BIN) make VERBOSE=1
|
||||||
|
|
||||||
|
$(src_dir): | $(src_root_dir)
|
||||||
|
git clone --depth=1 --branch=$(version) $(git_url) $@
|
||||||
|
|
||||||
|
$(src_root_dir) $(lib_dir) $(include_dir):
|
||||||
|
@mkdir -p $@
|
||||||
|
|
||||||
|
guard-%:
|
||||||
|
@if [ -z ${$*} ]; then echo "$* is a required environment variable"; exit 1; fi
|
@@ -1,19 +0,0 @@
|
|||||||
GIT_URL=https://github.com/xiph/opus.git
|
|
||||||
VERSION=v1.3.1
|
|
||||||
SRC_DIR=src
|
|
||||||
LIB_DIR=lib
|
|
||||||
INCLUDE_DIR=include
|
|
||||||
ROOT_DIR=${PWD}
|
|
||||||
LIB_PREFIX=libopus
|
|
||||||
|
|
||||||
mkdir -p ${LIB_DIR} ${INCLUDE_DIR}
|
|
||||||
|
|
||||||
git clone --depth=1 --branch=${VERSION} ${GIT_URL} ${SRC_DIR}
|
|
||||||
cd ${SRC_DIR}
|
|
||||||
${MEDIADEVICES_TOOLCHAIN_BIN} cmake -DOPUS_STACK_PROTECTOR=OFF .
|
|
||||||
${MEDIADEVICES_TOOLCHAIN_BIN} make -j2
|
|
||||||
mv ${LIB_PREFIX}.a ${ROOT_DIR}/${LIB_DIR}/${LIB_PREFIX}_${MEDIADEVICES_TARGET_PLATFORM}.a
|
|
||||||
mkdir -p ${ROOT_DIR}/${INCLUDE_DIR}
|
|
||||||
cp include/*.h ${ROOT_DIR}/${INCLUDE_DIR}
|
|
||||||
git clean -dfx
|
|
||||||
git reset --hard
|
|
Binary file not shown.
Binary file not shown.
BIN
pkg/codec/opus/lib/libopus-linux-armv7.a
Normal file
BIN
pkg/codec/opus/lib/libopus-linux-armv7.a
Normal file
Binary file not shown.
BIN
pkg/codec/opus/lib/libopus-windows-x64.a
Normal file
BIN
pkg/codec/opus/lib/libopus-windows-x64.a
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,7 +3,6 @@ package opus
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
|
|
||||||
"github.com/pion/mediadevices/pkg/codec"
|
"github.com/pion/mediadevices/pkg/codec"
|
||||||
"github.com/pion/mediadevices/pkg/io/audio"
|
"github.com/pion/mediadevices/pkg/io/audio"
|
||||||
@@ -28,8 +27,6 @@ type encoder struct {
|
|||||||
engine *C.OpusEncoder
|
engine *C.OpusEncoder
|
||||||
}
|
}
|
||||||
|
|
||||||
var latencies = []float64{5, 10, 20, 40, 60}
|
|
||||||
|
|
||||||
func newEncoder(r audio.Reader, p prop.Media, params Params) (codec.ReadCloser, error) {
|
func newEncoder(r audio.Reader, p prop.Media, params Params) (codec.ReadCloser, error) {
|
||||||
var cerror C.int
|
var cerror C.int
|
||||||
|
|
||||||
@@ -37,10 +34,6 @@ func newEncoder(r audio.Reader, p prop.Media, params Params) (codec.ReadCloser,
|
|||||||
return nil, fmt.Errorf("opus: inProp.SampleRate is required")
|
return nil, fmt.Errorf("opus: inProp.SampleRate is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.Latency == 0 {
|
|
||||||
p.Latency = 20
|
|
||||||
}
|
|
||||||
|
|
||||||
if params.BitRate == 0 {
|
if params.BitRate == 0 {
|
||||||
params.BitRate = 32000
|
params.BitRate = 32000
|
||||||
}
|
}
|
||||||
@@ -49,19 +42,8 @@ func newEncoder(r audio.Reader, p prop.Media, params Params) (codec.ReadCloser,
|
|||||||
params.ChannelMixer = &mixer.MonoMixer{}
|
params.ChannelMixer = &mixer.MonoMixer{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select the nearest supported latency
|
if !params.Latency.Validate() {
|
||||||
var targetLatency float64
|
return nil, fmt.Errorf("opus: unsupported latency %v", params.Latency)
|
||||||
// TODO: use p.Latency.Milliseconds() after Go 1.12 EOL
|
|
||||||
latencyInMS := float64(p.Latency.Nanoseconds() / 1000000)
|
|
||||||
nearestDist := math.Inf(+1)
|
|
||||||
for _, latency := range latencies {
|
|
||||||
dist := math.Abs(latency - latencyInMS)
|
|
||||||
if dist >= nearestDist {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
nearestDist = dist
|
|
||||||
targetLatency = latency
|
|
||||||
}
|
}
|
||||||
|
|
||||||
channels := p.ChannelCount
|
channels := p.ChannelCount
|
||||||
@@ -77,7 +59,7 @@ func newEncoder(r audio.Reader, p prop.Media, params Params) (codec.ReadCloser,
|
|||||||
}
|
}
|
||||||
|
|
||||||
rMix := audio.NewChannelMixer(channels, params.ChannelMixer)
|
rMix := audio.NewChannelMixer(channels, params.ChannelMixer)
|
||||||
rBuf := audio.NewBuffer(int(targetLatency * float64(p.SampleRate) / 1000))
|
rBuf := audio.NewBuffer(params.Latency.samples(p.SampleRate))
|
||||||
e := encoder{
|
e := encoder{
|
||||||
engine: engine,
|
engine: engine,
|
||||||
reader: rMix(rBuf(r)),
|
reader: rMix(rBuf(r)),
|
||||||
@@ -144,6 +126,9 @@ func (e *encoder) ForceKeyFrame() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *encoder) Close() error {
|
func (e *encoder) Close() error {
|
||||||
|
if e.engine == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
C.opus_encoder_destroy(e.engine)
|
C.opus_encoder_destroy(e.engine)
|
||||||
e.engine = nil
|
e.engine = nil
|
||||||
return nil
|
return nil
|
||||||
|
@@ -4,9 +4,9 @@ package opus
|
|||||||
|
|
||||||
//#cgo CFLAGS: -I${SRCDIR}/include
|
//#cgo CFLAGS: -I${SRCDIR}/include
|
||||||
//#cgo CXXFLAGS: -I${SRCDIR}/include
|
//#cgo CXXFLAGS: -I${SRCDIR}/include
|
||||||
//#cgo linux,arm LDFLAGS: ${SRCDIR}/lib/libopus_linux_armv7.a -lm
|
//#cgo linux,arm LDFLAGS: ${SRCDIR}/lib/libopus-linux-armv7.a -lm
|
||||||
//#cgo linux,arm64 LDFLAGS: ${SRCDIR}/lib/libopus_linux_arm64.a -lm
|
//#cgo linux,arm64 LDFLAGS: ${SRCDIR}/lib/libopus-linux-arm64.a -lm
|
||||||
//#cgo linux,amd64 LDFLAGS: ${SRCDIR}/lib/libopus_linux_x64.a -lm
|
//#cgo linux,amd64 LDFLAGS: ${SRCDIR}/lib/libopus-linux-x64.a -lm
|
||||||
//#cgo darwin,amd64 LDFLAGS: ${SRCDIR}/lib/libopus_darwin_x64.a
|
//#cgo darwin,amd64 LDFLAGS: ${SRCDIR}/lib/libopus-darwin-x64.a
|
||||||
//#cgo windows,amd64 LDFLAGS: ${SRCDIR}/lib/libopus_windows_x64.a
|
//#cgo windows,amd64 LDFLAGS: ${SRCDIR}/lib/libopus-windows-x64.a
|
||||||
import "C"
|
import "C"
|
||||||
|
43
pkg/codec/opus/opus_test.go
Normal file
43
pkg/codec/opus/opus_test.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package opus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pion/mediadevices/pkg/codec/internal/codectest"
|
||||||
|
"github.com/pion/mediadevices/pkg/prop"
|
||||||
|
"github.com/pion/mediadevices/pkg/wave"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEncoder(t *testing.T) {
|
||||||
|
t.Run("SimpleRead", func(t *testing.T) {
|
||||||
|
p, err := NewParams()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
codectest.AudioEncoderSimpleReadTest(t, &p,
|
||||||
|
prop.Media{
|
||||||
|
Audio: prop.Audio{
|
||||||
|
SampleRate: 48000,
|
||||||
|
ChannelCount: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wave.NewInt16Interleaved(wave.ChunkInfo{
|
||||||
|
Len: 960,
|
||||||
|
SamplingRate: 48000,
|
||||||
|
Channels: 2,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
t.Run("CloseTwice", func(t *testing.T) {
|
||||||
|
p, err := NewParams()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
codectest.AudioEncoderCloseTwiceTest(t, &p, prop.Media{
|
||||||
|
Audio: prop.Audio{
|
||||||
|
SampleRate: 48000,
|
||||||
|
ChannelCount: 2,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
@@ -1,27 +1,69 @@
|
|||||||
package opus
|
package opus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/pion/mediadevices/pkg/codec"
|
"github.com/pion/mediadevices/pkg/codec"
|
||||||
"github.com/pion/mediadevices/pkg/io/audio"
|
"github.com/pion/mediadevices/pkg/io/audio"
|
||||||
"github.com/pion/mediadevices/pkg/prop"
|
"github.com/pion/mediadevices/pkg/prop"
|
||||||
"github.com/pion/mediadevices/pkg/wave/mixer"
|
"github.com/pion/mediadevices/pkg/wave/mixer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Latency is a type of OPUS codec frame duration.
|
||||||
|
type Latency time.Duration
|
||||||
|
|
||||||
|
// Latency values available in OPUS codec.
|
||||||
|
const (
|
||||||
|
Latency2500us Latency = Latency(2500 * time.Microsecond)
|
||||||
|
Latency5ms Latency = Latency(5 * time.Millisecond)
|
||||||
|
Latency10ms Latency = Latency(10 * time.Millisecond)
|
||||||
|
Latency20ms Latency = Latency(20 * time.Millisecond)
|
||||||
|
Latency40ms Latency = Latency(40 * time.Millisecond)
|
||||||
|
Latency60ms Latency = Latency(60 * time.Millisecond)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Validate that the Latency is allowed in OPUS.
|
||||||
|
func (l Latency) Validate() bool {
|
||||||
|
switch l {
|
||||||
|
case Latency2500us, Latency5ms, Latency10ms, Latency20ms, Latency40ms, Latency60ms:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duration returns latency in time.Duration.
|
||||||
|
func (l Latency) Duration() time.Duration {
|
||||||
|
return time.Duration(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
// samples returns number of samples for given sample rate.
|
||||||
|
func (l Latency) samples(sampleRate int) int {
|
||||||
|
return int(l.Duration() * time.Duration(sampleRate) / time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
// Params stores opus specific encoding parameters.
|
// Params stores opus specific encoding parameters.
|
||||||
type Params struct {
|
type Params struct {
|
||||||
codec.BaseParams
|
codec.BaseParams
|
||||||
// ChannelMixer is a mixer to be used if number of given and expected channels differ.
|
// ChannelMixer is a mixer to be used if number of given and expected channels differ.
|
||||||
ChannelMixer mixer.ChannelMixer
|
ChannelMixer mixer.ChannelMixer
|
||||||
|
|
||||||
|
// Expected latency of the codec.
|
||||||
|
Latency Latency
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewParams returns default opus codec specific parameters.
|
// NewParams returns default opus codec specific parameters.
|
||||||
func NewParams() (Params, error) {
|
func NewParams() (Params, error) {
|
||||||
return Params{}, nil
|
return Params{
|
||||||
|
Latency: Latency20ms,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RTPCodec represents the codec metadata
|
// RTPCodec represents the codec metadata
|
||||||
func (p *Params) RTPCodec() *codec.RTPCodec {
|
func (p *Params) RTPCodec() *codec.RTPCodec {
|
||||||
return codec.NewRTPOpusCodec(48000)
|
c := codec.NewRTPOpusCodec(48000)
|
||||||
|
c.Latency = time.Duration(p.Latency)
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildAudioEncoder builds opus encoder with given params
|
// BuildAudioEncoder builds opus encoder with given params
|
||||||
|
49
pkg/codec/opus/params_test.go
Normal file
49
pkg/codec/opus/params_test.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package opus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLatency_Validate(t *testing.T) {
|
||||||
|
t.Run("Valid", func(t *testing.T) {
|
||||||
|
for _, l := range []Latency{
|
||||||
|
Latency2500us, Latency5ms, Latency10ms, Latency20ms, Latency40ms, Latency60ms,
|
||||||
|
} {
|
||||||
|
if !l.Validate() {
|
||||||
|
t.Errorf("Defined Latency(%v) must be valid", l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("Invalid", func(t *testing.T) {
|
||||||
|
for _, l := range []Latency{
|
||||||
|
0, Latency(time.Second),
|
||||||
|
} {
|
||||||
|
if l.Validate() {
|
||||||
|
t.Errorf("Latency(%v) must be valid", l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLatency_samples(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
latency Latency
|
||||||
|
sampleRate int
|
||||||
|
samples int
|
||||||
|
}{
|
||||||
|
{Latency5ms, 48000, 240},
|
||||||
|
{Latency20ms, 16000, 320},
|
||||||
|
{Latency20ms, 48000, 960},
|
||||||
|
}
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(fmt.Sprintf("%v_%d", time.Duration(testCase.latency), testCase.sampleRate), func(t *testing.T) {
|
||||||
|
samples := testCase.latency.samples(testCase.sampleRate)
|
||||||
|
if samples != testCase.samples {
|
||||||
|
t.Errorf("Expected samples: %d, got: %d", testCase.samples, samples)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||||
// +build dragonfly freebsd linux netbsd openbsd solaris
|
// +build dragonfly freebsd linux netbsd openbsd solaris
|
||||||
|
|
||||||
// Package vaapi implements hardware accelerated codecs.
|
// Package vaapi implements hardware accelerated codecs.
|
||||||
|
70
pkg/codec/vaapi/vaapi_test.go
Normal file
70
pkg/codec/vaapi/vaapi_test.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||||
|
// +build dragonfly freebsd linux netbsd openbsd solaris
|
||||||
|
|
||||||
|
package vaapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"image"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pion/mediadevices/pkg/codec"
|
||||||
|
"github.com/pion/mediadevices/pkg/codec/internal/codectest"
|
||||||
|
"github.com/pion/mediadevices/pkg/frame"
|
||||||
|
"github.com/pion/mediadevices/pkg/prop"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEncoder(t *testing.T) {
|
||||||
|
if _, err := os.Stat("/dev/dri/card0"); errors.Is(err, os.ErrNotExist) {
|
||||||
|
t.Skip("/dev/dri/card0 not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, factory := range map[string]func() (codec.VideoEncoderBuilder, error){
|
||||||
|
"VP8": func() (codec.VideoEncoderBuilder, error) {
|
||||||
|
p, err := NewVP8Params()
|
||||||
|
return &p, err
|
||||||
|
},
|
||||||
|
"VP9": func() (codec.VideoEncoderBuilder, error) {
|
||||||
|
p, err := NewVP9Params()
|
||||||
|
return &p, err
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
factory := factory
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Run("SimpleRead", func(t *testing.T) {
|
||||||
|
p, err := factory()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
codectest.VideoEncoderSimpleReadTest(t, p,
|
||||||
|
prop.Media{
|
||||||
|
Video: prop.Video{
|
||||||
|
Width: 256,
|
||||||
|
Height: 144,
|
||||||
|
FrameFormat: frame.FormatI420,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
image.NewYCbCr(
|
||||||
|
image.Rect(0, 0, 256, 144),
|
||||||
|
image.YCbCrSubsampleRatio420,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
t.Run("CloseTwice", func(t *testing.T) {
|
||||||
|
p, err := factory()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
codectest.VideoEncoderCloseTwiceTest(t, p, prop.Media{
|
||||||
|
Video: prop.Video{
|
||||||
|
Width: 640,
|
||||||
|
Height: 480,
|
||||||
|
FrameRate: 30,
|
||||||
|
FrameFormat: frame.FormatI420,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||||
// +build dragonfly freebsd linux netbsd openbsd solaris
|
// +build dragonfly freebsd linux netbsd openbsd solaris
|
||||||
|
|
||||||
package vaapi
|
package vaapi
|
||||||
@@ -552,6 +553,10 @@ func (e *encoderVP8) Close() error {
|
|||||||
e.mu.Lock()
|
e.mu.Lock()
|
||||||
defer e.mu.Unlock()
|
defer e.mu.Unlock()
|
||||||
|
|
||||||
|
if e.closed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
C.vaDestroySurfaces(e.display, &e.surfs[0], C.int(len(e.surfs)))
|
C.vaDestroySurfaces(e.display, &e.surfs[0], C.int(len(e.surfs)))
|
||||||
C.vaDestroyContext(e.display, e.ctxID)
|
C.vaDestroyContext(e.display, e.ctxID)
|
||||||
C.vaDestroyConfig(e.display, e.confID)
|
C.vaDestroyConfig(e.display, e.confID)
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||||
// +build dragonfly freebsd linux netbsd openbsd solaris
|
// +build dragonfly freebsd linux netbsd openbsd solaris
|
||||||
|
|
||||||
package vaapi
|
package vaapi
|
||||||
@@ -487,6 +488,10 @@ func (e *encoderVP9) Close() error {
|
|||||||
e.mu.Lock()
|
e.mu.Lock()
|
||||||
defer e.mu.Unlock()
|
defer e.mu.Unlock()
|
||||||
|
|
||||||
|
if e.closed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
C.vaDestroySurfaces(e.display, &e.surfs[0], C.int(len(e.surfs)))
|
C.vaDestroySurfaces(e.display, &e.surfs[0], C.int(len(e.surfs)))
|
||||||
C.vaDestroyContext(e.display, e.ctxID)
|
C.vaDestroyContext(e.display, e.ctxID)
|
||||||
C.vaDestroyConfig(e.display, e.confID)
|
C.vaDestroyConfig(e.display, e.confID)
|
||||||
|
@@ -16,6 +16,7 @@ type Params struct {
|
|||||||
RateControlOvershootPercent uint
|
RateControlOvershootPercent uint
|
||||||
RateControlMinQuantizer uint
|
RateControlMinQuantizer uint
|
||||||
RateControlMaxQuantizer uint
|
RateControlMaxQuantizer uint
|
||||||
|
LagInFrames uint
|
||||||
ErrorResilient ErrorResilientMode
|
ErrorResilient ErrorResilientMode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,6 +23,9 @@ package vpx
|
|||||||
// int pktSz(vpx_codec_cx_pkt_t *pkt) {
|
// int pktSz(vpx_codec_cx_pkt_t *pkt) {
|
||||||
// return pkt->data.frame.sz;
|
// return pkt->data.frame.sz;
|
||||||
// }
|
// }
|
||||||
|
// vpx_codec_frame_flags_t pktFrameFlags(vpx_codec_cx_pkt_t *pkt) {
|
||||||
|
// return pkt->data.frame.flags;
|
||||||
|
// }
|
||||||
//
|
//
|
||||||
// // Alloc helpers
|
// // Alloc helpers
|
||||||
// vpx_codec_ctx_t *newCtx() {
|
// vpx_codec_ctx_t *newCtx() {
|
||||||
@@ -70,6 +73,8 @@ type encoder struct {
|
|||||||
tLastFrame int
|
tLastFrame int
|
||||||
frame []byte
|
frame []byte
|
||||||
deadline int
|
deadline int
|
||||||
|
requireKeyFrame bool
|
||||||
|
isKeyFrame bool
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
closed bool
|
closed bool
|
||||||
@@ -141,6 +146,7 @@ func newParams(codecIface *C.vpx_codec_iface_t) (Params, error) {
|
|||||||
RateControlOvershootPercent: uint(cfg.rc_overshoot_pct),
|
RateControlOvershootPercent: uint(cfg.rc_overshoot_pct),
|
||||||
RateControlMinQuantizer: uint(cfg.rc_min_quantizer),
|
RateControlMinQuantizer: uint(cfg.rc_min_quantizer),
|
||||||
RateControlMaxQuantizer: uint(cfg.rc_max_quantizer),
|
RateControlMaxQuantizer: uint(cfg.rc_max_quantizer),
|
||||||
|
LagInFrames: uint(cfg.g_lag_in_frames),
|
||||||
ErrorResilient: ErrorResilientMode(cfg.g_error_resilient),
|
ErrorResilient: ErrorResilientMode(cfg.g_error_resilient),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -171,6 +177,7 @@ func newEncoder(r video.Reader, p prop.Media, params Params, codecIface *C.vpx_c
|
|||||||
cfg.g_h = C.uint(p.Height)
|
cfg.g_h = C.uint(p.Height)
|
||||||
cfg.g_timebase.num = 1
|
cfg.g_timebase.num = 1
|
||||||
cfg.g_timebase.den = 1000
|
cfg.g_timebase.den = 1000
|
||||||
|
cfg.g_lag_in_frames = C.uint(params.LagInFrames)
|
||||||
cfg.rc_target_bitrate = C.uint(params.BitRate) / 1000
|
cfg.rc_target_bitrate = C.uint(params.BitRate) / 1000
|
||||||
cfg.kf_max_dist = C.uint(params.KeyFrameInterval)
|
cfg.kf_max_dist = C.uint(params.KeyFrameInterval)
|
||||||
|
|
||||||
@@ -229,9 +236,16 @@ func (e *encoder) Read() ([]byte, func(), error) {
|
|||||||
|
|
||||||
if e.cfg.g_w != C.uint(width) || e.cfg.g_h != C.uint(height) {
|
if e.cfg.g_w != C.uint(width) || e.cfg.g_h != C.uint(height) {
|
||||||
e.cfg.g_w, e.cfg.g_h = C.uint(width), C.uint(height)
|
e.cfg.g_w, e.cfg.g_h = C.uint(width), C.uint(height)
|
||||||
if ec := C.vpx_codec_enc_config_set(e.codec, e.cfg); ec != C.VPX_CODEC_OK {
|
|
||||||
return nil, func() {}, fmt.Errorf("vpx_codec_enc_config_set failed (%d)", ec)
|
newCodec := C.newCtx()
|
||||||
|
if ec := C.vpx_codec_enc_init_ver(
|
||||||
|
newCodec, e.codec.iface, e.cfg, 0, C.VPX_ENCODER_ABI_VERSION,
|
||||||
|
); ec != 0 {
|
||||||
|
return nil, func() {}, fmt.Errorf("vpx_codec_enc_init failed (%d)", ec)
|
||||||
}
|
}
|
||||||
|
C.free(unsafe.Pointer(e.codec))
|
||||||
|
e.codec = newCodec
|
||||||
|
|
||||||
e.raw.w, e.raw.h = C.uint(width), C.uint(height)
|
e.raw.w, e.raw.h = C.uint(width), C.uint(height)
|
||||||
e.raw.r_w, e.raw.r_h = C.uint(width), C.uint(height)
|
e.raw.r_w, e.raw.r_h = C.uint(width), C.uint(height)
|
||||||
e.raw.d_w, e.raw.d_h = C.uint(width), C.uint(height)
|
e.raw.d_w, e.raw.d_h = C.uint(width), C.uint(height)
|
||||||
@@ -247,6 +261,9 @@ func (e *encoder) Read() ([]byte, func(), error) {
|
|||||||
duration = 1
|
duration = 1
|
||||||
}
|
}
|
||||||
var flags int
|
var flags int
|
||||||
|
if e.requireKeyFrame {
|
||||||
|
flags = flags | C.VPX_EFLAG_FORCE_KF
|
||||||
|
}
|
||||||
if ec := C.encode_wrapper(
|
if ec := C.encode_wrapper(
|
||||||
e.codec, e.raw,
|
e.codec, e.raw,
|
||||||
C.long(t-e.tStart), C.ulong(duration), C.long(flags), C.ulong(e.deadline),
|
C.long(t-e.tStart), C.ulong(duration), C.long(flags), C.ulong(e.deadline),
|
||||||
@@ -255,6 +272,7 @@ func (e *encoder) Read() ([]byte, func(), error) {
|
|||||||
return nil, func() {}, fmt.Errorf("vpx_codec_encode failed (%d)", ec)
|
return nil, func() {}, fmt.Errorf("vpx_codec_encode failed (%d)", ec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e.requireKeyFrame = false
|
||||||
e.frameIndex++
|
e.frameIndex++
|
||||||
e.tLastFrame = t
|
e.tLastFrame = t
|
||||||
|
|
||||||
@@ -266,6 +284,7 @@ func (e *encoder) Read() ([]byte, func(), error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
if pkt.kind == C.VPX_CODEC_CX_FRAME_PKT {
|
if pkt.kind == C.VPX_CODEC_CX_FRAME_PKT {
|
||||||
|
e.isKeyFrame = C.pktFrameFlags(pkt)&C.VPX_FRAME_IS_KEY == C.VPX_FRAME_IS_KEY
|
||||||
encoded := C.GoBytes(unsafe.Pointer(C.pktBuf(pkt)), C.pktSz(pkt))
|
encoded := C.GoBytes(unsafe.Pointer(C.pktBuf(pkt)), C.pktSz(pkt))
|
||||||
e.frame = append(e.frame, encoded...)
|
e.frame = append(e.frame, encoded...)
|
||||||
}
|
}
|
||||||
@@ -281,13 +300,20 @@ func (e *encoder) SetBitRate(b int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *encoder) ForceKeyFrame() error {
|
func (e *encoder) ForceKeyFrame() error {
|
||||||
panic("ForceKeyFrame is not implemented")
|
e.mu.Lock()
|
||||||
|
defer e.mu.Unlock()
|
||||||
|
e.requireKeyFrame = true
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *encoder) Close() error {
|
func (e *encoder) Close() error {
|
||||||
e.mu.Lock()
|
e.mu.Lock()
|
||||||
defer e.mu.Unlock()
|
defer e.mu.Unlock()
|
||||||
|
|
||||||
|
if e.closed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
e.closed = true
|
e.closed = true
|
||||||
|
|
||||||
C.free(unsafe.Pointer(e.raw))
|
C.free(unsafe.Pointer(e.raw))
|
||||||
|
212
pkg/codec/vpx/vpx_test.go
Normal file
212
pkg/codec/vpx/vpx_test.go
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
package vpx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"io"
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pion/mediadevices/pkg/codec"
|
||||||
|
"github.com/pion/mediadevices/pkg/codec/internal/codectest"
|
||||||
|
"github.com/pion/mediadevices/pkg/frame"
|
||||||
|
"github.com/pion/mediadevices/pkg/io/video"
|
||||||
|
"github.com/pion/mediadevices/pkg/prop"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEncoder(t *testing.T) {
|
||||||
|
for name, factory := range map[string]func() (codec.VideoEncoderBuilder, error){
|
||||||
|
"VP8": func() (codec.VideoEncoderBuilder, error) {
|
||||||
|
p, err := NewVP8Params()
|
||||||
|
return &p, err
|
||||||
|
},
|
||||||
|
"VP9": func() (codec.VideoEncoderBuilder, error) {
|
||||||
|
p, err := NewVP9Params()
|
||||||
|
p.LagInFrames = 0
|
||||||
|
return &p, err
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
factory := factory
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Run("SimpleRead", func(t *testing.T) {
|
||||||
|
p, err := factory()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
codectest.VideoEncoderSimpleReadTest(t, p,
|
||||||
|
prop.Media{
|
||||||
|
Video: prop.Video{
|
||||||
|
Width: 256,
|
||||||
|
Height: 144,
|
||||||
|
FrameFormat: frame.FormatI420,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
image.NewYCbCr(
|
||||||
|
image.Rect(0, 0, 256, 144),
|
||||||
|
image.YCbCrSubsampleRatio420,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
t.Run("CloseTwice", func(t *testing.T) {
|
||||||
|
p, err := factory()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
codectest.VideoEncoderCloseTwiceTest(t, p, prop.Media{
|
||||||
|
Video: prop.Video{
|
||||||
|
Width: 640,
|
||||||
|
Height: 480,
|
||||||
|
FrameRate: 30,
|
||||||
|
FrameFormat: frame.FormatI420,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestImageSizeChange(t *testing.T) {
|
||||||
|
for name, factory := range map[string]func() (codec.VideoEncoderBuilder, error){
|
||||||
|
"VP8": func() (codec.VideoEncoderBuilder, error) {
|
||||||
|
p, err := NewVP8Params()
|
||||||
|
return &p, err
|
||||||
|
},
|
||||||
|
"VP9": func() (codec.VideoEncoderBuilder, error) {
|
||||||
|
p, err := NewVP9Params()
|
||||||
|
// Disable latency to ease test and begin to receive packets for each input frame
|
||||||
|
p.LagInFrames = 0
|
||||||
|
return &p, err
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
factory := factory
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
param, err := factory()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, testCase := range map[string]struct {
|
||||||
|
initialWidth, initialHeight int
|
||||||
|
width, height int
|
||||||
|
}{
|
||||||
|
"NoChange": {
|
||||||
|
320, 240,
|
||||||
|
320, 240,
|
||||||
|
},
|
||||||
|
"Enlarge": {
|
||||||
|
320, 240,
|
||||||
|
640, 480,
|
||||||
|
},
|
||||||
|
"Shrink": {
|
||||||
|
640, 480,
|
||||||
|
320, 240,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
var cnt uint32
|
||||||
|
r, err := param.BuildVideoEncoder(
|
||||||
|
video.ReaderFunc(func() (image.Image, func(), error) {
|
||||||
|
i := atomic.AddUint32(&cnt, 1)
|
||||||
|
if i == 1 {
|
||||||
|
return image.NewYCbCr(
|
||||||
|
image.Rect(0, 0, testCase.width, testCase.height),
|
||||||
|
image.YCbCrSubsampleRatio420,
|
||||||
|
), func() {}, nil
|
||||||
|
}
|
||||||
|
return nil, nil, io.EOF
|
||||||
|
}),
|
||||||
|
prop.Media{
|
||||||
|
Video: prop.Video{
|
||||||
|
Width: testCase.initialWidth,
|
||||||
|
Height: testCase.initialHeight,
|
||||||
|
FrameRate: 1,
|
||||||
|
FrameFormat: frame.FormatI420,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, rel, err := r.Read()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rel()
|
||||||
|
_, _, err = r.Read()
|
||||||
|
if err != io.EOF {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequestKeyFrame(t *testing.T) {
|
||||||
|
for name, factory := range map[string]func() (codec.VideoEncoderBuilder, error){
|
||||||
|
"VP8": func() (codec.VideoEncoderBuilder, error) {
|
||||||
|
p, err := NewVP8Params()
|
||||||
|
return &p, err
|
||||||
|
},
|
||||||
|
"VP9": func() (codec.VideoEncoderBuilder, error) {
|
||||||
|
p, err := NewVP9Params()
|
||||||
|
// Disable latency to ease test and begin to receive packets for each input frame
|
||||||
|
p.LagInFrames = 0
|
||||||
|
return &p, err
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
factory := factory
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
param, err := factory()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var initialWidth, initialHeight, width, height int = 320, 240, 320, 240
|
||||||
|
|
||||||
|
var cnt uint32
|
||||||
|
r, err := param.BuildVideoEncoder(
|
||||||
|
video.ReaderFunc(func() (image.Image, func(), error) {
|
||||||
|
i := atomic.AddUint32(&cnt, 1)
|
||||||
|
if i == 3 {
|
||||||
|
return nil, nil, io.EOF
|
||||||
|
}
|
||||||
|
return image.NewYCbCr(
|
||||||
|
image.Rect(0, 0, width, height),
|
||||||
|
image.YCbCrSubsampleRatio420,
|
||||||
|
), func() {}, nil
|
||||||
|
}),
|
||||||
|
prop.Media{
|
||||||
|
Video: prop.Video{
|
||||||
|
Width: initialWidth,
|
||||||
|
Height: initialHeight,
|
||||||
|
FrameRate: 1,
|
||||||
|
FrameFormat: frame.FormatI420,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, rel, err := r.Read()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rel()
|
||||||
|
r.ForceKeyFrame()
|
||||||
|
_, rel, err = r.Read()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !r.(*encoder).isKeyFrame {
|
||||||
|
t.Fatal("Not a key frame")
|
||||||
|
}
|
||||||
|
rel()
|
||||||
|
_, _, err = r.Read()
|
||||||
|
if err != io.EOF {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
48
pkg/codec/x264/x264_test.go
Normal file
48
pkg/codec/x264/x264_test.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package x264
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pion/mediadevices/pkg/codec/internal/codectest"
|
||||||
|
"github.com/pion/mediadevices/pkg/frame"
|
||||||
|
"github.com/pion/mediadevices/pkg/prop"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEncoder(t *testing.T) {
|
||||||
|
t.Run("SimpleRead", func(t *testing.T) {
|
||||||
|
p, err := NewParams()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
p.BitRate = 200000
|
||||||
|
codectest.VideoEncoderSimpleReadTest(t, &p,
|
||||||
|
prop.Media{
|
||||||
|
Video: prop.Video{
|
||||||
|
Width: 256,
|
||||||
|
Height: 144,
|
||||||
|
FrameFormat: frame.FormatI420,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
image.NewYCbCr(
|
||||||
|
image.Rect(0, 0, 256, 144),
|
||||||
|
image.YCbCrSubsampleRatio420,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
t.Run("CloseTwice", func(t *testing.T) {
|
||||||
|
p, err := NewParams()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
p.BitRate = 200000
|
||||||
|
codectest.VideoEncoderCloseTwiceTest(t, &p, prop.Media{
|
||||||
|
Video: prop.Video{
|
||||||
|
Width: 640,
|
||||||
|
Height: 480,
|
||||||
|
FrameRate: 30,
|
||||||
|
FrameFormat: frame.FormatI420,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
@@ -1 +1,16 @@
|
|||||||
|
/*
|
||||||
|
Package camera provides a video camera driver.
|
||||||
|
|
||||||
|
Device Label Generation Rules
|
||||||
|
|
||||||
|
On Linux, the device label will be in the format of:
|
||||||
|
pci-0000:00:00.0-usb-0:0:0.0-video-index0;video0
|
||||||
|
If /dev/v4l/by-path/* is not available (for example in a docker container without
|
||||||
|
bindings in /dev/v4l/by-path/), it will be:
|
||||||
|
video0;video0
|
||||||
|
*/
|
||||||
package camera
|
package camera
|
||||||
|
|
||||||
|
// LabelSeparator is used to separate labels for a driver that
|
||||||
|
// is found from multiple locations on a host.
|
||||||
|
const LabelSeparator = ";"
|
||||||
|
@@ -56,7 +56,7 @@ func BenchmarkRead(b *testing.B) {
|
|||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, err := r.Read()
|
_, _, err := r.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("Failed to read: %v", err)
|
b.Fatalf("Failed to read: %v", err)
|
||||||
}
|
}
|
||||||
|
63
pkg/driver/camera/camera_darwin_test.go
Normal file
63
pkg/driver/camera/camera_darwin_test.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// +build darwin
|
||||||
|
|
||||||
|
// $ go test -v . -tags darwin -run="^TestCameraFrameFormatSupport$"
|
||||||
|
|
||||||
|
package camera
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pion/mediadevices/pkg/avfoundation"
|
||||||
|
"github.com/pion/mediadevices/pkg/frame"
|
||||||
|
"github.com/pion/mediadevices/pkg/prop"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCameraFrameFormatSupport(t *testing.T) {
|
||||||
|
devices, err := avfoundation.Devices(avfoundation.Video)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(devices) > 0 {
|
||||||
|
c := newCamera(devices[0])
|
||||||
|
if err := c.Open(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
supportedFormats := make(map[frame.Format]struct{})
|
||||||
|
for _, p := range c.Properties() {
|
||||||
|
supportedFormats[p.FrameFormat] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, format := range []frame.Format{
|
||||||
|
frame.FormatI420,
|
||||||
|
frame.FormatNV12,
|
||||||
|
frame.FormatNV21,
|
||||||
|
frame.FormatYUY2,
|
||||||
|
frame.FormatUYVY,
|
||||||
|
} {
|
||||||
|
if _, ok := supportedFormats[format]; !ok {
|
||||||
|
t.Logf("[%v] UNSUPPORTED", format)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r, err := c.VideoRecord(prop.Media{
|
||||||
|
Video: prop.Video{
|
||||||
|
Width: 640,
|
||||||
|
Height: 480,
|
||||||
|
FrameFormat: format,
|
||||||
|
}})
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("[%v] Failed to capture image: %v", format, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
_, _, err := r.Read()
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("[%v] Failed to read: %v", format, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Logf("[%v] OK", format)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -73,8 +73,11 @@ type camera struct {
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
discovered := make(map[string]struct{})
|
discovered := make(map[string]struct{})
|
||||||
|
discover(discovered, "/dev/v4l/by-path/*")
|
||||||
|
discover(discovered, "/dev/video*")
|
||||||
|
}
|
||||||
|
|
||||||
discover := func(pattern string) {
|
func discover(discovered map[string]struct{}, pattern string) {
|
||||||
devices, err := filepath.Glob(pattern)
|
devices, err := filepath.Glob(pattern)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// No v4l device.
|
// No v4l device.
|
||||||
@@ -88,7 +91,6 @@ func init() {
|
|||||||
} else {
|
} else {
|
||||||
reallink = filepath.Base(reallink)
|
reallink = filepath.Base(reallink)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := discovered[reallink]; ok {
|
if _, ok := discovered[reallink]; ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -96,28 +98,26 @@ func init() {
|
|||||||
discovered[reallink] = struct{}{}
|
discovered[reallink] = struct{}{}
|
||||||
cam := newCamera(device)
|
cam := newCamera(device)
|
||||||
priority := driver.PriorityNormal
|
priority := driver.PriorityNormal
|
||||||
if label == prioritizedDevice {
|
if reallink == prioritizedDevice {
|
||||||
priority = driver.PriorityHigh
|
priority = driver.PriorityHigh
|
||||||
}
|
}
|
||||||
driver.GetManager().Register(cam, driver.Info{
|
driver.GetManager().Register(cam, driver.Info{
|
||||||
Label: label,
|
Label: label + LabelSeparator + reallink,
|
||||||
DeviceType: driver.Camera,
|
DeviceType: driver.Camera,
|
||||||
Priority: priority,
|
Priority: priority,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
discover("/dev/v4l/by-path/*")
|
|
||||||
discover("/dev/video*")
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCamera(path string) *camera {
|
func newCamera(path string) *camera {
|
||||||
formats := map[webcam.PixelFormat]frame.Format{
|
formats := map[webcam.PixelFormat]frame.Format{
|
||||||
webcam.PixelFormat(C.V4L2_PIX_FMT_YUV420): frame.FormatI420,
|
webcam.PixelFormat(C.V4L2_PIX_FMT_YUV420): frame.FormatI420,
|
||||||
|
webcam.PixelFormat(C.V4L2_PIX_FMT_NV21): frame.FormatNV21,
|
||||||
|
webcam.PixelFormat(C.V4L2_PIX_FMT_NV12): frame.FormatNV12,
|
||||||
webcam.PixelFormat(C.V4L2_PIX_FMT_YUYV): frame.FormatYUYV,
|
webcam.PixelFormat(C.V4L2_PIX_FMT_YUYV): frame.FormatYUYV,
|
||||||
webcam.PixelFormat(C.V4L2_PIX_FMT_UYVY): frame.FormatUYVY,
|
webcam.PixelFormat(C.V4L2_PIX_FMT_UYVY): frame.FormatUYVY,
|
||||||
webcam.PixelFormat(C.V4L2_PIX_FMT_NV12): frame.FormatNV21,
|
|
||||||
webcam.PixelFormat(C.V4L2_PIX_FMT_MJPEG): frame.FormatMJPEG,
|
webcam.PixelFormat(C.V4L2_PIX_FMT_MJPEG): frame.FormatMJPEG,
|
||||||
|
webcam.PixelFormat(C.V4L2_PIX_FMT_Z16): frame.FormatZ16,
|
||||||
}
|
}
|
||||||
|
|
||||||
reversedFormats := make(map[frame.Format]webcam.PixelFormat)
|
reversedFormats := make(map[frame.Format]webcam.PixelFormat)
|
||||||
|
72
pkg/driver/camera/camera_linux_test.go
Normal file
72
pkg/driver/camera/camera_linux_test.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package camera
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pion/mediadevices/pkg/driver"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDiscover(t *testing.T) {
|
||||||
|
const (
|
||||||
|
shortName = "unittest-video0"
|
||||||
|
shortName2 = "unittest-video1"
|
||||||
|
longName = "unittest-long-device-name:0:1:2:3"
|
||||||
|
)
|
||||||
|
|
||||||
|
dir, err := ioutil.TempDir("", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
byPathDir := filepath.Join(dir, "v4l", "by-path")
|
||||||
|
if err := os.MkdirAll(byPathDir, 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(filepath.Join(dir, shortName), []byte{}, 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(filepath.Join(dir, shortName2), []byte{}, 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.Symlink(
|
||||||
|
filepath.Join(dir, shortName),
|
||||||
|
filepath.Join(byPathDir, longName),
|
||||||
|
); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
discovered := make(map[string]struct{})
|
||||||
|
discover(discovered, filepath.Join(byPathDir, "*"))
|
||||||
|
discover(discovered, filepath.Join(dir, "unittest-video*"))
|
||||||
|
|
||||||
|
drvs := driver.GetManager().Query(func(d driver.Driver) bool {
|
||||||
|
// Ignore real cameras.
|
||||||
|
return d.Info().DeviceType == driver.Camera && strings.Contains(d.Info().Label, "unittest")
|
||||||
|
})
|
||||||
|
if len(drvs) != 2 {
|
||||||
|
t.Fatalf("Expected 2 driver, got %d drivers", len(drvs))
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := []string{
|
||||||
|
drvs[0].Info().Label,
|
||||||
|
drvs[1].Info().Label,
|
||||||
|
}
|
||||||
|
// Returned drivers are unordered. Sort to get static result.
|
||||||
|
sort.Sort(sort.StringSlice(labels))
|
||||||
|
|
||||||
|
expected := longName + LabelSeparator + shortName
|
||||||
|
if label := labels[0]; label != expected {
|
||||||
|
t.Errorf("Expected label: %s, got: %s", expected, label)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedNoLink := shortName2 + LabelSeparator + shortName2
|
||||||
|
if label := labels[1]; label != expectedNoLink {
|
||||||
|
t.Errorf("Expected label: %s, got: %s", expectedNoLink, label)
|
||||||
|
}
|
||||||
|
}
|
@@ -155,9 +155,6 @@ func (m *microphone) AudioRecord(inputProp prop.Media) (audio.Reader, error) {
|
|||||||
return decodedChunk, func() {}, err
|
return decodedChunk, func() {}, err
|
||||||
})
|
})
|
||||||
|
|
||||||
// FIXME: The current audio detection and audio encoder can only work with a static latency. Since the latency from the driver
|
|
||||||
// can fluctuate, we need to stabilize it. Maybe there's a better way for doing this?
|
|
||||||
reader = audio.NewBuffer(int(inputProp.Latency.Seconds() * float64(inputProp.SampleRate)))(reader)
|
|
||||||
return reader, nil
|
return reader, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,6 +2,7 @@ package screen
|
|||||||
|
|
||||||
// #cgo pkg-config: x11 xext
|
// #cgo pkg-config: x11 xext
|
||||||
// #include <stdint.h>
|
// #include <stdint.h>
|
||||||
|
// #include <string.h>
|
||||||
// #include <sys/shm.h>
|
// #include <sys/shm.h>
|
||||||
// #include <X11/Xlib.h>
|
// #include <X11/Xlib.h>
|
||||||
// #define XUTIL_DEFINE_FUNCTIONS
|
// #define XUTIL_DEFINE_FUNCTIONS
|
||||||
@@ -125,6 +126,14 @@ func (c colorFunc) RGBA() (r, g, b, a uint32) {
|
|||||||
|
|
||||||
func (s *shmImage) At(x, y int) color.Color {
|
func (s *shmImage) At(x, y int) color.Color {
|
||||||
switch s.pixFmt {
|
switch s.pixFmt {
|
||||||
|
case pixFmtRGB24:
|
||||||
|
addr := (x + y*int(s.img.width)) * 4
|
||||||
|
r := uint32(s.b[addr]) * 0x100
|
||||||
|
g := uint32(s.b[addr+1]) * 0x100
|
||||||
|
b := uint32(s.b[addr+2]) * 0x100
|
||||||
|
return colorFunc(func() (_, _, _, _ uint32) {
|
||||||
|
return r, g, b, 0xFFFF
|
||||||
|
})
|
||||||
case pixFmtBGR24:
|
case pixFmtBGR24:
|
||||||
addr := (x + y*int(s.img.width)) * 4
|
addr := (x + y*int(s.img.width)) * 4
|
||||||
b := uint32(s.b[addr]) * 0x100
|
b := uint32(s.b[addr]) * 0x100
|
||||||
@@ -133,6 +142,15 @@ func (s *shmImage) At(x, y int) color.Color {
|
|||||||
return colorFunc(func() (_, _, _, _ uint32) {
|
return colorFunc(func() (_, _, _, _ uint32) {
|
||||||
return r, g, b, 0xFFFF
|
return r, g, b, 0xFFFF
|
||||||
})
|
})
|
||||||
|
case pixFmtRGB16:
|
||||||
|
addr := (x + y*int(s.img.width)) * 2
|
||||||
|
b1, b2 := s.b[addr], s.b[addr+1]
|
||||||
|
r := uint32(b1>>3) * 0x100
|
||||||
|
g := uint32((b1&0x7)<<3|(b2&0xE0)>>5) * 0x100
|
||||||
|
b := uint32(b2&0x1F) * 0x100
|
||||||
|
return colorFunc(func() (_, _, _, _ uint32) {
|
||||||
|
return r, g, b, 0xFFFF
|
||||||
|
})
|
||||||
case pixFmtBGR16:
|
case pixFmtBGR16:
|
||||||
addr := (x + y*int(s.img.width)) * 2
|
addr := (x + y*int(s.img.width)) * 2
|
||||||
b1, b2 := s.b[addr], s.b[addr+1]
|
b1, b2 := s.b[addr], s.b[addr+1]
|
||||||
@@ -149,12 +167,25 @@ func (s *shmImage) At(x, y int) color.Color {
|
|||||||
|
|
||||||
func (s *shmImage) RGBAAt(x, y int) color.RGBA {
|
func (s *shmImage) RGBAAt(x, y int) color.RGBA {
|
||||||
switch s.pixFmt {
|
switch s.pixFmt {
|
||||||
|
case pixFmtRGB24:
|
||||||
|
addr := (x + y*int(s.img.width)) * 4
|
||||||
|
r := s.b[addr]
|
||||||
|
g := s.b[addr+1]
|
||||||
|
b := s.b[addr+2]
|
||||||
|
return color.RGBA{R: r, G: g, B: b, A: 0xFF}
|
||||||
case pixFmtBGR24:
|
case pixFmtBGR24:
|
||||||
addr := (x + y*int(s.img.width)) * 4
|
addr := (x + y*int(s.img.width)) * 4
|
||||||
b := s.b[addr]
|
b := s.b[addr]
|
||||||
g := s.b[addr+1]
|
g := s.b[addr+1]
|
||||||
r := s.b[addr+2]
|
r := s.b[addr+2]
|
||||||
return color.RGBA{R: r, G: g, B: b, A: 0xFF}
|
return color.RGBA{R: r, G: g, B: b, A: 0xFF}
|
||||||
|
case pixFmtRGB16:
|
||||||
|
addr := (x + y*int(s.img.width)) * 2
|
||||||
|
b1, b2 := s.b[addr], s.b[addr+1]
|
||||||
|
r := b1 >> 3
|
||||||
|
g := (b1&0x7)<<3 | (b2&0xE0)>>5
|
||||||
|
b := b2 & 0x1F
|
||||||
|
return color.RGBA{R: r, G: g, B: b, A: 0xFF}
|
||||||
case pixFmtBGR16:
|
case pixFmtBGR16:
|
||||||
addr := (x + y*int(s.img.width)) * 2
|
addr := (x + y*int(s.img.width)) * 2
|
||||||
b1, b2 := s.b[addr], s.b[addr+1]
|
b1, b2 := s.b[addr], s.b[addr+1]
|
||||||
@@ -178,11 +209,17 @@ func (s *shmImage) ToRGBA(dst *image.RGBA) *image.RGBA {
|
|||||||
dst.Pix = dst.Pix[:l]
|
dst.Pix = dst.Pix[:l]
|
||||||
}
|
}
|
||||||
switch s.pixFmt {
|
switch s.pixFmt {
|
||||||
|
case pixFmtRGB24:
|
||||||
|
C.memcpy(unsafe.Pointer(&dst.Pix[0]), unsafe.Pointer(s.img.data), C.size_t(len(dst.Pix)))
|
||||||
|
return dst
|
||||||
case pixFmtBGR24:
|
case pixFmtBGR24:
|
||||||
C.copyBGR24(unsafe.Pointer(&dst.Pix[0]), s.img.data, C.ulong(len(dst.Pix)))
|
C.copyBGR24(unsafe.Pointer(&dst.Pix[0]), s.img.data, C.size_t(len(dst.Pix)))
|
||||||
|
return dst
|
||||||
|
case pixFmtRGB16:
|
||||||
|
C.memcpy(unsafe.Pointer(&dst.Pix[0]), unsafe.Pointer(s.img.data), C.size_t(len(dst.Pix)))
|
||||||
return dst
|
return dst
|
||||||
case pixFmtBGR16:
|
case pixFmtBGR16:
|
||||||
C.copyBGR16(unsafe.Pointer(&dst.Pix[0]), s.img.data, C.ulong(len(dst.Pix)))
|
C.copyBGR16(unsafe.Pointer(&dst.Pix[0]), s.img.data, C.size_t(len(dst.Pix)))
|
||||||
return dst
|
return dst
|
||||||
default:
|
default:
|
||||||
panic("unsupported pixel format")
|
panic("unsupported pixel format")
|
||||||
@@ -199,8 +236,12 @@ func newShmImage(dp *C.Display, screen int) (*shmImage, error) {
|
|||||||
s := &shmImage{dp: dp}
|
s := &shmImage{dp: dp}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
case v.red_mask == 0xFF && v.green_mask == 0xFF00 && v.blue_mask == 0xFF0000:
|
||||||
|
s.pixFmt = pixFmtRGB24
|
||||||
case v.red_mask == 0xFF0000 && v.green_mask == 0xFF00 && v.blue_mask == 0xFF:
|
case v.red_mask == 0xFF0000 && v.green_mask == 0xFF00 && v.blue_mask == 0xFF:
|
||||||
s.pixFmt = pixFmtBGR24
|
s.pixFmt = pixFmtBGR24
|
||||||
|
case v.red_mask == 0x1F && v.green_mask == 0x7E0 && v.blue_mask == 0xF800:
|
||||||
|
s.pixFmt = pixFmtRGB16
|
||||||
case v.red_mask == 0xF800 && v.green_mask == 0x7E0 && v.blue_mask == 0x1F:
|
case v.red_mask == 0xF800 && v.green_mask == 0x7E0 && v.blue_mask == 0x1F:
|
||||||
s.pixFmt = pixFmtBGR16
|
s.pixFmt = pixFmtBGR16
|
||||||
default:
|
default:
|
||||||
@@ -209,7 +250,7 @@ func newShmImage(dp *C.Display, screen int) (*shmImage, error) {
|
|||||||
return nil, errors.New("unsupported pixel format")
|
return nil, errors.New("unsupported pixel format")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.shm.shmid = C.shmget(C.IPC_PRIVATE, C.ulong(w*h*4+8), C.IPC_CREAT|0600)
|
s.shm.shmid = C.shmget(C.IPC_PRIVATE, C.size_t(w*h*4+8), C.IPC_CREAT|0600)
|
||||||
if s.shm.shmid == -1 {
|
if s.shm.shmid == -1 {
|
||||||
return nil, errors.New("failed to get shared memory")
|
return nil, errors.New("failed to get shared memory")
|
||||||
}
|
}
|
||||||
@@ -279,5 +320,5 @@ func (r *reader) Close() {
|
|||||||
|
|
||||||
// cAlign64 is fot testing
|
// cAlign64 is fot testing
|
||||||
func cAlign64(ptr uintptr) uintptr {
|
func cAlign64(ptr uintptr) uintptr {
|
||||||
return uintptr(C.align64ForTest(C.ulong(uintptr(ptr))))
|
return uintptr(C.align64ForTest(C.size_t(uintptr(ptr))))
|
||||||
}
|
}
|
||||||
|
@@ -1,14 +1,19 @@
|
|||||||
package driver
|
package driver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/google/uuid"
|
||||||
"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/prop"
|
"github.com/pion/mediadevices/pkg/prop"
|
||||||
uuid "github.com/satori/go.uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func wrapAdapter(a Adapter, info Info) Driver {
|
func wrapAdapter(a Adapter, info Info) Driver {
|
||||||
id := uuid.NewV4().String()
|
generator, err := uuid.NewRandom()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
id := generator.String()
|
||||||
d := &adapterWrapper{
|
d := &adapterWrapper{
|
||||||
Adapter: a,
|
Adapter: a,
|
||||||
id: id,
|
id: id,
|
||||||
|
@@ -1,24 +0,0 @@
|
|||||||
package frame
|
|
||||||
|
|
||||||
type Format string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// FormatI420 https://www.fourcc.org/pixel-format/yuv-i420/
|
|
||||||
FormatI420 Format = "I420"
|
|
||||||
// FormatI444 is a YUV format without sub-sampling
|
|
||||||
FormatI444 Format = "I444"
|
|
||||||
// FormatNV21 https://www.fourcc.org/pixel-format/yuv-nv21/
|
|
||||||
FormatNV21 = "NV21"
|
|
||||||
// FormatYUY2 https://www.fourcc.org/pixel-format/yuv-yuy2/
|
|
||||||
FormatYUY2 = "YUY2"
|
|
||||||
// FormatUYVY https://www.fourcc.org/pixel-format/yuv-uyvy/
|
|
||||||
FormatUYVY = "UYVY"
|
|
||||||
|
|
||||||
// FormatRGBA https://www.fourcc.org/pixel-format/rgb-rgba/
|
|
||||||
FormatRGBA Format = "RGBA"
|
|
||||||
|
|
||||||
// FormatMJPEG https://www.fourcc.org/mjpg/
|
|
||||||
FormatMJPEG = "MJPEG"
|
|
||||||
)
|
|
||||||
|
|
||||||
const FormatYUYV = FormatYUY2
|
|
@@ -4,21 +4,48 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewDecoder(f Format) (Decoder, error) {
|
type Format string
|
||||||
var decoder decoderFunc
|
|
||||||
|
|
||||||
switch f {
|
const (
|
||||||
case FormatI420:
|
// FormatI420 https://www.fourcc.org/pixel-format/yuv-i420/
|
||||||
decoder = decodeI420
|
FormatI420 Format = "I420"
|
||||||
case FormatNV21:
|
// FormatI444 is a YUV format without sub-sampling
|
||||||
decoder = decodeNV21
|
FormatI444 Format = "I444"
|
||||||
case FormatYUY2:
|
// FormatNV21 https://www.fourcc.org/pixel-format/yuv-nv21/
|
||||||
decoder = decodeYUY2
|
FormatNV21 = "NV21"
|
||||||
case FormatUYVY:
|
// FormatNV12 https://www.fourcc.org/pixel-format/yuv-nv12/
|
||||||
decoder = decodeUYVY
|
FormatNV12 = "NV12"
|
||||||
case FormatMJPEG:
|
// FormatYUY2 https://www.fourcc.org/pixel-format/yuv-yuy2/
|
||||||
decoder = decodeMJPEG
|
FormatYUY2 = "YUY2"
|
||||||
default:
|
// FormatUYVY https://www.fourcc.org/pixel-format/yuv-uyvy/
|
||||||
|
FormatUYVY = "UYVY"
|
||||||
|
|
||||||
|
// FormatRGBA https://www.fourcc.org/pixel-format/rgb-rgba/
|
||||||
|
FormatRGBA Format = "RGBA"
|
||||||
|
|
||||||
|
// FormatMJPEG https://www.fourcc.org/mjpg/
|
||||||
|
FormatMJPEG = "MJPEG"
|
||||||
|
|
||||||
|
// FormatZ16 https://www.kernel.org/doc/html/v5.9/userspace-api/media/v4l/pixfmt-z16.html
|
||||||
|
FormatZ16 = "Z16"
|
||||||
|
)
|
||||||
|
|
||||||
|
const FormatYUYV = FormatYUY2
|
||||||
|
|
||||||
|
var decoderMap = map[Format]decoderFunc{
|
||||||
|
FormatI420: decodeI420,
|
||||||
|
FormatNV21: decodeNV21,
|
||||||
|
FormatNV12: decodeNV12,
|
||||||
|
FormatYUY2: decodeYUY2,
|
||||||
|
FormatUYVY: decodeUYVY,
|
||||||
|
FormatMJPEG: decodeMJPEG,
|
||||||
|
FormatZ16: decodeZ16,
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDecoder(f Format) (Decoder, error) {
|
||||||
|
decoder, ok := decoderMap[f]
|
||||||
|
|
||||||
|
if !ok {
|
||||||
return nil, fmt.Errorf("%s is not supported", f)
|
return nil, fmt.Errorf("%s is not supported", f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -35,8 +35,8 @@ func decodeNV21(frame []byte, width, height int) (image.Image, func(), error) {
|
|||||||
|
|
||||||
var cb, cr []byte
|
var cb, cr []byte
|
||||||
for i := yi; i < ci; i += 2 {
|
for i := yi; i < ci; i += 2 {
|
||||||
cb = append(cb, frame[i])
|
cr = append(cr, frame[i])
|
||||||
cr = append(cr, frame[i+1])
|
cb = append(cb, frame[i+1])
|
||||||
}
|
}
|
||||||
|
|
||||||
return &image.YCbCr{
|
return &image.YCbCr{
|
||||||
@@ -49,3 +49,15 @@ func decodeNV21(frame []byte, width, height int) (image.Image, func(), error) {
|
|||||||
Rect: image.Rect(0, 0, width, height),
|
Rect: image.Rect(0, 0, width, height),
|
||||||
}, func() {}, nil
|
}, func() {}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decodeNV12(frame []byte, width, height int) (image.Image, func(), error) {
|
||||||
|
img, release, err := decodeNV21(frame, width, height)
|
||||||
|
if err != nil {
|
||||||
|
return img, release, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// The only difference between NV21 and NV12 is the chroma order, so simply swap them
|
||||||
|
yuv := img.(*image.YCbCr)
|
||||||
|
yuv.Cb, yuv.Cr = yuv.Cr, yuv.Cb
|
||||||
|
return yuv, release, err
|
||||||
|
}
|
||||||
|
@@ -17,7 +17,7 @@ func decodeYUY2(frame []byte, width, height int) (image.Image, func(), error) {
|
|||||||
ci := yi / 2
|
ci := yi / 2
|
||||||
fi := yi + 2*ci
|
fi := yi + 2*ci
|
||||||
|
|
||||||
if len(frame) != fi {
|
if len(frame) < fi {
|
||||||
return nil, func() {}, fmt.Errorf("frame length (%d) less than expected (%d)", len(frame), fi)
|
return nil, func() {}, fmt.Errorf("frame length (%d) less than expected (%d)", len(frame), fi)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ func decodeUYVY(frame []byte, width, height int) (image.Image, func(), error) {
|
|||||||
ci := yi / 2
|
ci := yi / 2
|
||||||
fi := yi + 2*ci
|
fi := yi + 2*ci
|
||||||
|
|
||||||
if len(frame) != fi {
|
if len(frame) < fi {
|
||||||
return nil, func() {}, fmt.Errorf("frame length (%d) less than expected (%d)", len(frame), fi)
|
return nil, func() {}, fmt.Errorf("frame length (%d) less than expected (%d)", len(frame), fi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@ func decodeYUY2(frame []byte, width, height int) (image.Image, func(), error) {
|
|||||||
ci := yi / 2
|
ci := yi / 2
|
||||||
fi := yi + 2*ci
|
fi := yi + 2*ci
|
||||||
|
|
||||||
if len(frame) != fi {
|
if len(frame) < fi {
|
||||||
return nil, func() {}, fmt.Errorf("frame length (%d) less than expected (%d)", len(frame), fi)
|
return nil, func() {}, fmt.Errorf("frame length (%d) less than expected (%d)", len(frame), fi)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ func decodeUYVY(frame []byte, width, height int) (image.Image, func(), error) {
|
|||||||
ci := yi / 2
|
ci := yi / 2
|
||||||
fi := yi + 2*ci
|
fi := yi + 2*ci
|
||||||
|
|
||||||
if len(frame) != fi {
|
if len(frame) < fi {
|
||||||
return nil, func() {}, fmt.Errorf("frame length (%d) less than expected (%d)", len(frame), fi)
|
return nil, func() {}, fmt.Errorf("frame length (%d) less than expected (%d)", len(frame), fi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -27,7 +27,11 @@ func TestDecodeYUY2(t *testing.T) {
|
|||||||
Rect: image.Rect(0, 0, width, height),
|
Rect: image.Rect(0, 0, width, height),
|
||||||
}
|
}
|
||||||
|
|
||||||
img, _, err := decodeYUY2(input, width, height)
|
decoder, err := NewDecoder(FormatYUY2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
img, _, err := decoder.Decode(input, width, height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -56,7 +60,77 @@ func TestDecodeUYVY(t *testing.T) {
|
|||||||
Rect: image.Rect(0, 0, width, height),
|
Rect: image.Rect(0, 0, width, height),
|
||||||
}
|
}
|
||||||
|
|
||||||
img, _, err := decodeUYVY(input, width, height)
|
decoder, err := NewDecoder(FormatUYVY)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
img, _, err := decoder.Decode(input, width, height)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(expected, img) {
|
||||||
|
t.Errorf("Wrong decode result,\nexpected:\n%+v\ngot:\n%+v", expected, img)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeNV21(t *testing.T) {
|
||||||
|
const (
|
||||||
|
width = 2
|
||||||
|
height = 2
|
||||||
|
)
|
||||||
|
input := []byte{
|
||||||
|
0x01, 0x03, 0x05, 0x07, // Y
|
||||||
|
// Cr Cb
|
||||||
|
0x82, 0x84,
|
||||||
|
}
|
||||||
|
expected := &image.YCbCr{
|
||||||
|
Y: []byte{0x01, 0x03, 0x05, 0x07},
|
||||||
|
YStride: width,
|
||||||
|
Cb: []byte{0x84},
|
||||||
|
Cr: []byte{0x82},
|
||||||
|
CStride: width / 2,
|
||||||
|
SubsampleRatio: image.YCbCrSubsampleRatio420,
|
||||||
|
Rect: image.Rect(0, 0, width, height),
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder, err := NewDecoder(FormatNV21)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
img, _, err := decoder.Decode(input, width, height)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(expected, img) {
|
||||||
|
t.Errorf("Wrong decode result,\nexpected:\n%+v\ngot:\n%+v", expected, img)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeNV12(t *testing.T) {
|
||||||
|
const (
|
||||||
|
width = 2
|
||||||
|
height = 2
|
||||||
|
)
|
||||||
|
input := []byte{
|
||||||
|
0x01, 0x03, 0x05, 0x07, // Y
|
||||||
|
// Cb Cr
|
||||||
|
0x84, 0x82,
|
||||||
|
}
|
||||||
|
expected := &image.YCbCr{
|
||||||
|
Y: []byte{0x01, 0x03, 0x05, 0x07},
|
||||||
|
YStride: width,
|
||||||
|
Cb: []byte{0x84},
|
||||||
|
Cr: []byte{0x82},
|
||||||
|
CStride: width / 2,
|
||||||
|
SubsampleRatio: image.YCbCrSubsampleRatio420,
|
||||||
|
Rect: image.Rect(0, 0, width, height),
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder, err := NewDecoder(FormatNV12)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
img, _, err := decoder.Decode(input, width, height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
36
pkg/frame/z16.go
Normal file
36
pkg/frame/z16.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package frame
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
func decodeZ16(frame []byte, width, height int) (image.Image, func(), error) {
|
||||||
|
expectedSize := 2 * (width * height)
|
||||||
|
if expectedSize != len(frame) {
|
||||||
|
return nil, func() {}, fmt.Errorf("frame length (%d) not expected size (%d)", len(frame), expectedSize)
|
||||||
|
}
|
||||||
|
img := image.NewGray16(image.Rect(0, 0, width, height))
|
||||||
|
/*
|
||||||
|
v4l specifies images in terms of series of lines which is perplexing because the
|
||||||
|
example in https://www.kernel.org/doc/html/v5.9/userspace-api/media/v4l/pixfmt-z16.html
|
||||||
|
seems to swap the subscript of each depth value.
|
||||||
|
|
||||||
|
Clear example:
|
||||||
|
Width: 3, Height: 4
|
||||||
|
[Z_low(x_0,y_0), Z_high(x_0,y_0), Z_low(x_1,y_0), Z_high(x_1,y_0), Z_low(x_2,y_0), Z_high(x_2,y_0),
|
||||||
|
Z_low(x_0,y_1), Z_high(x_0,y_1), Z_low(x_1,y_1), Z_high(x_1,y_1), Z_low(x_2,y_1), Z_high(x_2,y_1),
|
||||||
|
Z_low(x_0,y_2), Z_high(x_0,y_2), Z_low(x_1,y_2), Z_high(x_1,y_2), Z_low(x_2,y_2), Z_high(x_2,y_2),
|
||||||
|
Z_low(x_0,y_3), Z_high(x_0,y_3), Z_low(x_1,y_3), Z_high(x_1,y_3), Z_low(x_2,y_3), Z_high(x_2,y_3)]
|
||||||
|
*/
|
||||||
|
for x := 0; x < width; x++ {
|
||||||
|
for y := 0; y < height; y++ {
|
||||||
|
idx := 2 * (x + (y * width))
|
||||||
|
z := binary.LittleEndian.Uint16(frame[idx : idx+2])
|
||||||
|
img.SetGray16(x, y, color.Gray16{Y: z})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return img, func() {}, nil
|
||||||
|
}
|
45
pkg/frame/z16_test.go
Normal file
45
pkg/frame/z16_test.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package frame
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDecodeZ16(t *testing.T) {
|
||||||
|
const (
|
||||||
|
width = 2
|
||||||
|
height = 3
|
||||||
|
)
|
||||||
|
decoder, err := NewDecoder(FormatZ16)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
img, _, err := decoder.Decode([]byte{0x00}, width, height)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected to get a frame length mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
input := []byte{
|
||||||
|
0x0c, 0x00, 0x20, 0x03,
|
||||||
|
0xa3, 0x01, 0x10, 0x00,
|
||||||
|
0x56, 0x09, 0x5d, 0x00,
|
||||||
|
}
|
||||||
|
expected := image.NewGray16(image.Rect(0, 0, width, height))
|
||||||
|
expected.Stride = width * 2
|
||||||
|
expected.SetGray16(0, 0, color.Gray16{Y: 12})
|
||||||
|
expected.SetGray16(1, 0, color.Gray16{Y: 800})
|
||||||
|
expected.SetGray16(0, 1, color.Gray16{Y: 419})
|
||||||
|
expected.SetGray16(1, 1, color.Gray16{Y: 16})
|
||||||
|
expected.SetGray16(0, 2, color.Gray16{Y: 2390})
|
||||||
|
expected.SetGray16(1, 2, color.Gray16{Y: 93})
|
||||||
|
|
||||||
|
img, _, err = decoder.Decode(input, width, height)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(expected, img) {
|
||||||
|
t.Errorf("Wrong decode result,\nexpected:\n%+v\ngot:\n%+v", expected, img)
|
||||||
|
}
|
||||||
|
}
|
@@ -107,12 +107,14 @@ func TestBroadcast(t *testing.T) {
|
|||||||
|
|
||||||
fps := float64(count) / duration.Seconds()
|
fps := float64(count) / duration.Seconds()
|
||||||
if fps < pauseCond.expectedFPS-2 || fps > pauseCond.expectedFPS+2 {
|
if fps < pauseCond.expectedFPS-2 || fps > pauseCond.expectedFPS+2 {
|
||||||
t.Fatal("Unexpected average FPS")
|
t.Error("Unexpected average FPS")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
droppedFramesPerSecond := float64(droppedFrames) / duration.Seconds()
|
droppedFramesPerSecond := float64(droppedFrames) / duration.Seconds()
|
||||||
if droppedFramesPerSecond < pauseCond.expectedDrop-2 || droppedFramesPerSecond > pauseCond.expectedDrop+2 {
|
if droppedFramesPerSecond < pauseCond.expectedDrop-2 || droppedFramesPerSecond > pauseCond.expectedDrop+2 {
|
||||||
t.Fatal("Unexpected drop count")
|
t.Error("Unexpected drop count")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fpsChan <- []float64{fps, droppedFramesPerSecond, float64(lastFrameCount)}
|
fpsChan <- []float64{fps, droppedFramesPerSecond, float64(lastFrameCount)}
|
||||||
|
@@ -7,6 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// #include "convert_cgo.h"
|
// #include "convert_cgo.h"
|
||||||
|
// #cgo CFLAGS: -std=c11
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
// CGO version of the functions will be selected at runtime.
|
// CGO version of the functions will be selected at runtime.
|
||||||
|
@@ -144,9 +144,11 @@ func newInt16InterleavedDecoder() (Decoder, Format) {
|
|||||||
container := NewInt16Interleaved(chunkInfo)
|
container := NewInt16Interleaved(chunkInfo)
|
||||||
|
|
||||||
if endian == hostEndian {
|
if endian == hostEndian {
|
||||||
|
data := container.Data
|
||||||
|
dst := *(*[]byte)(unsafe.Pointer(&data))
|
||||||
|
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&dst))
|
||||||
n := len(chunk)
|
n := len(chunk)
|
||||||
h := reflect.SliceHeader{Data: uintptr(unsafe.Pointer(&container.Data[0])), Len: n, Cap: n}
|
hdr.Len, hdr.Cap = n, n
|
||||||
dst := *(*[]byte)(unsafe.Pointer(&h))
|
|
||||||
copy(dst, chunk)
|
copy(dst, chunk)
|
||||||
return container, nil
|
return container, nil
|
||||||
}
|
}
|
||||||
@@ -188,9 +190,11 @@ func newInt16NonInterleavedDecoder() (Decoder, Format) {
|
|||||||
|
|
||||||
if endian == hostEndian {
|
if endian == hostEndian {
|
||||||
for ch := 0; ch < channels; ch++ {
|
for ch := 0; ch < channels; ch++ {
|
||||||
|
data := container.Data[ch]
|
||||||
|
dst := *(*[]byte)(unsafe.Pointer(&data))
|
||||||
|
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&dst))
|
||||||
|
hdr.Len, hdr.Cap = chunkLen, chunkLen
|
||||||
offset := ch * chunkLen
|
offset := ch * chunkLen
|
||||||
h := reflect.SliceHeader{Data: uintptr(unsafe.Pointer(&container.Data[ch][0])), Len: chunkLen, Cap: chunkLen}
|
|
||||||
dst := *(*[]byte)(unsafe.Pointer(&h))
|
|
||||||
copy(dst, chunk[offset:offset+chunkLen])
|
copy(dst, chunk[offset:offset+chunkLen])
|
||||||
}
|
}
|
||||||
return container, nil
|
return container, nil
|
||||||
@@ -228,9 +232,11 @@ func newFloat32InterleavedDecoder() (Decoder, Format) {
|
|||||||
container := NewFloat32Interleaved(chunkInfo)
|
container := NewFloat32Interleaved(chunkInfo)
|
||||||
|
|
||||||
if endian == hostEndian {
|
if endian == hostEndian {
|
||||||
|
data := container.Data
|
||||||
|
dst := *(*[]byte)(unsafe.Pointer(&data))
|
||||||
|
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&dst))
|
||||||
n := len(chunk)
|
n := len(chunk)
|
||||||
h := reflect.SliceHeader{Data: uintptr(unsafe.Pointer(&container.Data[0])), Len: n, Cap: n}
|
hdr.Len, hdr.Cap = n, n
|
||||||
dst := *(*[]byte)(unsafe.Pointer(&h))
|
|
||||||
copy(dst, chunk)
|
copy(dst, chunk)
|
||||||
return container, nil
|
return container, nil
|
||||||
}
|
}
|
||||||
@@ -272,9 +278,11 @@ func newFloat32NonInterleavedDecoder() (Decoder, Format) {
|
|||||||
|
|
||||||
if endian == hostEndian {
|
if endian == hostEndian {
|
||||||
for ch := 0; ch < channels; ch++ {
|
for ch := 0; ch < channels; ch++ {
|
||||||
|
data := container.Data[ch]
|
||||||
|
dst := *(*[]byte)(unsafe.Pointer(&data))
|
||||||
|
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&dst))
|
||||||
|
hdr.Len, hdr.Cap = chunkLen, chunkLen
|
||||||
offset := ch * chunkLen
|
offset := ch * chunkLen
|
||||||
h := reflect.SliceHeader{Data: uintptr(unsafe.Pointer(&container.Data[ch][0])), Len: chunkLen, Cap: chunkLen}
|
|
||||||
dst := *(*[]byte)(unsafe.Pointer(&h))
|
|
||||||
copy(dst, chunk[offset:offset+chunkLen])
|
copy(dst, chunk[offset:offset+chunkLen])
|
||||||
}
|
}
|
||||||
return container, nil
|
return container, nil
|
||||||
|
0
scripts/.gitkeep
Normal file
0
scripts/.gitkeep
Normal file
33
track.go
33
track.go
@@ -64,6 +64,8 @@ type Track interface {
|
|||||||
Unbind(webrtc.TrackLocalContext) error
|
Unbind(webrtc.TrackLocalContext) error
|
||||||
// NewRTPReader creates a new reader from the source. The reader will encode the source, and packetize
|
// NewRTPReader creates a new reader from the source. The reader will encode the source, and packetize
|
||||||
// the encoded data in RTP format with given mtu size.
|
// the encoded data in RTP format with given mtu size.
|
||||||
|
//
|
||||||
|
// Note: `mtu int` will be changed to `mtu uint16` in a future update.
|
||||||
NewRTPReader(codecName string, ssrc uint32, mtu int) (RTPReadCloser, error)
|
NewRTPReader(codecName string, ssrc uint32, mtu int) (RTPReadCloser, error)
|
||||||
// NewEncodedReader creates a EncodedReadCloser that reads the encoded data in codecName format
|
// NewEncodedReader creates a EncodedReadCloser that reads the encoded data in codecName format
|
||||||
NewEncodedReader(codecName string) (EncodedReadCloser, error)
|
NewEncodedReader(codecName string) (EncodedReadCloser, error)
|
||||||
@@ -209,21 +211,30 @@ func (track *baseTrack) bind(ctx webrtc.TrackLocalContext, specializedTrack Trac
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (track *baseTrack) unbind(ctx webrtc.TrackLocalContext) error {
|
func (track *baseTrack) unbind(ctx webrtc.TrackLocalContext) error {
|
||||||
track.mu.Lock()
|
ch, err := track.removeActivePeerConnection(ctx.ID())
|
||||||
defer track.mu.Unlock()
|
if err != err {
|
||||||
|
return err
|
||||||
ch, ok := track.activePeerConnections[ctx.ID()]
|
|
||||||
if !ok {
|
|
||||||
return errNotFoundPeerConnection
|
|
||||||
}
|
}
|
||||||
|
|
||||||
doneCh := make(chan struct{})
|
doneCh := make(chan struct{})
|
||||||
ch <- doneCh
|
ch <- doneCh
|
||||||
<-doneCh
|
<-doneCh
|
||||||
delete(track.activePeerConnections, ctx.ID())
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (track *baseTrack) removeActivePeerConnection(id string) (chan<- chan<- struct{}, error) {
|
||||||
|
track.mu.Lock()
|
||||||
|
defer track.mu.Unlock()
|
||||||
|
|
||||||
|
ch, ok := track.activePeerConnections[id]
|
||||||
|
if !ok {
|
||||||
|
return nil, errNotFoundPeerConnection
|
||||||
|
}
|
||||||
|
delete(track.activePeerConnections, id)
|
||||||
|
|
||||||
|
return ch, nil
|
||||||
|
}
|
||||||
|
|
||||||
func newTrackFromDriver(d driver.Driver, constraints MediaTrackConstraints, selector *CodecSelector) (Track, error) {
|
func newTrackFromDriver(d driver.Driver, constraints MediaTrackConstraints, selector *CodecSelector) (Track, error) {
|
||||||
if err := d.Open(); err != nil {
|
if err := d.Open(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -339,7 +350,7 @@ func (track *VideoTrack) NewRTPReader(codecName string, ssrc uint32, mtu int) (R
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
packetizer := rtp.NewPacketizer(mtu, uint8(selectedCodec.PayloadType), ssrc, selectedCodec.Payloader, rtp.NewRandomSequencer(), selectedCodec.ClockRate)
|
packetizer := rtp.NewPacketizer(uint16(mtu), uint8(selectedCodec.PayloadType), ssrc, selectedCodec.Payloader, rtp.NewRandomSequencer(), selectedCodec.ClockRate)
|
||||||
|
|
||||||
return &rtpReadCloserImpl{
|
return &rtpReadCloserImpl{
|
||||||
readFn: func() ([]*rtp.Packet, func(), error) {
|
readFn: func() ([]*rtp.Packet, func(), error) {
|
||||||
@@ -365,7 +376,7 @@ type AudioTrack struct {
|
|||||||
*audio.Broadcaster
|
*audio.Broadcaster
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAudioTrack constructs a new VideoTrack
|
// NewAudioTrack constructs a new AudioTrack
|
||||||
func NewAudioTrack(source AudioSource, selector *CodecSelector) Track {
|
func NewAudioTrack(source AudioSource, selector *CodecSelector) Track {
|
||||||
return newAudioTrackFromReader(source, source, selector)
|
return newAudioTrackFromReader(source, source, selector)
|
||||||
}
|
}
|
||||||
@@ -425,7 +436,7 @@ func (track *AudioTrack) newEncodedReader(codecNames ...string) (EncodedReadClos
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sample := newAudioSampler(selectedCodec.ClockRate, inputProp.Latency)
|
sample := newAudioSampler(selectedCodec.ClockRate, selectedCodec.Latency)
|
||||||
|
|
||||||
return &encodedReadCloserImpl{
|
return &encodedReadCloserImpl{
|
||||||
readFn: func() (EncodedBuffer, func(), error) {
|
readFn: func() (EncodedBuffer, func(), error) {
|
||||||
@@ -459,7 +470,7 @@ func (track *AudioTrack) NewRTPReader(codecName string, ssrc uint32, mtu int) (R
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
packetizer := rtp.NewPacketizer(mtu, uint8(selectedCodec.PayloadType), ssrc, selectedCodec.Payloader, rtp.NewRandomSequencer(), selectedCodec.ClockRate)
|
packetizer := rtp.NewPacketizer(uint16(mtu), uint8(selectedCodec.PayloadType), ssrc, selectedCodec.Payloader, rtp.NewRandomSequencer(), selectedCodec.ClockRate)
|
||||||
|
|
||||||
return &rtpReadCloserImpl{
|
return &rtpReadCloserImpl{
|
||||||
readFn: func() ([]*rtp.Packet, func(), error) {
|
readFn: func() ([]*rtp.Packet, func(), error) {
|
||||||
|
Reference in New Issue
Block a user