Repository: pion/rtp
Pull request #53 (2020-04-30 07:01:57)

Update CI configs to v0.3.1
Update lint scripts and CI configs.

codecov[bot]: # [Codecov](https://codecov.io/gh/pion/rtp/pull/53?src=pr&el=h1) Report
> Merging [#53](https://codecov.io/gh/pion/rtp/pull/53?src=pr&el=desc) into [master](https://codecov.io/gh/pion/rtp/commit/8c17fece3019701b86f7ebfa70faa66c96fe8156&el=desc) will **not change** coverage.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/pion/rtp/pull/53/graphs/tree.svg?width=650&height=150&src=pr&token=4KrbiBsspj)](https://codecov.io/gh/pion/rtp/pull/53?src=pr&el=tree)

```diff
@@           Coverage Diff           @@
##           master      #53   +/-   ##
=======================================
  Coverage   87.12%   87.12%           
=======================================
  Files          12       12           
  Lines         497      497           
=======================================
  Hits          433      433           
  Misses         50       50           
  Partials       14       14           
```

| Flag | Coverage Δ | |
|---|---|---|
| #go | `87.12% <ø> (ø)` | |
| #wasm | `87.12% <ø> (ø)` | |


------

[Continue to review full report at Codecov](https://codecov.io/gh/pion/rtp/pull/53?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pion/rtp/pull/53?src=pr&el=footer). Last update [8c17fec...22893b8](https://codecov.io/gh/pion/rtp/pull/53?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/.github/assert-contributors.sh b/.github/assert-contributors.sh
index 915f680..470d817 100755
--- a/.github/assert-contributors.sh
+++ b/.github/assert-contributors.sh
@@ -16,11 +16,16 @@ fi
 
 SCRIPT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )
 
-EXCLUDED_CONTIBUTORS=('John R. Bradley' 'renovate[bot]' 'Renovate Bot' 'Pion Bot')
-MISSING_CONTIBUTORS=()
+if [ -f ${SCRIPT_PATH}/.ci.conf ]
+then
+  . ${SCRIPT_PATH}/.ci.conf
+fi
+
+EXCLUDED_CONTRIBUTORS+=('John R. Bradley' 'renovate[bot]' 'Renovate Bot' 'Pion Bot')
+MISSING_CONTRIBUTORS=()
 
 shouldBeIncluded () {
-	for i in "${EXCLUDED_CONTIBUTORS[@]}"
+	for i in "${EXCLUDED_CONTRIBUTORS[@]}"
 	do
 		if [ "$i" == "$1" ] ; then
 			return 1
@@ -35,15 +40,15 @@ for contributor in $(git log --format='%aN' | sort -u)
 do
 	if shouldBeIncluded $contributor; then
 		if ! grep -q "$contributor" "$SCRIPT_PATH/../README.md"; then
-			MISSING_CONTIBUTORS+=("$contributor")
+			MISSING_CONTRIBUTORS+=("$contributor")
 		fi
 	fi
 done
 unset IFS
 
-if [ ${#MISSING_CONTIBUTORS[@]} -ne 0 ]; then
+if [ ${#MISSING_CONTRIBUTORS[@]} -ne 0 ]; then
     echo "Please add the following contributors to the README"
-    for i in "${MISSING_CONTIBUTORS[@]}"
+    for i in "${MISSING_CONTRIBUTORS[@]}"
     do
 	    echo "$i"
     done
diff --git a/.github/lint-disallowed-functions-in-library.sh b/.github/lint-disallowed-functions-in-library.sh
index f5c9d87..6d5d55a 100755
--- a/.github/lint-disallowed-functions-in-library.sh
+++ b/.github/lint-disallowed-functions-in-library.sh
@@ -10,13 +10,35 @@ set -e
 
 # Disallow usages of functions that cause the program to exit in the library code
 SCRIPT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )
-EXCLUDE_DIRECTORIES="--exclude-dir=examples --exclude-dir=.git --exclude-dir=.github "
+if [ -f ${SCRIPT_PATH}/.ci.conf ]
+then
+  . ${SCRIPT_PATH}/.ci.conf
+fi
+
+EXCLUDE_DIRECTORIES=${DISALLOWED_FUNCTIONS_EXCLUDED_DIRECTORIES:-"examples"}
 DISALLOWED_FUNCTIONS=('os.Exit(' 'panic(' 'Fatal(' 'Fatalf(' 'Fatalln(' 'fmt.Println(' 'fmt.Printf(' 'log.Print(' 'log.Println(' 'log.Printf(')
 
+files=$(
+  find "$SCRIPT_PATH/.." -name "*.go" \
+    | grep -v -e '^.*_test.go$' \
+    | while read file
+    do
+      excluded=false
+      for ex in $EXCLUDE_DIRECTORIES
+      do
+        if [[ $file == */$ex/* ]]
+        then
+          excluded=true
+          break
+        fi
+      done
+      $excluded || echo "$file"
+    done
+)
 
 for disallowedFunction in "${DISALLOWED_FUNCTIONS[@]}"
 do
-	if grep -R $EXCLUDE_DIRECTORIES -e "$disallowedFunction" "$SCRIPT_PATH/.." | grep -v -e '_test.go' -e 'nolint'; then
+	if grep -e "$disallowedFunction" $files | grep -v -e 'nolint'; then
 		echo "$disallowedFunction may only be used in example code"
 		exit 1
 	fi
diff --git a/.travis.yml b/.travis.yml
index e2bb205..e464e84 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -28,11 +28,13 @@ cache:
 
 _lint_job: &lint_job
   env: CACHE_NAME=lint
+  before_install:
+    - if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi
+  install: skip
   before_script:
     - |
       curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \
         | bash -s - -b $GOPATH/bin v${GOLANGCI_LINT_VERSION}
-  install: skip
   script:
     - bash .github/assert-contributors.sh
     - bash .github/lint-disallowed-functions-in-library.sh
@@ -42,23 +44,28 @@ _lint_job: &lint_job
 _test_job: &test_job
   env: CACHE_NAME=test
   before_install:
+    - if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi
     - go mod download
   install:
     - go build ./...
   script:
-    - coverpkgs=$(go list ./... | grep -v examples | paste -s -d ',')
+    - testpkgs=${TEST_PACKAGES:-$(go list ./... | grep -v examples)}
+    - coverpkgs=$(echo "${testpkgs}" | paste -s -d ',')
     - |
       go test \
         -coverpkg=${coverpkgs} -coverprofile=cover.out -covermode=atomic \
-        -tags quic \
-        -v -race ./...
+        ${TEST_EXTRA_ARGS:-} \
+        -v -race ${testpkgs}
+    - if [ -n "${TEST_HOOK}" ]; then ${TEST_HOOK}; fi
   after_success:
     - travis_retry bash <(curl -s https://codecov.io/bash) -c -F go
 _test_i386_job: &test_i386_job
   env: CACHE_NAME=test386
-  language: bash
   services: docker
+  before_install:
+    - if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi
   script:
+    - testpkgs=${TEST_PACKAGES:-$(go list ./... | grep -v examples)}
     - |
       docker run \
         -u $(id -u):$(id -g) \
@@ -70,12 +77,15 @@ _test_i386_job: &test_i386_job
         -w /go/src/github.com/pion/$(basename ${PWD}) \
         -it i386/golang:${GO_VERSION}-alpine \
         /usr/local/go/bin/go test \
-          -tags quic \
-          -v ./...
+          ${TEST_EXTRA_ARGS:-} \
+          -v ${testpkgs}
 _test_wasm_job: &test_wasm_job
   env: CACHE_NAME=wasm
   language: node_js
   node_js: 12
+  before_install:
+    - if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi
+    - if ${SKIP_WASM_TEST:-false}; then exit 0; fi
   install:
     # Manually download and install Go instead of using gimme.
     # It looks like gimme Go causes some errors on go-test for Wasm.
@@ -83,15 +93,10 @@ _test_wasm_job: &test_wasm_job
     - export GOROOT=${HOME}/go
     - export PATH=${GOROOT}/bin:${PATH}
     - yarn install
-    - export GO_JS_WASM_EXEC=${GOROOT}/misc/wasm/go_js_wasm_exec
-    # If the repository has wasm_exec hook, use it.
-    - |
-      if [ -f test-wasm/go_js_wasm_exec ]; then
-        export GO_JS_WASM_EXEC=${PWD}/test-wasm/go_js_wasm_exec
-      fi
+    - export GO_JS_WASM_EXEC=${GO_JS_WASM_EXEC:-${GOROOT}/misc/wasm/go_js_wasm_exec}
   script:
-    - testpkgs=$(go list ./... | grep -v examples)
-    - coverpkgs=$(go list ./... | grep -v examples | paste -s -d ',')
+    - testpkgs=${TEST_PACKAGES:-$(go list ./... | grep -v examples)}
+    - coverpkgs=$(echo "${testpkgs}" | paste -s -d ',')
     - |
       GOOS=js GOARCH=wasm go test \
         -coverpkg=${coverpkgs} -coverprofile=cover.out -covermode=atomic \
@@ -114,9 +119,11 @@ jobs:
     - <<: *test_i386_job
       name: Test i386 1.13
       env: GO_VERSION=1.13
+      go: 1.14  # version for host environment used to go list
     - <<: *test_i386_job
       name: Test i386 1.14
       env: GO_VERSION=1.14
+      go: 1.14  # version for host environment used to go list
     - <<: *test_wasm_job
       name: Test WASM 1.13
       env: GO_VERSION=1.13
diff --git a/README.md b/README.md
index 8296629..0796a5c 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,7 @@ Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contribu
 * [Atsushi Watanabe](https://github.com/at-wat)
 * [adwpc](https://github.com/adwpc) *add transport-cc extension*
 * [Bao Nguyen](https://github.com/sysbot) *add VP9 noop, bug fixes.
+* [Tarrence van As](https://github.com/tarrencev) *add audio level extension*
 
 ### License
 MIT License - see [LICENSE](LICENSE) for full text
diff --git a/audiolevelextension.go b/audiolevelextension.go
new file mode 100644
index 0000000..f85f6b0
--- /dev/null
+++ b/audiolevelextension.go
@@ -0,0 +1,82 @@
+package rtp
+
+import (
+	"errors"
+)
+
+const (
+	// AudioLevelOneByteExtensionSize One byte header size
+	AudioLevelOneByteExtensionSize = 2
+	// AudioLevelTwoByteExtensionSize Two byte header size
+	AudioLevelTwoByteExtensionSize = 4
+)
+
+var (
+	errInvalidSize           = errors.New("invalid buffer size")
+	errInvalidExtensonLength = errors.New("invalid extension length")
+	errAudioLevelOverflow    = errors.New("audio level overflow")
+)
+
+// AudioLevelExtension is a extension payload format described in
+// https://tools.ietf.org/html/rfc6464
+//
+// Implementation based on:
+// https://chromium.googlesource.com/external/webrtc/+/e2a017725570ead5946a4ca8235af27470ca0df9/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc#49
+//
+// One byte format:
+// 0                   1
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |  ID   | len=0 |V| level       |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// Two byte format:
+// 0                   1                   2                   3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |      ID       |     len=1     |V|    level    |    0 (pad)    |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+type AudioLevelExtension struct {
+	ID    uint8
+	Level uint8
+	Voice bool
+}
+
+// Marshal serializes the members to buffer
+func (a *AudioLevelExtension) Marshal() ([]byte, error) {
+	if a.Level > 127 {
+		return nil, errAudioLevelOverflow
+	}
+	voice := uint8(0x00)
+	if a.Voice {
+		voice = 0x80
+	}
+	buf := make([]byte, AudioLevelOneByteExtensionSize)
+	buf[0] = a.ID << 4 & 0xf0
+	buf[1] = voice | a.Level
+	return buf, nil
+}
+
+// Unmarshal parses the passed byte slice and stores the result in the members
+func (a *AudioLevelExtension) Unmarshal(rawData []byte) error {
+	// one byte format
+	switch len(rawData) {
+	case AudioLevelOneByteExtensionSize:
+		if rawData[0]&^0xF0 != 0 {
+			return errInvalidExtensonLength
+		}
+		a.ID = rawData[0] >> 4
+		a.Level = rawData[1] & 0x7F
+		a.Voice = rawData[1]&0x80 != 0
+		return nil
+	case AudioLevelTwoByteExtensionSize:
+		if rawData[1] != 1 {
+			return errInvalidExtensonLength
+		}
+		a.ID = rawData[0]
+		a.Level = rawData[2] & 0x7F
+		a.Voice = rawData[2]&0x80 != 0
+		return nil
+	}
+	return errInvalidSize
+}


Test patch:
diff --git a/audiolevelextension_test.go b/audiolevelextension_test.go
new file mode 100644
index 0000000..0314eb2
--- /dev/null
+++ b/audiolevelextension_test.go
@@ -0,0 +1,149 @@
+package rtp
+
+import (
+	"bytes"
+	"testing"
+)
+
+func TestAudioLevelExtensionTooSmall(t *testing.T) {
+	a := AudioLevelExtension{}
+
+	rawData := []byte{}
+
+	if err := a.Unmarshal(rawData); err != errInvalidSize {
+		t.Fatal("err != errInvalidSize")
+	}
+}
+
+func TestAudioLevelExtensionTooBig(t *testing.T) {
+	a := AudioLevelExtension{}
+
+	rawData := []byte{
+		0x00, 0x00, 0x00, 0x00, 0x00,
+	}
+
+	if err := a.Unmarshal(rawData); err != errInvalidSize {
+		t.Fatal("err != errInvalidSize")
+	}
+}
+
+func TestAudioLevelOneByteExtensionInvalidLength(t *testing.T) {
+	a := AudioLevelExtension{}
+
+	rawData := []byte{
+		0x31, 0x88,
+	}
+
+	if err := a.Unmarshal(rawData); err != errInvalidExtensonLength {
+		t.Fatal("err != errInvalidExtensonLength")
+	}
+}
+
+func TestAudioLevelTwoByteExtensionInvalidLength(t *testing.T) {
+	a := AudioLevelExtension{}
+
+	rawData := []byte{
+		0x30, 0x00, 0x00, 0x00,
+	}
+
+	if err := a.Unmarshal(rawData); err != errInvalidExtensonLength {
+		t.Fatal("err != errInvalidExtensonLength")
+	}
+}
+
+func TestAudioLevelOneByteExtensionVoiceTrue(t *testing.T) {
+	a1 := AudioLevelExtension{}
+
+	rawData := []byte{
+		0x30, 0x88,
+	}
+
+	if err := a1.Unmarshal(rawData); err != nil {
+		t.Fatal("Unmarshal error on extension data")
+	}
+
+	a2 := AudioLevelExtension{
+		ID:    3,
+		Level: 8,
+		Voice: true,
+	}
+
+	if a1 != a2 {
+		t.Error("Unmarshal failed")
+	}
+
+	dstData, _ := a2.Marshal()
+	if !bytes.Equal(dstData, rawData) {
+		t.Error("Marshal failed")
+	}
+}
+
+func TestAudioLevelOneByteExtensionVoiceFalse(t *testing.T) {
+	a1 := AudioLevelExtension{}
+
+	rawData := []byte{
+		0x30, 0x8,
+	}
+
+	if err := a1.Unmarshal(rawData); err != nil {
+		t.Fatal("Unmarshal error on extension data")
+	}
+
+	a2 := AudioLevelExtension{
+		ID:    3,
+		Level: 8,
+		Voice: false,
+	}
+
+	if a1 != a2 {
+		t.Error("Unmarshal failed")
+	}
+
+	dstData, _ := a2.Marshal()
+	if !bytes.Equal(dstData, rawData) {
+		t.Error("Marshal failed")
+	}
+}
+
+func TestAudioLevelOneByteExtensionLevelOverflow(t *testing.T) {
+	a := AudioLevelExtension{
+		ID:    3,
+		Level: 128,
+		Voice: false,
+	}
+
+	if _, err := a.Marshal(); err != errAudioLevelOverflow {
+		t.Fatal("err != errAudioLevelOverflow")
+	}
+}
+
+func TestAudioLevelTwoByteExtensionVoiceFalse(t *testing.T) {
+	a1 := AudioLevelExtension{}
+
+	oneByteRawData := []byte{
+		0x30, 0x8,
+	}
+
+	twoByteRawData := []byte{
+		0x3, 0x1, 0x8, 0x0,
+	}
+
+	if err := a1.Unmarshal(twoByteRawData); err != nil {
+		t.Fatal("Unmarshal error on extension data")
+	}
+
+	a2 := AudioLevelExtension{
+		ID:    3,
+		Level: 8,
+		Voice: false,
+	}
+
+	if a1 != a2 {
+		t.Error("Unmarshal failed")
+	}
+
+	dstData, _ := a2.Marshal()
+	if !bytes.Equal(dstData, oneByteRawData) {
+		t.Error("Marshal failed")
+	}
+}