Implement the GstNavigation interface using a dedicated DataChannel protocol

This allows interacting with the source element from within the browser
very easily
This commit is contained in:
Thibault Saunier 2021-12-24 12:26:26 +00:00 committed by Mathieu Duponchelle
parent 7b66b21f47
commit 689bd93055
9 changed files with 3589 additions and 249 deletions

203
Cargo.lock generated
View file

@ -13,9 +13,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.48"
version = "1.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62e1f47f7dc0422027a4e370dd4548d4d66b26782e513e98dca1e689e058a80e"
checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
[[package]]
name = "async-channel"
@ -160,9 +160,9 @@ checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0"
[[package]]
name = "async-tungstenite"
version = "0.16.0"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0d06e9a20f1c0d64b6067ef6aa9fdf59e194ecde93575591fb4c78063692324"
checksum = "5682ea0913e5c20780fe5785abacb85a411e7437bf52a1bedb93ddb3972cb8dd"
dependencies = [
"async-native-tls",
"async-std",
@ -251,9 +251,9 @@ checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]]
name = "cache-padded"
version = "1.1.1"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba"
checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
[[package]]
name = "cc"
@ -290,9 +290,9 @@ dependencies = [
[[package]]
name = "clap"
version = "3.0.0-rc.1"
version = "3.0.0-rc.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c068998524e6d40ea78c8d2a4b00398f0a8b818c2d484bcb3cbeb2cff2c105ae"
checksum = "e6f243c7279f09ffed852a0a564c72091331651484cdbb32b7287f16df8611a7"
dependencies = [
"atty",
"bitflags",
@ -307,11 +307,11 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "3.0.0-rc.1"
version = "3.0.0-rc.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0152ba3ee01fa5a9133d4e15a1d9659c75d2270365768dd5a880cc7e68871874"
checksum = "8cd9992739777a4a23535089a8d235eac43044ba8b431d9f54fe334dfa779930"
dependencies = [
"heck",
"heck 0.3.3",
"proc-macro-error",
"proc-macro2",
"quote",
@ -389,9 +389,9 @@ checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59"
[[package]]
name = "fastrand"
version = "1.5.0"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e"
checksum = "779d043b6a0b90cc4c0ed7ee380a6504394cee7efd7db050e3774eee387324b2"
dependencies = [
"instant",
]
@ -435,9 +435,9 @@ checksum = "69a039c3498dc930fe810151a34ba0c1c70b02b8625035592e74432f678591f2"
[[package]]
name = "futures"
version = "0.3.17"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca"
checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4"
dependencies = [
"futures-channel",
"futures-core",
@ -450,9 +450,9 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.17"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888"
checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b"
dependencies = [
"futures-core",
"futures-sink",
@ -460,15 +460,15 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.17"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d"
checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7"
[[package]]
name = "futures-executor"
version = "0.3.17"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c"
checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a"
dependencies = [
"futures-core",
"futures-task",
@ -477,9 +477,9 @@ dependencies = [
[[package]]
name = "futures-io"
version = "0.3.17"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377"
checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2"
[[package]]
name = "futures-lite"
@ -498,12 +498,10 @@ dependencies = [
[[package]]
name = "futures-macro"
version = "0.3.17"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb"
checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c"
dependencies = [
"autocfg",
"proc-macro-hack",
"proc-macro2",
"quote",
"syn",
@ -511,23 +509,22 @@ dependencies = [
[[package]]
name = "futures-sink"
version = "0.3.17"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11"
checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508"
[[package]]
name = "futures-task"
version = "0.3.17"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99"
checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72"
[[package]]
name = "futures-util"
version = "0.3.17"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481"
checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164"
dependencies = [
"autocfg",
"futures-channel",
"futures-core",
"futures-io",
@ -537,8 +534,6 @@ dependencies = [
"memchr",
"pin-project-lite",
"pin-utils",
"proc-macro-hack",
"proc-macro-nested",
"slab",
]
@ -566,7 +561,7 @@ dependencies = [
[[package]]
name = "glib"
version = "0.15.0"
source = "git+https://github.com/gtk-rs/gtk-rs-core#88de0f7cdffd391893918861eb16e65ef714977a"
source = "git+https://github.com/gtk-rs/gtk-rs-core#9c59482b4bc1d3d46ca7ddaf40f9e9e04964b4c6"
dependencies = [
"bitflags",
"futures-channel",
@ -579,15 +574,16 @@ dependencies = [
"libc",
"once_cell",
"smallvec",
"thiserror",
]
[[package]]
name = "glib-macros"
version = "0.15.0"
source = "git+https://github.com/gtk-rs/gtk-rs-core#88de0f7cdffd391893918861eb16e65ef714977a"
source = "git+https://github.com/gtk-rs/gtk-rs-core#9c59482b4bc1d3d46ca7ddaf40f9e9e04964b4c6"
dependencies = [
"anyhow",
"heck",
"heck 0.4.0",
"proc-macro-crate",
"proc-macro-error",
"proc-macro2",
@ -598,7 +594,7 @@ dependencies = [
[[package]]
name = "glib-sys"
version = "0.15.0"
source = "git+https://github.com/gtk-rs/gtk-rs-core#88de0f7cdffd391893918861eb16e65ef714977a"
source = "git+https://github.com/gtk-rs/gtk-rs-core#9c59482b4bc1d3d46ca7ddaf40f9e9e04964b4c6"
dependencies = [
"libc",
"system-deps",
@ -606,9 +602,9 @@ dependencies = [
[[package]]
name = "gloo-timers"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f"
checksum = "6f16c88aa13d2656ef20d1c042086b8767bbe2bdb62526894275a1b062161b2e"
dependencies = [
"futures-channel",
"futures-core",
@ -620,7 +616,7 @@ dependencies = [
[[package]]
name = "gobject-sys"
version = "0.15.0"
source = "git+https://github.com/gtk-rs/gtk-rs-core#88de0f7cdffd391893918861eb16e65ef714977a"
source = "git+https://github.com/gtk-rs/gtk-rs-core#9c59482b4bc1d3d46ca7ddaf40f9e9e04964b4c6"
dependencies = [
"glib-sys",
"libc",
@ -639,7 +635,7 @@ dependencies = [
[[package]]
name = "gstreamer"
version = "0.18.0"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#f31aa2efee1ff84439ae199dcef58ce841809e14"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#1dae136ae3b0d935f4cf336f111d8aad2feab723"
dependencies = [
"bitflags",
"cfg-if",
@ -665,7 +661,7 @@ dependencies = [
[[package]]
name = "gstreamer-app"
version = "0.18.0"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#f31aa2efee1ff84439ae199dcef58ce841809e14"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#1dae136ae3b0d935f4cf336f111d8aad2feab723"
dependencies = [
"bitflags",
"futures-core",
@ -681,7 +677,7 @@ dependencies = [
[[package]]
name = "gstreamer-app-sys"
version = "0.18.0"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#f31aa2efee1ff84439ae199dcef58ce841809e14"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#1dae136ae3b0d935f4cf336f111d8aad2feab723"
dependencies = [
"glib-sys",
"gstreamer-base-sys",
@ -693,7 +689,7 @@ dependencies = [
[[package]]
name = "gstreamer-base"
version = "0.18.0"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#f31aa2efee1ff84439ae199dcef58ce841809e14"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#1dae136ae3b0d935f4cf336f111d8aad2feab723"
dependencies = [
"bitflags",
"cfg-if",
@ -706,7 +702,7 @@ dependencies = [
[[package]]
name = "gstreamer-base-sys"
version = "0.18.0"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#f31aa2efee1ff84439ae199dcef58ce841809e14"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#1dae136ae3b0d935f4cf336f111d8aad2feab723"
dependencies = [
"glib-sys",
"gobject-sys",
@ -718,7 +714,7 @@ dependencies = [
[[package]]
name = "gstreamer-rtp"
version = "0.18.0"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#f31aa2efee1ff84439ae199dcef58ce841809e14"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#1dae136ae3b0d935f4cf336f111d8aad2feab723"
dependencies = [
"bitflags",
"glib",
@ -730,7 +726,7 @@ dependencies = [
[[package]]
name = "gstreamer-rtp-sys"
version = "0.18.0"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#f31aa2efee1ff84439ae199dcef58ce841809e14"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#1dae136ae3b0d935f4cf336f111d8aad2feab723"
dependencies = [
"glib-sys",
"gstreamer-base-sys",
@ -742,7 +738,7 @@ dependencies = [
[[package]]
name = "gstreamer-sdp"
version = "0.18.0"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#f31aa2efee1ff84439ae199dcef58ce841809e14"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#1dae136ae3b0d935f4cf336f111d8aad2feab723"
dependencies = [
"glib",
"gstreamer",
@ -752,7 +748,7 @@ dependencies = [
[[package]]
name = "gstreamer-sdp-sys"
version = "0.18.0"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#f31aa2efee1ff84439ae199dcef58ce841809e14"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#1dae136ae3b0d935f4cf336f111d8aad2feab723"
dependencies = [
"glib-sys",
"gstreamer-sys",
@ -763,7 +759,7 @@ dependencies = [
[[package]]
name = "gstreamer-sys"
version = "0.18.0"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#f31aa2efee1ff84439ae199dcef58ce841809e14"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#1dae136ae3b0d935f4cf336f111d8aad2feab723"
dependencies = [
"glib-sys",
"gobject-sys",
@ -774,7 +770,7 @@ dependencies = [
[[package]]
name = "gstreamer-video"
version = "0.18.0"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#f31aa2efee1ff84439ae199dcef58ce841809e14"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#1dae136ae3b0d935f4cf336f111d8aad2feab723"
dependencies = [
"bitflags",
"cfg-if",
@ -786,12 +782,13 @@ dependencies = [
"gstreamer-video-sys",
"libc",
"once_cell",
"serde",
]
[[package]]
name = "gstreamer-video-sys"
version = "0.18.0"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#f31aa2efee1ff84439ae199dcef58ce841809e14"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#1dae136ae3b0d935f4cf336f111d8aad2feab723"
dependencies = [
"glib-sys",
"gobject-sys",
@ -804,7 +801,7 @@ dependencies = [
[[package]]
name = "gstreamer-webrtc"
version = "0.18.0"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#f31aa2efee1ff84439ae199dcef58ce841809e14"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#1dae136ae3b0d935f4cf336f111d8aad2feab723"
dependencies = [
"glib",
"gstreamer",
@ -816,7 +813,7 @@ dependencies = [
[[package]]
name = "gstreamer-webrtc-sys"
version = "0.18.0"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#f31aa2efee1ff84439ae199dcef58ce841809e14"
source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#1dae136ae3b0d935f4cf336f111d8aad2feab723"
dependencies = [
"glib-sys",
"gstreamer-sdp-sys",
@ -839,6 +836,12 @@ dependencies = [
"unicode-segmentation",
]
[[package]]
name = "heck"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
version = "0.1.19"
@ -850,9 +853,9 @@ dependencies = [
[[package]]
name = "http"
version = "0.2.5"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b"
checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03"
dependencies = [
"bytes",
"fnv",
@ -897,9 +900,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "0.4.8"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]]
name = "js-sys"
@ -927,9 +930,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.108"
version = "0.2.112"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
[[package]]
name = "log"
@ -1019,9 +1022,9 @@ dependencies = [
[[package]]
name = "num_cpus"
version = "1.13.0"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [
"hermit-abi",
"libc",
@ -1029,9 +1032,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.8.0"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "opaque-debug"
@ -1061,9 +1064,9 @@ checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a"
[[package]]
name = "openssl-sys"
version = "0.9.71"
version = "0.9.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7df13d165e607909b363a4757a6f133f8a818a74e9d3a98d09c6128e15fa4c73"
checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb"
dependencies = [
"autocfg",
"cc",
@ -1122,9 +1125,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.22"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f"
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
[[package]]
name = "polling"
@ -1141,9 +1144,9 @@ dependencies = [
[[package]]
name = "ppv-lite86"
version = "0.2.15"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "pretty-hex"
@ -1185,32 +1188,20 @@ dependencies = [
"version_check",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro-nested"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
[[package]]
name = "proc-macro2"
version = "1.0.32"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.10"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
dependencies = [
"proc-macro2",
]
@ -1299,9 +1290,9 @@ dependencies = [
[[package]]
name = "ryu"
version = "1.0.5"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]]
name = "schannel"
@ -1338,9 +1329,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.130"
version = "1.0.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
checksum = "8b9875c23cf305cd1fd7eb77234cbb705f21ea6a72c637a5c6db5fe4b8e7f008"
dependencies = [
"serde_derive",
]
@ -1356,9 +1347,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.130"
version = "1.0.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
checksum = "ecc0db5cb2556c0e558887d9bbdcf6ac4471e83ff66cf696e5419024d1606276"
dependencies = [
"proc-macro2",
"quote",
@ -1367,9 +1358,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.71"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063bf466a64011ac24040a49009724ee60a57da1b437617ceb32e53ad61bfb19"
checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5"
dependencies = [
"itoa",
"ryu",
@ -1400,9 +1391,9 @@ dependencies = [
[[package]]
name = "signal-hook"
version = "0.3.10"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c98891d737e271a2954825ef19e46bd16bdb98e2746f2eec4f7a4ef7946efd1"
checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d"
dependencies = [
"libc",
"signal-hook-registry",
@ -1447,9 +1438,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.81"
version = "1.0.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b"
dependencies = [
"proc-macro2",
"quote",
@ -1463,7 +1454,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1487aaddaacbc5d60a2a507ba1617c5ca66c57dd0dd07d0c5efd5b693841d4"
dependencies = [
"cfg-expr",
"heck",
"heck 0.3.3",
"pkg-config",
"toml",
"version-compare",
@ -1649,9 +1640,9 @@ dependencies = [
[[package]]
name = "typenum"
version = "1.14.0"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "unicode-bidi"
@ -1731,9 +1722,9 @@ checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73"
[[package]]
name = "version_check"
version = "0.9.3"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "waker-fn"

View file

@ -135,6 +135,27 @@ for the list of properties.
[the source code]: plugins/src/signaller/imp.rs
### Enable 'navigation' a.k.a user interactivity with the content
`webrtcsink` implements the [`GstNavigation`] interface which allows interacting
with the content, for example move with your mouse, entering keys with the
keyboard, etc... On top of that a `WebRTCDataChannel` based protocol has been
implemented and can be activated with the `enable-data-channel-navigation=true`
property. The [demo](www/) implements the protocol and you can easily test this
feature, using the [`wpesrc`] for example.
As an example, the following pipeline allows you to navigate the GStreamer
documentation inside the video running within your web browser (in
http://127.0.0.1:8000 if you followed previous steps of that readme):
```
gst-launch-1.0 wpesrc location=https://gstreamer.freedesktop.org/documentation/ ! webrtcsink enable-data-channel-navigation=true
```
[`GstNavigation`]: https://gstreamer.freedesktop.org/documentation/video/gstnavigation.html
[`wpesrc`]: https://gstreamer.freedesktop.org/documentation/wpe/wpesrc.html
## Testing congestion control
For the purpose of testing congestion in a reproducible manner, a

View file

@ -11,7 +11,7 @@ build = "build.rs"
[dependencies]
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_20", "ser_de"] }
gst-app = { package = "gstreamer-app", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_20"] }
gst-video = { package = "gstreamer-video", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_20"] }
gst-video = { package = "gstreamer-video", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_20", "ser_de"] }
gst-webrtc = { package = "gstreamer-webrtc", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_20"] }
gst-sdp = { package = "gstreamer-sdp", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_20"] }
gst-rtp = { package = "gstreamer-rtp", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_20"] }

View file

@ -2,8 +2,11 @@ use anyhow::Context;
use gst::glib;
use gst::prelude::*;
use gst::subclass::prelude::*;
use gst_video::prelude::*;
use gst_video::subclass::prelude::*;
use gst::{gst_debug, gst_error, gst_info, gst_log, gst_trace, gst_warning};
use gst_rtp::prelude::*;
use gst_webrtc::WebRTCDataChannel;
use async_std::task;
use futures::prelude::*;
@ -44,6 +47,7 @@ const DEFAULT_CONGESTION_CONTROL: WebRTCSinkCongestionControl =
WebRTCSinkCongestionControl::Homegrown;
const DEFAULT_DO_FEC: bool = true;
const DEFAULT_DO_RETRANSMISSION: bool = true;
const DEFAULT_ENABLE_DATA_CHANNEL_NAVIGATION: bool = false;
/// User configuration
struct Settings {
@ -56,6 +60,7 @@ struct Settings {
max_bitrate: u32,
do_fec: bool,
do_retransmission: bool,
enable_data_channel_navigation: bool,
}
/// Represents a codec we can offer
@ -190,11 +195,30 @@ struct State {
audio_serial: u32,
video_serial: u32,
streams: HashMap<String, InputStream>,
navigation_handler: Option<NavigationEventHandler>,
}
fn create_navigation_event<N: IsA< gst_video::Navigation>>(sink: &N, msg: &str) {
let event: Result<gst_video::NavigationEvent, _> = serde_json::from_str(msg);
if let Ok(event) = event {
sink.send_event(event.structure());
} else {
gst_error!(CAT, "Invalid navigation event: {:?}", msg);
}
}
/// Simple utility for tearing down a pipeline cleanly
struct PipelineWrapper(gst::Pipeline);
// Structure to generate GstNavigation event from a WebRTCDataChannel
#[derive(Debug)]
struct NavigationEventHandler {
channel: WebRTCDataChannel,
message_sig: glib::SignalHandlerId,
}
/// Our instance structure
#[derive(Default)]
pub struct WebRTCSink {
@ -220,6 +244,7 @@ impl Default for Settings {
max_bitrate: DEFAULT_MAX_BITRATE,
do_fec: DEFAULT_DO_FEC,
do_retransmission: DEFAULT_DO_RETRANSMISSION,
enable_data_channel_navigation: DEFAULT_ENABLE_DATA_CHANNEL_NAVIGATION,
}
}
}
@ -239,6 +264,7 @@ impl Default for State {
audio_serial: 0,
video_serial: 0,
streams: HashMap::new(),
navigation_handler: None,
}
}
}
@ -1167,6 +1193,33 @@ impl InputStream {
}
}
impl NavigationEventHandler {
pub fn new(
element: &super::WebRTCSink,
webrtcbin: &gst::Element,
) -> Self {
let channel = webrtcbin.emit_by_name::<WebRTCDataChannel>(
"create-data-channel",
&[&"input", &None::<gst::Structure>],
);
let weak_element = element.downgrade();
Self {
message_sig: channel.connect("on-message-string", false, move |values| {
if let Some(element) = weak_element.upgrade() {
let _channel = values[0].get::<WebRTCDataChannel>().unwrap();
let msg = values[1].get::<&str>().unwrap();
create_navigation_event(&element, msg);
}
None
}),
channel,
}
}
}
impl WebRTCSink {
/// Build an ordered map of Codecs, given user-provided audio / video caps */
fn lookup_codecs(&self) -> BTreeMap<i32, Codec> {
@ -1664,6 +1717,11 @@ impl WebRTCSink {
})?;
element.emit_by_name::<()>("new-webrtcbin", &[&peer_id, &webrtcbin]);
if settings.enable_data_channel_navigation {
state.navigation_handler = Some(
NavigationEventHandler::new(&element, &webrtcbin)
);
}
pipeline.set_state(gst::State::Playing).map_err(|err| {
WebRTCSinkError::ConsumerPipelineError {
@ -2161,7 +2219,7 @@ impl ObjectSubclass for WebRTCSink {
const NAME: &'static str = "RsWebRTCSink";
type Type = super::WebRTCSink;
type ParentType = gst::Bin;
type Interfaces = (gst::ChildProxy,);
type Interfaces = (gst::ChildProxy, gst_video::Navigation);
}
impl ObjectImpl for WebRTCSink {
@ -2243,6 +2301,13 @@ impl ObjectImpl for WebRTCSink {
DEFAULT_DO_RETRANSMISSION,
glib::ParamFlags::READWRITE | gst::PARAM_FLAG_MUTABLE_READY
),
glib::ParamSpecBoolean::new(
"enable-data-channel-navigation",
"Enable data channel navigation",
"Enable navigation events through a dedicated WebRTCDataChannel",
DEFAULT_ENABLE_DATA_CHANNEL_NAVIGATION,
glib::ParamFlags::READWRITE | gst::PARAM_FLAG_MUTABLE_READY
),
]
});
@ -2331,6 +2396,10 @@ impl ObjectImpl for WebRTCSink {
let mut settings = self.settings.lock().unwrap();
settings.do_retransmission = value.get::<bool>().expect("type checked upstream");
}
"enable-data-channel-navigation" => {
let mut settings = self.settings.lock().unwrap();
settings.enable_data_channel_navigation = value.get::<bool>().expect("type checked upstream");
}
_ => unimplemented!(),
}
}
@ -2373,6 +2442,10 @@ impl ObjectImpl for WebRTCSink {
let settings = self.settings.lock().unwrap();
settings.do_retransmission.to_value()
}
"enable-data-channel-navigation" => {
let settings = self.settings.lock().unwrap();
settings.enable_data_channel_navigation.to_value()
}
"stats" => self.gather_stats().to_value(),
_ => unimplemented!(),
}
@ -2585,3 +2658,23 @@ impl ChildProxyImpl for WebRTCSink {
}
}
}
impl NavigationImpl for WebRTCSink {
fn send_event(&self, _imp: &Self::Type, event_def: gst::Structure) {
let mut state = self.state.lock().unwrap();
let event = gst::event::Navigation::new(event_def);
state
.streams
.iter_mut()
.for_each(|(_, stream)| {
if stream.sink_pad.name().starts_with("video_") {
gst_log!(CAT, "Navigating to: {:?}", event);
// FIXME: Handle multi tracks.
if !stream.sink_pad.push_event(event.clone()) {
gst_info!(CAT, "Could not send event: {:?}", event);
}
}
});
}
}

View file

@ -7,7 +7,7 @@ mod imp;
mod utils;
glib::wrapper! {
pub struct WebRTCSink(ObjectSubclass<imp::WebRTCSink>) @extends gst::Bin, gst::Element, gst::Object, @implements gst::ChildProxy;
pub struct WebRTCSink(ObjectSubclass<imp::WebRTCSink>) @extends gst::Bin, gst::Element, gst::Object, @implements gst::ChildProxy, gst_video::Navigation;
}
unsafe impl Send for WebRTCSink {}

View file

@ -16,6 +16,8 @@
</style>
<link rel="stylesheet" type="text/css" href="theme.css">
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="keyboard.js"></script>
<script src="input.js"></script>
<script src="webrtc.js"></script>
<script>window.onload = setup;</script>
</head>

View file

@ -55,7 +55,7 @@ class Input {
this.buttonMask = 0;
/**
* @type {Guacamole.Keyboard}
* @type {Keyboard}
*/
this.keyboard = null;
@ -117,7 +117,7 @@ class Input {
*/
_mouseButtonMovement(event) {
const down = (event.type === 'mousedown' ? 1 : 0);
var mtype = "m";
var data = {};
if (event.type === 'mousemove' && !this.m) return;
@ -133,12 +133,20 @@ class Input {
}
if (document.pointerLockElement) {
mtype = "m2";
// FIXME - mark as relative!
console.warn("FIXME: Make event relative!")
this.x = event.movementX;
this.y = event.movementY;
} else if (event.type === 'mousemove') {
this.x = this._clientToServerX(event.clientX);
this.y = this._clientToServerY(event.clientY);
data["event"] = "MouseMove"
}
if (event.type === 'mousedown') {
data["event"] = "MouseButtonPress";
} else if (event.type === 'mouseup') {
data["event"] = "MouseButtonRelease";
}
if (event.type === 'mousedown' || event.type === 'mouseup') {
@ -148,16 +156,14 @@ class Input {
} else {
this.buttonMask &= ~mask;
}
data["button"] = this.buttonMask;
}
var toks = [
mtype,
this.x,
this.y,
this.buttonMask
];
data["x"] = this.x;
data["y"] = this.y;
this.send(toks.join(","));
this.send(JSON.stringify(data));
}
/**
@ -165,56 +171,45 @@ class Input {
* @param {TouchEvent} event
*/
_touch(event) {
var mtype = "m";
var mask = 1;
var data = {};
if (event.type === 'touchstart') {
this.buttonMask |= mask;
data["event"] = "MouseButtonPress";
data["button"] = this.buttonMask;
} else if (event.type === 'touchend') {
this.buttonMask &= ~mask;
data["event"] = "MouseButtonRelease";
data["button"] = this.buttonMask;
} else if (event.type === 'touchmove') {
event.preventDefault();
data["event"] = "MouseMove";
}
this.x = this._clientToServerX(event.changedTouches[0].clientX);
this.y = this._clientToServerY(event.changedTouches[0].clientY);
var toks = [
mtype,
this.x,
this.y,
this.buttonMask
];
data["x"] = this.x;
data["y"] = this.y;
this.send(toks.join(","));
this.send(JSON.stringify(data));
}
/**
* Handles mouse wheel events and sends them to WebRTC app.
* @param {MouseWheelEvent} event
* @param {MouseEvent} event
*/
_mouseWheel(event) {
var mtype = (document.pointerLockElement ? "m2" : "m");
var button = 3;
if (event.deltaY < 0) {
button = 4;
}
var mask = 1 << button;
var toks;
// Simulate button press and release.
for (var i = 0; i < 2; i++) {
if (i === 0)
this.buttonMask |= mask;
else
this.buttonMask &= ~mask;
toks = [
mtype,
this.x,
this.y,
this.buttonMask
];
this.send(toks.join(","));
}
_wheel(event) {
let data = {
"event": "MouseScroll",
"x": this.x,
"y": this.y,
"delta_x": -event.deltaX,
"delta_y": -event.deltaY,
};
this.send(JSON.stringify(data));
event.preventDefault();
}
@ -261,24 +256,11 @@ class Input {
}
}
/**
* Sends WebRTC app command to toggle display of the remote mouse pointer.
*/
_pointerLock() {
if (document.pointerLockElement) {
this.send("p,1");
} else {
this.send("p,0");
}
}
/**
* Sends WebRTC app command to hide the remote pointer when exiting pointer lock.
*/
_exitPointerLock() {
document.exitPointerLock();
// hide the pointer.
this.send("p,0");
}
/**
@ -295,13 +277,26 @@ class Input {
const vpWidth = frameW * multi;
const vpHeight = (frameH * multi);
var elem = this.element;
var offsetLeft = 0;
var offsetTop = 0;
do {
if (!isNaN(elem.offsetLeft)) {
offsetLeft += elem.offsetLeft;
}
if (!isNaN(elem.offsetTop)) {
offsetTop += elem.offsetTop;
}
} while (elem = elem.offsetParent);
this.m = {
mouseMultiX: frameW / vpWidth,
mouseMultiY: frameH / vpHeight,
mouseOffsetX: Math.max((windowW - vpWidth) / 2.0, 0),
mouseOffsetY: Math.max((windowH - vpHeight) / 2.0, 0),
centerOffsetX: (document.documentElement.clientWidth - this.element.offsetWidth) / 2.0,
centerOffsetY: (document.documentElement.clientHeight - this.element.offsetHeight) / 2.0,
offsetLeft: offsetLeft,
offsetTop: offsetTop,
scrollX: window.scrollX,
scrollY: window.scrollY,
frameW,
@ -314,7 +309,7 @@ class Input {
* @param {Integer} clientX
*/
_clientToServerX(clientX) {
let serverX = Math.round((clientX - this.m.mouseOffsetX - this.m.centerOffsetX + this.m.scrollX) * this.m.mouseMultiX);
var serverX = Math.round((clientX - this.m.mouseOffsetX - this.m.offsetLeft + this.m.scrollX) * this.m.mouseMultiX);
if (serverX === this.m.frameW - 1) serverX = this.m.frameW;
if (serverX > this.m.frameW) serverX = this.m.frameW;
@ -328,7 +323,7 @@ class Input {
* @param {Integer} clientY
*/
_clientToServerY(clientY) {
let serverY = Math.round((clientY - this.m.mouseOffsetY - this.m.centerOffsetY + this.m.scrollY) * this.m.mouseMultiY);
let serverY = Math.round((clientY - this.m.mouseOffsetY - this.m.offsetTop + this.m.scrollY) * this.m.mouseMultiY);
if (serverY === this.m.frameH - 1) serverY = this.m.frameH;
if (serverY > this.m.frameH) serverY = this.m.frameH;
@ -337,61 +332,6 @@ class Input {
return serverY;
}
/**
* Sends command to WebRTC app to connect virtual joystick and initializes the local GamepadManger.
* @param {GamepadEvent} event
*/
_gamepadConnected(event) {
console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.",
event.gamepad.index, event.gamepad.id,
event.gamepad.buttons.length, event.gamepad.axes.length);
if (this.ongamepadconnected !== null) {
this.ongamepadconnected(event.gamepad.id);
}
// Initialize the gamepad manager.
this.gamepadManager = new GamepadManager(event.gamepad, this._gamepadButton.bind(this), this._gamepadAxis.bind(this), this._gamepadDisconnect.bind(this));
// Send joystick connect message over data channel.
this.send("js,c," + this.gamepadManager.numAxes + "," + this.gamepadManager.numButtons);
}
/**
* Sends joystick disconnect command to WebRTC app.
*/
_gamepadDisconnect() {
console.log("Gamepad disconnected");
if (this.ongamepaddisconneceted !== null) {
this.ongamepaddisconneceted();
}
this.send("js,d")
}
/**
* Send gamepad button to WebRTC app.
*
* @param {number} gp_num - the gamepad number
* @param {number} btn_num - the uinput converted button number
* @param {number} val - the button value, 1 or 0 for pressed or not-pressed.
*/
_gamepadButton(gp_num, btn_num, val) {
this.send("js,b," + btn_num + "," + val);
}
/**
* Send the gamepad axis to the WebRTC app.
*
* @param {number} gp_num - the gamepad number
* @param {number} axis_num - the uinput converted axis number
* @param {number} val - the normalize value between [0, 255]
*/
_gamepadAxis(gp_num, axis_num, val) {
this.send("js,a," + axis_num + "," + val)
}
/**
* When fullscreen is entered, request keyboard and pointer lock.
*/
@ -405,7 +345,8 @@ class Input {
this.keyboard.reset();
// Reset stuck keys on server side.
this.send("kr");
// FIXME: How to implement resetting keyboard with the GstNavigation interface
// this.send("kr");
}
/**
@ -438,26 +379,16 @@ class Input {
*/
attach() {
this.listeners.push(addListener(this.element, 'resize', this._windowMath, this));
this.listeners.push(addListener(this.element, 'mousewheel', this._mouseWheel, this));
this.listeners.push(addListener(this.element, 'wheel', this._wheel, this));
this.listeners.push(addListener(this.element, 'contextmenu', this._contextMenu, this));
this.listeners.push(addListener(this.element.parentElement, 'fullscreenchange', this._onFullscreenChange, this));
this.listeners.push(addListener(document, 'pointerlockchange', this._pointerLock, this));
this.listeners.push(addListener(window, 'keydown', this._key, this));
this.listeners.push(addListener(window, 'keyup', this._key, this));
this.listeners.push(addListener(window, 'resize', this._windowMath, this));
this.listeners.push(addListener(window, 'resize', this._resizeStart, this));
// Gamepad support
this.listeners.push(addListener(window, 'gamepadconnected', this._gamepadConnected, this));
this.listeners.push(addListener(window, 'gamepaddisconnected', this._gamepadDisconnect, this));
if ('ontouchstart' in window) {
this.listeners.push(addListener(window, 'touchstart', this._touch, this));
this.listeners.push(addListener(this.element, 'touchend', this._touch, this));
this.listeners.push(addListener(this.element, 'touchmove', this._touch, this));
console.log("Enabling mouse pointer display for touch devices.");
this.send("p,1");
console.warning("FIXME: Enabling mouse pointer display for touch devices.");
} else {
this.listeners.push(addListener(this.element, 'mousemove', this._mouseButtonMovement, this));
this.listeners.push(addListener(this.element, 'mousedown', this._mouseButtonMovement, this));
@ -471,12 +402,12 @@ class Input {
}, this));
// Using guacamole keyboard because it has the keysym translations.
this.keyboard = new Guacamole.Keyboard(window);
this.keyboard = new Keyboard(window);
this.keyboard.onkeydown = (keysym) => {
this.send("kd," + keysym);
this.send(JSON.stringify( {"event": "KeyPress", "key": keysym}));
};
this.keyboard.onkeyup = (keysym) => {
this.send("ku," + keysym);
this.send(JSON.stringify( {"event": "KeyRelease", "key": keysym}));
};
this._windowMath();
@ -490,7 +421,8 @@ class Input {
this.keyboard.onkeyup = null;
this.keyboard.reset();
delete this.keyboard;
this.send("kr");
// FIXME: How to implement resetting keyboard with the GstNavigation interface
// this.send("kr");
}
}

3280
www/keyboard.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -52,6 +52,8 @@ function Session(our_id, peer_id, closed_callback) {
this.peer_id = peer_id;
this.our_id = our_id;
this.closed_callback = closed_callback;
this.data_channel = null;
this.input = null;
this.getVideoElement = function() {
return document.getElementById("stream-" + this.our_id);
@ -76,6 +78,9 @@ function Session(our_id, peer_id, closed_callback) {
this.ws_conn.close();
this.ws_conn = null;
}
this.input && this.input.detach();
this.data_channel = null;
};
this.handleIncomingError = function(error) {
@ -240,20 +245,36 @@ function Session(our_id, peer_id, closed_callback) {
this.peer_connection.onaddstream = this.onRemoteStreamAdded.bind(this);
this.peer_connection.ondatachannel = (event) => {
console.log('Data channel created');
let receive_channel = event.channel;
let buffer = [];
console.log(`Data channel created: ${event.channel.label}`);
this.data_channel = event.channel;
receive_channel.onopen = (event) => {
console.log ("Receive channel opened");
video_element = this.getVideoElement();
if (video_element) {
this.input = new Input(video_element, (data) => {
if (this.data_channel) {
console.log(`Navigation data: ${data}`);
this.data_channel.send(data);
}
});
}
receive_channel.onclose = (event) => {
console.log ("Receive channel closed");
this.data_channel.onopen = (event) => {
console.log("Receive channel opened, attaching input");
this.input.attach();
}
receive_channel.onerror = (event) => {
console.log ("Error on receive channel", event.data);
this.data_channel.onclose = (event) => {
console.info("Receive channel closed");
this.input && this.input.detach();
this.data_channel = null;
}
receive_channel.onmessage = (event) => {
this.data_channel.onerror = (event) => {
this.input && this.input.detach();
console.warn("Error on receive channel", event.data);
this.data_channel = null;
}
let buffer = [];
this.data_channel.onmessage = (event) => {
if (typeof event.data === 'string' || event.data instanceof String) {
if (event.data == 'BEGIN_IMAGE')
buffer = [];