diff --git a/examples/chat/mix.lock b/examples/chat/mix.lock index 630a4229..e43e878c 100644 --- a/examples/chat/mix.lock +++ b/examples/chat/mix.lock @@ -7,8 +7,8 @@ "crc": {:hex, :crc, "0.10.5", "ee12a7c056ac498ef2ea985ecdc9fa53c1bfb4e53a484d9f17ff94803707dfd8", [:mix, :rebar3], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3e673b6495a9525c5c641585af1accba59a1eb33de697bedf341e247012c2c7f"}, "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, "elixir_uuid": {:hex, :elixir_uuid, "1.2.1", "dce506597acb7e6b0daeaff52ff6a9043f5919a4c3315abb4143f0b00378c097", [:mix], [], "hexpm", "f7eba2ea6c3555cea09706492716b0d87397b88946e6380898c2889d68585752"}, - "ex_dtls": {:hex, :ex_dtls, "0.16.0", "3ae38025ccc77f6db573e2e391602fa9bbc02253c137d8d2d59469a66cbe806b", [:mix], [{:bundlex, "~> 1.5.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2a4e30d74c6ddf95cc5b796423293c06a0da295454c3823819808ff031b4b361"}, - "ex_ice": {:hex, :ex_ice, "0.12.0", "b52ec3ff878d5fb632ef9facc7657dfdf59e2ff9f23e634b0918e6ce1a05af48", [:mix], [{:elixir_uuid, "~> 1.0", [hex: :elixir_uuid, repo: "hexpm", optional: false]}, {:ex_stun, "~> 0.2.0", [hex: :ex_stun, repo: "hexpm", optional: false]}, {:ex_turn, "~> 0.2.0", [hex: :ex_turn, repo: "hexpm", optional: false]}], "hexpm", "a86024a5fbf9431082784be4bb3606d3cde9218fb325a9f208ccd6e0abfd0d73"}, + "ex_dtls": {:git, "https://github.com/elixir-webrtc/ex_dtls.git", "5a9904d30d55005bcf91f3b87e5fcd5e936f7fb8", [branch: "disconnect"]}, + "ex_ice": {:git, "https://github.com/elixir-webrtc/ex_ice.git", "c13e5ad9d379efc5cad3977b3676cf5c34113a73", [branch: "close"]}, "ex_libsrtp": {:hex, :ex_libsrtp, "0.7.2", "211bd89c08026943ce71f3e2c0231795b99cee748808ed3ae7b97cd8d2450b6b", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2e20645d0d739a4ecdcf8d4810a0c198120c8a2f617f2b75b2e2e704d59f492a"}, "ex_rtcp": {:hex, :ex_rtcp, "0.4.0", "f9e515462a9581798ff6413583a25174cfd2101c94a2ebee871cca7639886f0a", [:mix], [], "hexpm", "28956602cf210d692fcdaf3f60ca49681634e1deb28ace41246aee61ee22dc3b"}, "ex_rtp": {:hex, :ex_rtp, "0.4.0", "1f1b5c1440a904706011e3afbb41741f5da309ce251cb986690ce9fd82636658", [:mix], [], "hexpm", "0f72d80d5953a62057270040f0f1ee6f955c08eeae82ac659c038001d7d5a790"}, @@ -20,14 +20,14 @@ "hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "membrane_precompiled_dependency_provider": {:hex, :membrane_precompiled_dependency_provider, "0.1.2", "8af73b7dc15ba55c9f5fbfc0453d4a8edfb007ade54b56c37d626be0d1189aba", [:mix], [{:bundlex, "~> 1.4", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "7fe3e07361510445a29bee95336adde667c4162b76b7f4c8af3aeb3415292023"}, - "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, + "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, "mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "plug": {:hex, :plug, "1.15.3", "712976f504418f6dff0a3e554c40d705a9bcf89a7ccef92fc6a5ef8f16a30a97", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4365a3c010a56af402e0809208873d113e9c38c401cabd88027ef4f5c01fd2"}, - "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, + "plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"}, "qex": {:hex, :qex, "0.5.1", "0d82c0f008551d24fffb99d97f8299afcb8ea9cf99582b770bd004ed5af63fd6", [:mix], [], "hexpm", "935a39fdaf2445834b95951456559e9dc2063d0a055742c558a99987b38d6bab"}, - "req": {:hex, :req, "0.5.8", "50d8d65279d6e343a5e46980ac2a70e97136182950833a1968b371e753f6a662", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "d7fc5898a566477e174f26887821a3c5082b243885520ee4b45555f5d53f40ef"}, + "req": {:hex, :req, "0.5.10", "a3a063eab8b7510785a467f03d30a8d95f66f5c3d9495be3474b61459c54376c", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "8a604815743f8a2d3b5de0659fa3137fa4b1cffd636ecb69b30b2b9b2c2559be"}, "rustler": {:hex, :rustler, "0.36.1", "2d4b1ff57ea2789a44756a40dbb5fbb73c6ee0a13d031dcba96d0a5542598a6a", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.7", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "f3fba4ad272970e0d1bc62972fc4a99809651e54a125c5242de9bad4574b2d02"}, "shmex": {:hex, :shmex, "0.5.1", "81dd209093416bf6608e66882cb7e676089307448a1afd4fc906c1f7e5b94cf4", [:mix], [{:bunch_native, "~> 0.5.0", [hex: :bunch_native, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.0", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "c29f8286891252f64c4e1dac40b217d960f7d58def597c4e606ff8fbe71ceb80"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, diff --git a/examples/dtmf/mix.lock b/examples/dtmf/mix.lock index 31f267e3..4bd1290f 100644 --- a/examples/dtmf/mix.lock +++ b/examples/dtmf/mix.lock @@ -6,8 +6,8 @@ "crc": {:hex, :crc, "0.10.5", "ee12a7c056ac498ef2ea985ecdc9fa53c1bfb4e53a484d9f17ff94803707dfd8", [:mix, :rebar3], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3e673b6495a9525c5c641585af1accba59a1eb33de697bedf341e247012c2c7f"}, "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, "elixir_uuid": {:hex, :elixir_uuid, "1.2.1", "dce506597acb7e6b0daeaff52ff6a9043f5919a4c3315abb4143f0b00378c097", [:mix], [], "hexpm", "f7eba2ea6c3555cea09706492716b0d87397b88946e6380898c2889d68585752"}, - "ex_dtls": {:hex, :ex_dtls, "0.17.0", "dbe1d494583a307c26148cb5ea5d7c14e65daa8ec96cc73002cc3313ce4b9a81", [:mix], [{:bundlex, "~> 1.5.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "3eaa7221ec08fa9e4bc9430e426cbd5eb4feb8d8f450b203cf39b2114a94d713"}, - "ex_ice": {:hex, :ex_ice, "0.12.0", "b52ec3ff878d5fb632ef9facc7657dfdf59e2ff9f23e634b0918e6ce1a05af48", [:mix], [{:elixir_uuid, "~> 1.0", [hex: :elixir_uuid, repo: "hexpm", optional: false]}, {:ex_stun, "~> 0.2.0", [hex: :ex_stun, repo: "hexpm", optional: false]}, {:ex_turn, "~> 0.2.0", [hex: :ex_turn, repo: "hexpm", optional: false]}], "hexpm", "a86024a5fbf9431082784be4bb3606d3cde9218fb325a9f208ccd6e0abfd0d73"}, + "ex_dtls": {:git, "https://github.com/elixir-webrtc/ex_dtls.git", "5a9904d30d55005bcf91f3b87e5fcd5e936f7fb8", [branch: "disconnect"]}, + "ex_ice": {:git, "https://github.com/elixir-webrtc/ex_ice.git", "c13e5ad9d379efc5cad3977b3676cf5c34113a73", [branch: "close"]}, "ex_libsrtp": {:hex, :ex_libsrtp, "0.7.2", "211bd89c08026943ce71f3e2c0231795b99cee748808ed3ae7b97cd8d2450b6b", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2e20645d0d739a4ecdcf8d4810a0c198120c8a2f617f2b75b2e2e704d59f492a"}, "ex_rtcp": {:hex, :ex_rtcp, "0.4.0", "f9e515462a9581798ff6413583a25174cfd2101c94a2ebee871cca7639886f0a", [:mix], [], "hexpm", "28956602cf210d692fcdaf3f60ca49681634e1deb28ace41246aee61ee22dc3b"}, "ex_rtp": {:hex, :ex_rtp, "0.4.0", "1f1b5c1440a904706011e3afbb41741f5da309ce251cb986690ce9fd82636658", [:mix], [], "hexpm", "0f72d80d5953a62057270040f0f1ee6f955c08eeae82ac659c038001d7d5a790"}, diff --git a/examples/echo/mix.lock b/examples/echo/mix.lock index faf23643..ebde26fb 100644 --- a/examples/echo/mix.lock +++ b/examples/echo/mix.lock @@ -7,8 +7,8 @@ "crc": {:hex, :crc, "0.10.5", "ee12a7c056ac498ef2ea985ecdc9fa53c1bfb4e53a484d9f17ff94803707dfd8", [:mix, :rebar3], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3e673b6495a9525c5c641585af1accba59a1eb33de697bedf341e247012c2c7f"}, "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, "elixir_uuid": {:hex, :elixir_uuid, "1.2.1", "dce506597acb7e6b0daeaff52ff6a9043f5919a4c3315abb4143f0b00378c097", [:mix], [], "hexpm", "f7eba2ea6c3555cea09706492716b0d87397b88946e6380898c2889d68585752"}, - "ex_dtls": {:hex, :ex_dtls, "0.16.0", "3ae38025ccc77f6db573e2e391602fa9bbc02253c137d8d2d59469a66cbe806b", [:mix], [{:bundlex, "~> 1.5.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2a4e30d74c6ddf95cc5b796423293c06a0da295454c3823819808ff031b4b361"}, - "ex_ice": {:hex, :ex_ice, "0.12.0", "b52ec3ff878d5fb632ef9facc7657dfdf59e2ff9f23e634b0918e6ce1a05af48", [:mix], [{:elixir_uuid, "~> 1.0", [hex: :elixir_uuid, repo: "hexpm", optional: false]}, {:ex_stun, "~> 0.2.0", [hex: :ex_stun, repo: "hexpm", optional: false]}, {:ex_turn, "~> 0.2.0", [hex: :ex_turn, repo: "hexpm", optional: false]}], "hexpm", "a86024a5fbf9431082784be4bb3606d3cde9218fb325a9f208ccd6e0abfd0d73"}, + "ex_dtls": {:git, "https://github.com/elixir-webrtc/ex_dtls.git", "5a9904d30d55005bcf91f3b87e5fcd5e936f7fb8", [branch: "disconnect"]}, + "ex_ice": {:git, "https://github.com/elixir-webrtc/ex_ice.git", "c13e5ad9d379efc5cad3977b3676cf5c34113a73", [branch: "close"]}, "ex_libsrtp": {:hex, :ex_libsrtp, "0.7.2", "211bd89c08026943ce71f3e2c0231795b99cee748808ed3ae7b97cd8d2450b6b", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2e20645d0d739a4ecdcf8d4810a0c198120c8a2f617f2b75b2e2e704d59f492a"}, "ex_rtcp": {:hex, :ex_rtcp, "0.4.0", "f9e515462a9581798ff6413583a25174cfd2101c94a2ebee871cca7639886f0a", [:mix], [], "hexpm", "28956602cf210d692fcdaf3f60ca49681634e1deb28ace41246aee61ee22dc3b"}, "ex_rtp": {:hex, :ex_rtp, "0.4.0", "1f1b5c1440a904706011e3afbb41741f5da309ce251cb986690ce9fd82636658", [:mix], [], "hexpm", "0f72d80d5953a62057270040f0f1ee6f955c08eeae82ac659c038001d7d5a790"}, @@ -19,15 +19,15 @@ "hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "membrane_precompiled_dependency_provider": {:hex, :membrane_precompiled_dependency_provider, "0.1.2", "8af73b7dc15ba55c9f5fbfc0453d4a8edfb007ade54b56c37d626be0d1189aba", [:mix], [{:bundlex, "~> 1.4", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "7fe3e07361510445a29bee95336adde667c4162b76b7f4c8af3aeb3415292023"}, - "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, + "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, "mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_ownership": {:hex, :nimble_ownership, "0.3.1", "99d5244672fafdfac89bfad3d3ab8f0d367603ce1dc4855f86a1c75008bce56f", [:mix], [], "hexpm", "4bf510adedff0449a1d6e200e43e57a814794c8b5b6439071274d248d272a549"}, "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "plug": {:hex, :plug, "1.15.3", "712976f504418f6dff0a3e554c40d705a9bcf89a7ccef92fc6a5ef8f16a30a97", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4365a3c010a56af402e0809208873d113e9c38c401cabd88027ef4f5c01fd2"}, - "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, + "plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"}, "qex": {:hex, :qex, "0.5.1", "0d82c0f008551d24fffb99d97f8299afcb8ea9cf99582b770bd004ed5af63fd6", [:mix], [], "hexpm", "935a39fdaf2445834b95951456559e9dc2063d0a055742c558a99987b38d6bab"}, - "req": {:hex, :req, "0.5.8", "50d8d65279d6e343a5e46980ac2a70e97136182950833a1968b371e753f6a662", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "d7fc5898a566477e174f26887821a3c5082b243885520ee4b45555f5d53f40ef"}, + "req": {:hex, :req, "0.5.10", "a3a063eab8b7510785a467f03d30a8d95f66f5c3d9495be3474b61459c54376c", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "8a604815743f8a2d3b5de0659fa3137fa4b1cffd636ecb69b30b2b9b2c2559be"}, "shmex": {:hex, :shmex, "0.5.1", "81dd209093416bf6608e66882cb7e676089307448a1afd4fc906c1f7e5b94cf4", [:mix], [{:bunch_native, "~> 0.5.0", [hex: :bunch_native, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.0", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "c29f8286891252f64c4e1dac40b217d960f7d58def597c4e606ff8fbe71ceb80"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, "thousand_island": {:hex, :thousand_island, "1.3.11", "b68f3e91f74d564ae20b70d981bbf7097dde084343c14ae8a33e5b5fbb3d6f37", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "555c18c62027f45d9c80df389c3d01d86ba11014652c00be26e33b1b64e98d29"}, diff --git a/examples/save_to_file/mix.lock b/examples/save_to_file/mix.lock index 96877cbc..15d25f82 100644 --- a/examples/save_to_file/mix.lock +++ b/examples/save_to_file/mix.lock @@ -6,8 +6,8 @@ "crc": {:hex, :crc, "0.10.5", "ee12a7c056ac498ef2ea985ecdc9fa53c1bfb4e53a484d9f17ff94803707dfd8", [:mix, :rebar3], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3e673b6495a9525c5c641585af1accba59a1eb33de697bedf341e247012c2c7f"}, "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, "elixir_uuid": {:hex, :elixir_uuid, "1.2.1", "dce506597acb7e6b0daeaff52ff6a9043f5919a4c3315abb4143f0b00378c097", [:mix], [], "hexpm", "f7eba2ea6c3555cea09706492716b0d87397b88946e6380898c2889d68585752"}, - "ex_dtls": {:hex, :ex_dtls, "0.16.0", "3ae38025ccc77f6db573e2e391602fa9bbc02253c137d8d2d59469a66cbe806b", [:mix], [{:bundlex, "~> 1.5.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2a4e30d74c6ddf95cc5b796423293c06a0da295454c3823819808ff031b4b361"}, - "ex_ice": {:hex, :ex_ice, "0.12.0", "b52ec3ff878d5fb632ef9facc7657dfdf59e2ff9f23e634b0918e6ce1a05af48", [:mix], [{:elixir_uuid, "~> 1.0", [hex: :elixir_uuid, repo: "hexpm", optional: false]}, {:ex_stun, "~> 0.2.0", [hex: :ex_stun, repo: "hexpm", optional: false]}, {:ex_turn, "~> 0.2.0", [hex: :ex_turn, repo: "hexpm", optional: false]}], "hexpm", "a86024a5fbf9431082784be4bb3606d3cde9218fb325a9f208ccd6e0abfd0d73"}, + "ex_dtls": {:git, "https://github.com/elixir-webrtc/ex_dtls.git", "5a9904d30d55005bcf91f3b87e5fcd5e936f7fb8", [branch: "disconnect"]}, + "ex_ice": {:git, "https://github.com/elixir-webrtc/ex_ice.git", "c13e5ad9d379efc5cad3977b3676cf5c34113a73", [branch: "close"]}, "ex_libsrtp": {:hex, :ex_libsrtp, "0.7.2", "211bd89c08026943ce71f3e2c0231795b99cee748808ed3ae7b97cd8d2450b6b", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2e20645d0d739a4ecdcf8d4810a0c198120c8a2f617f2b75b2e2e704d59f492a"}, "ex_rtcp": {:hex, :ex_rtcp, "0.4.0", "f9e515462a9581798ff6413583a25174cfd2101c94a2ebee871cca7639886f0a", [:mix], [], "hexpm", "28956602cf210d692fcdaf3f60ca49681634e1deb28ace41246aee61ee22dc3b"}, "ex_rtp": {:hex, :ex_rtp, "0.4.0", "1f1b5c1440a904706011e3afbb41741f5da309ce251cb986690ce9fd82636658", [:mix], [], "hexpm", "0f72d80d5953a62057270040f0f1ee6f955c08eeae82ac659c038001d7d5a790"}, @@ -18,14 +18,14 @@ "hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "membrane_precompiled_dependency_provider": {:hex, :membrane_precompiled_dependency_provider, "0.1.2", "8af73b7dc15ba55c9f5fbfc0453d4a8edfb007ade54b56c37d626be0d1189aba", [:mix], [{:bundlex, "~> 1.4", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "7fe3e07361510445a29bee95336adde667c4162b76b7f4c8af3aeb3415292023"}, - "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, + "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, "mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "plug": {:hex, :plug, "1.15.3", "712976f504418f6dff0a3e554c40d705a9bcf89a7ccef92fc6a5ef8f16a30a97", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4365a3c010a56af402e0809208873d113e9c38c401cabd88027ef4f5c01fd2"}, - "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, + "plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"}, "qex": {:hex, :qex, "0.5.1", "0d82c0f008551d24fffb99d97f8299afcb8ea9cf99582b770bd004ed5af63fd6", [:mix], [], "hexpm", "935a39fdaf2445834b95951456559e9dc2063d0a055742c558a99987b38d6bab"}, - "req": {:hex, :req, "0.5.8", "50d8d65279d6e343a5e46980ac2a70e97136182950833a1968b371e753f6a662", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "d7fc5898a566477e174f26887821a3c5082b243885520ee4b45555f5d53f40ef"}, + "req": {:hex, :req, "0.5.10", "a3a063eab8b7510785a467f03d30a8d95f66f5c3d9495be3474b61459c54376c", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "8a604815743f8a2d3b5de0659fa3137fa4b1cffd636ecb69b30b2b9b2c2559be"}, "shmex": {:hex, :shmex, "0.5.1", "81dd209093416bf6608e66882cb7e676089307448a1afd4fc906c1f7e5b94cf4", [:mix], [{:bunch_native, "~> 0.5.0", [hex: :bunch_native, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.0", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "c29f8286891252f64c4e1dac40b217d960f7d58def597c4e606ff8fbe71ceb80"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, "thousand_island": {:hex, :thousand_island, "1.3.11", "b68f3e91f74d564ae20b70d981bbf7097dde084343c14ae8a33e5b5fbb3d6f37", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "555c18c62027f45d9c80df389c3d01d86ba11014652c00be26e33b1b64e98d29"}, diff --git a/examples/send_from_file/mix.lock b/examples/send_from_file/mix.lock index c834f1b7..7bf8bdb4 100644 --- a/examples/send_from_file/mix.lock +++ b/examples/send_from_file/mix.lock @@ -7,8 +7,8 @@ "crc": {:hex, :crc, "0.10.5", "ee12a7c056ac498ef2ea985ecdc9fa53c1bfb4e53a484d9f17ff94803707dfd8", [:mix, :rebar3], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3e673b6495a9525c5c641585af1accba59a1eb33de697bedf341e247012c2c7f"}, "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, "elixir_uuid": {:hex, :elixir_uuid, "1.2.1", "dce506597acb7e6b0daeaff52ff6a9043f5919a4c3315abb4143f0b00378c097", [:mix], [], "hexpm", "f7eba2ea6c3555cea09706492716b0d87397b88946e6380898c2889d68585752"}, - "ex_dtls": {:hex, :ex_dtls, "0.17.0", "dbe1d494583a307c26148cb5ea5d7c14e65daa8ec96cc73002cc3313ce4b9a81", [:mix], [{:bundlex, "~> 1.5.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "3eaa7221ec08fa9e4bc9430e426cbd5eb4feb8d8f450b203cf39b2114a94d713"}, - "ex_ice": {:hex, :ex_ice, "0.12.0", "b52ec3ff878d5fb632ef9facc7657dfdf59e2ff9f23e634b0918e6ce1a05af48", [:mix], [{:elixir_uuid, "~> 1.0", [hex: :elixir_uuid, repo: "hexpm", optional: false]}, {:ex_stun, "~> 0.2.0", [hex: :ex_stun, repo: "hexpm", optional: false]}, {:ex_turn, "~> 0.2.0", [hex: :ex_turn, repo: "hexpm", optional: false]}], "hexpm", "a86024a5fbf9431082784be4bb3606d3cde9218fb325a9f208ccd6e0abfd0d73"}, + "ex_dtls": {:git, "https://github.com/elixir-webrtc/ex_dtls.git", "5a9904d30d55005bcf91f3b87e5fcd5e936f7fb8", [branch: "disconnect"]}, + "ex_ice": {:git, "https://github.com/elixir-webrtc/ex_ice.git", "c13e5ad9d379efc5cad3977b3676cf5c34113a73", [branch: "close"]}, "ex_libsrtp": {:hex, :ex_libsrtp, "0.7.2", "211bd89c08026943ce71f3e2c0231795b99cee748808ed3ae7b97cd8d2450b6b", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2e20645d0d739a4ecdcf8d4810a0c198120c8a2f617f2b75b2e2e704d59f492a"}, "ex_rtcp": {:hex, :ex_rtcp, "0.4.0", "f9e515462a9581798ff6413583a25174cfd2101c94a2ebee871cca7639886f0a", [:mix], [], "hexpm", "28956602cf210d692fcdaf3f60ca49681634e1deb28ace41246aee61ee22dc3b"}, "ex_rtp": {:hex, :ex_rtp, "0.4.0", "1f1b5c1440a904706011e3afbb41741f5da309ce251cb986690ce9fd82636658", [:mix], [], "hexpm", "0f72d80d5953a62057270040f0f1ee6f955c08eeae82ac659c038001d7d5a790"}, diff --git a/examples/whip_whep/lib/whip_whep/forwarder.ex b/examples/whip_whep/lib/whip_whep/forwarder.ex index cc96bf8c..043e7b59 100644 --- a/examples/whip_whep/lib/whip_whep/forwarder.ex +++ b/examples/whip_whep/lib/whip_whep/forwarder.ex @@ -68,6 +68,34 @@ defmodule WhipWhep.Forwarder do {:noreply, state} end + @impl true + def handle_info({:ex_webrtc, pc, {:connection_state_change, :failed}}, %{input_pc: pc} = state) do + Logger.info("Input peer connection (#{inspect(pc)}) state change: failed. Removing.") + :ok = PeerConnection.stop(state.input_pc) + + Enum.each(state.pending_outputs, &PeerConnection.stop(&1)) + Enum.each(state.outputs, fn {pc, _output} -> PeerConnection.stop(pc) end) + + state = %{ + state + | input_pc: nil, + audio_input: nil, + video_input: nil, + pending_outputs: [], + outputs: %{} + } + + {:noreply, state} + end + + @impl true + def handle_info( + {:ex_webrtc, pc, {:connection_state_change, _conn_state}}, + %{input_pc: pc} = state + ) do + {:noreply, state} + end + @impl true def handle_info({:ex_webrtc, pc, {:connection_state_change, :connected}}, state) do state = @@ -94,7 +122,7 @@ defmodule WhipWhep.Forwarder do @impl true def handle_info({:ex_webrtc, pc, {:connection_state_change, :failed}}, state) do Logger.info("Output peer connection (#{inspect(pc)}) state change: failed. Removing.") - :ok = PeerConnection.close(pc) + :ok = PeerConnection.stop(pc) pending_outputs = List.delete(state.pending_outputs, pc) outputs = Map.delete(state.outputs, pc) state = %{state | pending_outputs: pending_outputs, outputs: outputs} diff --git a/examples/whip_whep/lib/whip_whep/peer_supervisor.ex b/examples/whip_whep/lib/whip_whep/peer_supervisor.ex index 91bbb292..5d66aaa7 100644 --- a/examples/whip_whep/lib/whip_whep/peer_supervisor.ex +++ b/examples/whip_whep/lib/whip_whep/peer_supervisor.ex @@ -69,8 +69,8 @@ defmodule WhipWhep.PeerSupervisor do {:ok, pc, pc_id, answer.sdp} else - {:error, res} = err -> - Logger.info("Failed to complete negotiation for #{inspect(pc)}") + {:error, _res} = err -> + Logger.info("Failed to complete negotiation for #{inspect(pc)}, #{inspect(err)}") terminate_pc(pc) err end diff --git a/examples/whip_whep/mix.lock b/examples/whip_whep/mix.lock index 26e6588f..6a0d21d0 100644 --- a/examples/whip_whep/mix.lock +++ b/examples/whip_whep/mix.lock @@ -9,8 +9,8 @@ "crc": {:hex, :crc, "0.10.5", "ee12a7c056ac498ef2ea985ecdc9fa53c1bfb4e53a484d9f17ff94803707dfd8", [:mix, :rebar3], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3e673b6495a9525c5c641585af1accba59a1eb33de697bedf341e247012c2c7f"}, "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, "elixir_uuid": {:hex, :elixir_uuid, "1.2.1", "dce506597acb7e6b0daeaff52ff6a9043f5919a4c3315abb4143f0b00378c097", [:mix], [], "hexpm", "f7eba2ea6c3555cea09706492716b0d87397b88946e6380898c2889d68585752"}, - "ex_dtls": {:hex, :ex_dtls, "0.16.0", "3ae38025ccc77f6db573e2e391602fa9bbc02253c137d8d2d59469a66cbe806b", [:mix], [{:bundlex, "~> 1.5.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2a4e30d74c6ddf95cc5b796423293c06a0da295454c3823819808ff031b4b361"}, - "ex_ice": {:hex, :ex_ice, "0.12.0", "b52ec3ff878d5fb632ef9facc7657dfdf59e2ff9f23e634b0918e6ce1a05af48", [:mix], [{:elixir_uuid, "~> 1.0", [hex: :elixir_uuid, repo: "hexpm", optional: false]}, {:ex_stun, "~> 0.2.0", [hex: :ex_stun, repo: "hexpm", optional: false]}, {:ex_turn, "~> 0.2.0", [hex: :ex_turn, repo: "hexpm", optional: false]}], "hexpm", "a86024a5fbf9431082784be4bb3606d3cde9218fb325a9f208ccd6e0abfd0d73"}, + "ex_dtls": {:git, "https://github.com/elixir-webrtc/ex_dtls.git", "5a9904d30d55005bcf91f3b87e5fcd5e936f7fb8", [branch: "disconnect"]}, + "ex_ice": {:git, "https://github.com/elixir-webrtc/ex_ice.git", "c13e5ad9d379efc5cad3977b3676cf5c34113a73", [branch: "close"]}, "ex_libsrtp": {:hex, :ex_libsrtp, "0.7.2", "211bd89c08026943ce71f3e2c0231795b99cee748808ed3ae7b97cd8d2450b6b", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2e20645d0d739a4ecdcf8d4810a0c198120c8a2f617f2b75b2e2e704d59f492a"}, "ex_rtcp": {:hex, :ex_rtcp, "0.4.0", "f9e515462a9581798ff6413583a25174cfd2101c94a2ebee871cca7639886f0a", [:mix], [], "hexpm", "28956602cf210d692fcdaf3f60ca49681634e1deb28ace41246aee61ee22dc3b"}, "ex_rtp": {:hex, :ex_rtp, "0.4.0", "1f1b5c1440a904706011e3afbb41741f5da309ce251cb986690ce9fd82636658", [:mix], [], "hexpm", "0f72d80d5953a62057270040f0f1ee6f955c08eeae82ac659c038001d7d5a790"}, @@ -22,17 +22,17 @@ "hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "membrane_precompiled_dependency_provider": {:hex, :membrane_precompiled_dependency_provider, "0.1.2", "8af73b7dc15ba55c9f5fbfc0453d4a8edfb007ade54b56c37d626be0d1189aba", [:mix], [{:bundlex, "~> 1.4", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "7fe3e07361510445a29bee95336adde667c4162b76b7f4c8af3aeb3415292023"}, - "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, + "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, "mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_ownership": {:hex, :nimble_ownership, "0.3.1", "99d5244672fafdfac89bfad3d3ab8f0d367603ce1dc4855f86a1c75008bce56f", [:mix], [], "hexpm", "4bf510adedff0449a1d6e200e43e57a814794c8b5b6439071274d248d272a549"}, "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "observer_cli": {:hex, :observer_cli, "1.7.4", "3c1bfb6d91bf68f6a3d15f46ae20da0f7740d363ee5bc041191ce8722a6c4fae", [:mix, :rebar3], [{:recon, "~> 2.5.1", [hex: :recon, repo: "hexpm", optional: false]}], "hexpm", "50de6d95d814f447458bd5d72666a74624eddb0ef98bdcee61a0153aae0865ff"}, "plug": {:hex, :plug, "1.15.3", "712976f504418f6dff0a3e554c40d705a9bcf89a7ccef92fc6a5ef8f16a30a97", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4365a3c010a56af402e0809208873d113e9c38c401cabd88027ef4f5c01fd2"}, - "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, + "plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"}, "qex": {:hex, :qex, "0.5.1", "0d82c0f008551d24fffb99d97f8299afcb8ea9cf99582b770bd004ed5af63fd6", [:mix], [], "hexpm", "935a39fdaf2445834b95951456559e9dc2063d0a055742c558a99987b38d6bab"}, "recon": {:hex, :recon, "2.5.5", "c108a4c406fa301a529151a3bb53158cadc4064ec0c5f99b03ddb8c0e4281bdf", [:mix, :rebar3], [], "hexpm", "632a6f447df7ccc1a4a10bdcfce71514412b16660fe59deca0fcf0aa3c054404"}, - "req": {:hex, :req, "0.5.8", "50d8d65279d6e343a5e46980ac2a70e97136182950833a1968b371e753f6a662", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "d7fc5898a566477e174f26887821a3c5082b243885520ee4b45555f5d53f40ef"}, + "req": {:hex, :req, "0.5.10", "a3a063eab8b7510785a467f03d30a8d95f66f5c3d9495be3474b61459c54376c", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "8a604815743f8a2d3b5de0659fa3137fa4b1cffd636ecb69b30b2b9b2c2559be"}, "shmex": {:hex, :shmex, "0.5.1", "81dd209093416bf6608e66882cb7e676089307448a1afd4fc906c1f7e5b94cf4", [:mix], [{:bunch_native, "~> 0.5.0", [hex: :bunch_native, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.0", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "c29f8286891252f64c4e1dac40b217d960f7d58def597c4e606ff8fbe71ceb80"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, "thousand_island": {:hex, :thousand_island, "1.3.11", "b68f3e91f74d564ae20b70d981bbf7097dde084343c14ae8a33e5b5fbb3d6f37", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "555c18c62027f45d9c80df389c3d01d86ba11014652c00be26e33b1b64e98d29"}, diff --git a/lib/ex_webrtc/dtls_transport.ex b/lib/ex_webrtc/dtls_transport.ex index 5695358f..3cf5bb58 100644 --- a/lib/ex_webrtc/dtls_transport.ex +++ b/lib/ex_webrtc/dtls_transport.ex @@ -62,6 +62,11 @@ defmodule ExWebRTC.DTLSTransport do GenServer.call(dtls_transport, :set_ice_connected) end + @spec set_ice_disconnected(dtls_transport()) :: :ok + def set_ice_disconnected(dtls_transport) do + GenServer.call(dtls_transport, :set_ice_disconnected) + end + @spec get_certs_info(dtls_transport()) :: %{ local_cert_info: cert_info(), remote_cert_info: cert_info() | nil @@ -101,6 +106,11 @@ defmodule ExWebRTC.DTLSTransport do GenServer.cast(dtls_transport, {:set_packet_loss, packet_loss}) end + @spec close(dtls_transport()) :: :ok + def close(dtls_transport) do + GenServer.call(dtls_transport, :close) + end + @spec stop(dtls_transport()) :: :ok def stop(dtls_transport) do GenServer.stop(dtls_transport) @@ -126,8 +136,8 @@ defmodule ExWebRTC.DTLSTransport do remote_cert: nil, remote_base64_cert: nil, remote_fingerprint: nil, - in_srtp: ExLibSRTP.new(), - out_srtp: ExLibSRTP.new(), + in_srtp: nil, + out_srtp: nil, # sha256 hex dump peer_fingerprint: nil, dtls_state: :new, @@ -146,7 +156,7 @@ defmodule ExWebRTC.DTLSTransport do state = %{state | ice_connected: true} if state.mode == :active do - {packets, timeout} = ExDTLS.do_handshake(state.dtls) + {:ok, packets, timeout} = ExDTLS.do_handshake(state.dtls) Process.send_after(self(), :dtls_timeout, timeout) :ok = do_send(state, packets) state = update_dtls_state(state, :connecting) @@ -158,7 +168,7 @@ defmodule ExWebRTC.DTLSTransport do end @impl true - def handle_call(:set_ice_connected, _from, state) do + def handle_call(:set_ice_connected, _from, %{dtls_state: :connecting} = state) do state = %{state | ice_connected: true} if state.buffered_local_packets do @@ -171,6 +181,23 @@ defmodule ExWebRTC.DTLSTransport do end end + @impl true + def handle_call(:set_ice_connected, _from, state) do + Logger.debug(""" + Setting ice connected in unexpected DTLS state: #{state.dtls_state}. \ + DTLS handshake won't be performed.\ + """) + + state = %{state | ice_connected: true} + {:reply, :ok, state} + end + + @impl true + def handle_call(:set_ice_disconnected, _from, state) do + state = %{state | ice_connected: false} + {:reply, :ok, state} + end + @impl true def handle_call(:get_certs_info, _from, state) do local_cert_info = %{ @@ -201,9 +228,18 @@ defmodule ExWebRTC.DTLSTransport do end @impl true - def handle_call({:start_dtls, mode, peer_fingerprint}, _from, %{dtls: nil} = state) - when mode in [:active, :passive] do - Logger.debug("Started DTLSTransport with role #{mode}") + def handle_call( + {:start_dtls, mode, peer_fingerprint}, + _from, + %{dtls: dtls, dtls_state: dtls_state} = state + ) + when mode in [:active, :passive] and (dtls == nil or dtls_state == :closed) do + if dtls_state == :closed do + Logger.debug("Re-initializing DTLSTransport with role #{mode}") + else + Logger.debug("Starting DTLSTransport with role #{mode}") + end + ex_dtls_mode = if mode == :active, do: :client, else: :server dtls = @@ -222,13 +258,20 @@ defmodule ExWebRTC.DTLSTransport do data -> send(self(), {:ex_ice, state.ice_pid, {:data, data}}) end - state = %{ - state - | dtls: dtls, - mode: mode, - peer_fingerprint: peer_fingerprint, - buffered_remote_packets: nil - } + state = + %{ + state + | dtls: dtls, + mode: mode, + in_srtp: ExLibSRTP.new(), + out_srtp: ExLibSRTP.new(), + peer_fingerprint: peer_fingerprint, + # clear remote info in case we are re-initializing dtls transport + remote_cert: nil, + remote_base64_cert: nil, + remote_fingerprint: nil + } + |> update_dtls_state(:new) {:reply, :ok, state} end @@ -239,6 +282,17 @@ defmodule ExWebRTC.DTLSTransport do {:reply, {:error, :already_started}, state} end + @impl true + def handle_call(:close, _from, %{state: :closed} = state) do + {:reply, :ok, state} + end + + @impl true + def handle_call(:close, _from, state) do + state = do_close(state, notify: false) + {:reply, :ok, state} + end + @impl true def handle_cast({:send_rtp, data}, %{dtls_state: :connected, ice_connected: true} = state) do case ExLibSRTP.protect(state.out_srtp, data) do @@ -251,7 +305,12 @@ defmodule ExWebRTC.DTLSTransport do @impl true def handle_cast({:send_rtp, _data}, state) do - Logger.debug("Attempted to send RTP in wrong DTLS state: #{state.dtls_state}. Ignoring.") + Logger.debug(""" + Attempted to send RTP in wrong DTLS/ICE state. \ + DTLS state: #{state.dtls_state}, ICE connected: #{state.ice_connected}. \ + Ignoring.\ + """) + {:noreply, state} end @@ -293,6 +352,13 @@ defmodule ExWebRTC.DTLSTransport do {:noreply, state} end + @impl true + def handle_info(:dtls_timeout, %{dtls: nil} = state) do + # ignore stale timers that were scheduled before calling + # close/1 or receiving close_notify DTLS alert + {:noreply, state} + end + @impl true def handle_info(:dtls_timeout, %{buffered_local_packets: buffered_local_packets} = state) do case ExDTLS.handle_timeout(state.dtls) do @@ -327,6 +393,13 @@ defmodule ExWebRTC.DTLSTransport do {:ok, state} -> {:noreply, state} + {:error, :peer_closed_for_writing} -> + # See W3C WebRTC sec. 5.5.1 + # peer_closed_for_writing is returned when the remote side + # sends close_notify alert + state = do_close(state) + {:noreply, state} + {:error, _reason} -> # See W3C WebRTC sec. 5.5. state = update_dtls_state(state, :failed) @@ -345,6 +418,22 @@ defmodule ExWebRTC.DTLSTransport do Logger.debug("Stopping DTLSTransport with reason: #{inspect(reason)}") end + defp handle_ice_data({:data, _data}, %{dtls_state: :closed} = state) do + # Ignore incoming packets when we are in the closed state. + # IMPORTANT!!! This isn't optimal. + # When DTLSTransport has been closed because of receiving close_notify alert, + # it can still be re-used after doing an ice restart with a different peer. + # In such case, we might start getting remote ICE packets before getting remote SDP answer + # (see the case clause below). + # These packets could be buffered and processed once we receive said SDP answer to speedup + # connection establishment time (instead of waiting for retransmissions). + # However, it is hard to differentiate this case from a case where DTLSTransport received + # close_notify alert, but the remote side behaves incorrectly and is still sending ICE packets. + # Buffering incoming packets in closed state, we don't know whether they belong to current or previous + # session, which can lead to hard to find bugs. + {:ok, state} + end + defp handle_ice_data({:data, data}, %{dtls: nil} = state) do # received DTLS data from remote peer before receiving an # SDP answer and initializing the DTLS Transport, will buffer the data @@ -470,11 +559,16 @@ defmodule ExWebRTC.DTLSTransport do :ok end - defp update_dtls_state(%{dtls_state: dtls_state} = state, dtls_state), do: state + defp update_dtls_state(state, dtls_state, otps \\ []) + defp update_dtls_state(%{dtls_state: dtls_state} = state, dtls_state, _opts), do: state - defp update_dtls_state(state, new_dtls_state) do + defp update_dtls_state(state, new_dtls_state, opts) do Logger.debug("Changing DTLS state: #{state.dtls_state} -> #{new_dtls_state}") - notify(state.owner, {:state_change, new_dtls_state}) + + if opts[:notify] != false do + notify(state.owner, {:state_change, new_dtls_state}) + end + %{state | dtls_state: new_dtls_state} end @@ -494,6 +588,23 @@ defmodule ExWebRTC.DTLSTransport do %{state | buffered_remote_rtp_packets: []} end + defp do_close(state, opts \\ []) do + {:ok, packets} = ExDTLS.close(state.dtls) + :ok = do_send(state, packets) + + %{ + state + | buffered_local_packets: nil, + buffered_remote_packets: nil, + buffered_remote_rtp_packets: [], + in_srtp: nil, + out_srtp: nil, + dtls: nil, + mode: nil + } + |> update_dtls_state(:closed, opts) + end + defp do_send(state, data) when is_list(data) do Enum.each(data, &(:ok = do_send(state, &1))) end diff --git a/lib/ex_webrtc/ice_transport.ex b/lib/ex_webrtc/ice_transport.ex index 190bc40a..c601a086 100644 --- a/lib/ex_webrtc/ice_transport.ex +++ b/lib/ex_webrtc/ice_transport.ex @@ -3,7 +3,7 @@ defmodule ExWebRTC.ICETransport do # module implementing this behaviour @type t() :: module() - @type state() :: :checking | :connected | :completed | :failed + @type state() :: :checking | :connected | :completed | :failed | :closed @callback start_link(Keyword.t()) :: {:ok, pid()} @callback on_data(pid(), pid()) :: :ok @@ -19,6 +19,7 @@ defmodule ExWebRTC.ICETransport do @callback set_role(pid(), ExICE.ICEAgent.role()) :: :ok @callback set_remote_credentials(pid(), ufrag :: binary(), pwd :: binary()) :: :ok @callback get_stats(pid()) :: map() + @callback close(pid()) :: :ok @callback stop(pid()) :: :ok end @@ -58,5 +59,7 @@ defmodule ExWebRTC.DefaultICETransport do @impl true defdelegate get_stats(pid), to: ICEAgent @impl true + defdelegate close(pid), to: ICEAgent + @impl true defdelegate stop(pid), to: ICEAgent end diff --git a/lib/ex_webrtc/peer_connection.ex b/lib/ex_webrtc/peer_connection.ex index 8e8623c1..9cc47277 100644 --- a/lib/ex_webrtc/peer_connection.ex +++ b/lib/ex_webrtc/peer_connection.ex @@ -63,7 +63,7 @@ defmodule ExWebRTC.PeerConnection do For the exact meaning, refer to the [RTCDtlsTransport: state property](https://developer.mozilla.org/en-US/docs/Web/API/RTCDtlsTransport/state) """ - @type dtls_transport_state() :: :new | :connecting | :connected | :failed + @type dtls_transport_state() :: :new | :connecting | :connected | :failed | :closed @typedoc """ Possible signaling states. @@ -105,8 +105,8 @@ defmodule ExWebRTC.PeerConnection do | {:track_muted, MediaStreamTrack.id()} | {:track_ended, MediaStreamTrack.id()} | {:data, DataChannel.ref(), binary()} - | {:rtp, MediaStreamTrack.id(), String.t() | nil, ExRTP.Packet.t()}} - | {:rtcp, [{MediaStreamTrack.id() | nil, ExRTCP.Packet.packet()}]} + | {:rtp, MediaStreamTrack.id(), String.t() | nil, ExRTP.Packet.t()} + | {:rtcp, [{MediaStreamTrack.id() | nil, ExRTCP.Packet.packet()}]}} #### NON-MDN-API #### @@ -156,7 +156,7 @@ defmodule ExWebRTC.PeerConnection do Controlling process is a process that receives all of the messages (described by `t:message/0`) from this PeerConnection. """ - @spec controlling_process(peer_connection(), Process.dest()) :: :ok + @spec controlling_process(peer_connection(), Process.dest()) :: :ok | {:error, term()} def controlling_process(peer_connection, controlling_process) do GenServer.call(peer_connection, {:controlling_process, controlling_process}) end @@ -273,7 +273,7 @@ defmodule ExWebRTC.PeerConnection do Can be used for experimental purposes. """ - @spec set_packet_loss(peer_connection(), 0..100) :: :ok + @spec set_packet_loss(peer_connection(), 0..100) :: :ok | {:error, term()} def set_packet_loss(peer_connection, value) when value in 0..100 do GenServer.cast(peer_connection, {:set_packet_loss, value}) end @@ -472,7 +472,7 @@ defmodule ExWebRTC.PeerConnection do peer_connection(), RTPTransceiver.kind() | MediaStreamTrack.t(), direction: RTPTransceiver.direction() - ) :: {:ok, RTPTransceiver.t()} + ) :: {:ok, RTPTransceiver.t()} | {:error, term()} def add_transceiver(peer_connection, kind_or_track, options \\ []) do GenServer.call(peer_connection, {:add_transceiver, kind_or_track, options}) end @@ -507,7 +507,8 @@ defmodule ExWebRTC.PeerConnection do For more information, refer to the [RTCPeerConnection: addTrack() method](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addTrack). """ - @spec add_track(peer_connection(), MediaStreamTrack.t()) :: {:ok, RTPSender.t()} + @spec add_track(peer_connection(), MediaStreamTrack.t()) :: + {:ok, RTPSender.t()} | {:error, term()} def add_track(peer_connection, track) do GenServer.call(peer_connection, {:add_track, track}) end @@ -574,11 +575,20 @@ defmodule ExWebRTC.PeerConnection do @doc """ Closes the PeerConnection. - This function kills the `peer_connection` process. + This function doest not kill the `peer_connection` process. + If you want to stop the `peer_connection` process, see `stop/1`. For more information, refer to the [RTCPeerConnection: close() method](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/close). """ @spec close(peer_connection()) :: :ok def close(peer_connection) do + GenServer.call(peer_connection, :close) + end + + @doc """ + Closes and stops PeerConnection process. + """ + @spec stop(peer_connection()) :: :ok + def stop(peer_connection) do GenServer.stop(peer_connection) end @@ -653,6 +663,15 @@ defmodule ExWebRTC.PeerConnection do {:noreply, state} end + @impl true + def handle_call( + {:controlling_process, _controlling_process}, + _from, + %{signaling_state: :closed} = state + ) do + {:reply, {:error, :invalid_state}, state} + end + @impl true def handle_call({:controlling_process, controlling_process}, _from, state) do state = %{state | owner: controlling_process} @@ -664,6 +683,15 @@ defmodule ExWebRTC.PeerConnection do {:reply, state.config, state} end + @impl true + def handle_call( + {:set_sender_codec, _sender_id, _codec}, + _from, + %{signaling_state: :closed} = state + ) do + {:reply, {:error, :invalid_state}, state} + end + @impl true def handle_call({:set_sender_codec, sender_id, codec}, _from, state) do state.transceivers @@ -829,6 +857,11 @@ defmodule ExWebRTC.PeerConnection do {:reply, {:ok, desc}, state} end + @impl true + def handle_call({:set_local_description, _desc}, _from, %{signaling_state: :closed} = state) do + {:reply, {:error, :invalid_state}, state} + end + @impl true def handle_call({:set_local_description, desc}, _from, state) do case apply_local_description(desc, state) do @@ -859,6 +892,11 @@ defmodule ExWebRTC.PeerConnection do {:reply, desc, state} end + @impl true + def handle_call({:set_remote_description, _desc}, _from, %{signaling_state: :closed} = state) do + {:reply, {:error, :invalid_state}, state} + end + @impl true def handle_call({:set_remote_description, desc}, _from, state) do case apply_remote_description(desc, state) do @@ -889,6 +927,11 @@ defmodule ExWebRTC.PeerConnection do {:reply, desc, state} end + @impl true + def handle_call({:add_ice_candidate, _}, _from, %{signaling_state: :closed} = state) do + {:reply, {:error, :invalid_state}, state} + end + @impl true def handle_call( {:add_ice_candidate, _}, @@ -918,6 +961,11 @@ defmodule ExWebRTC.PeerConnection do {:reply, transceivers, state} end + @impl true + def handle_call({:add_transceiver, _kind, _options}, _from, %{signaling_state: :closed} = state) do + {:reply, {:error, :invalid_state}, state} + end + @impl true def handle_call({:add_transceiver, kind, options}, _from, state) when kind in [:audio, :video] do @@ -945,6 +993,15 @@ defmodule ExWebRTC.PeerConnection do {:reply, {:ok, RTPTransceiver.to_struct(tr)}, state} end + @impl true + def handle_call( + {:set_transceiver_direction, _tr_id, _direction}, + _from, + %{signaling_state: :closed} = state + ) do + {:reply, {:error, :invalid_state}, state} + end + @impl true def handle_call({:set_transceiver_direction, tr_id, direction}, _from, state) do state.transceivers @@ -963,6 +1020,11 @@ defmodule ExWebRTC.PeerConnection do end end + @impl true + def handle_call({:stop_transceiver, _tr_id}, _from, %{signaling_state: :closed} = state) do + {:reply, {:error, :invalid_state}, state} + end + @impl true def handle_call({:stop_transceiver, tr_id}, _from, state) do state.transceivers @@ -985,6 +1047,11 @@ defmodule ExWebRTC.PeerConnection do end end + @impl true + def handle_call({:add_track, _track}, _from, %{signaling_state: :closed} = state) do + {:reply, {:error, :invalid_state}, state} + end + @impl true def handle_call({:add_track, %MediaStreamTrack{kind: kind} = track}, _from, state) do # we ignore the condition that sender has never been used to send @@ -1019,6 +1086,15 @@ defmodule ExWebRTC.PeerConnection do {:reply, {:ok, RTPSender.to_struct(sender)}, state} end + @impl true + def handle_call( + {:replace_track, _sender_id, _track}, + _from, + %{signaling_state: :closed} = state + ) do + {:reply, {:error, :invalid_state}, state} + end + @impl true def handle_call({:replace_track, sender_id, track}, _from, state) do state.transceivers @@ -1044,6 +1120,11 @@ defmodule ExWebRTC.PeerConnection do end end + @impl true + def handle_call({:remove_track, _sender_id}, _from, %{signaling_state: :closed} = state) do + {:reply, {:error, :invalid_state}, state} + end + @impl true def handle_call({:remove_track, sender_id}, _from, state) do state.transceivers @@ -1067,6 +1148,14 @@ defmodule ExWebRTC.PeerConnection do if Code.ensure_loaded?(ExSCTP) do @impl true + def handle_call( + {:create_data_channel, _label, _opts}, + _from, + %{signaling_state: :closed} = state + ) do + {:reply, {:error, :invalid_state}, state} + end + def handle_call({:create_data_channel, label, opts}, _from, state) do ordered = Keyword.get(opts, :ordered, true) lifetime = Keyword.get(opts, :max_packet_life_time) @@ -1075,7 +1164,7 @@ defmodule ExWebRTC.PeerConnection do with true <- byte_size(label) < 65_535, true <- lifetime == nil or max_rtx == nil do - {events, channel, sctp_transport} = + {:ok, events, channel, sctp_transport} = SCTPTransport.add_channel( state.sctp_transport, label, @@ -1094,6 +1183,15 @@ defmodule ExWebRTC.PeerConnection do end end + @impl true + def handle_call( + {:close_data_channel, _channel_ref}, + _from, + %{signaling_state: :closed} = state + ) do + {:reply, {:error, :invalid_state}, state} + end + @impl true def handle_call({:close_data_channel, channel_ref}, _from, state) do {events, sctp_transport} = SCTPTransport.close_channel(state.sctp_transport, channel_ref) @@ -1271,9 +1369,47 @@ defmodule ExWebRTC.PeerConnection do {:reply, stats, state} end + @impl true + def handle_call({:set_packet_loss, _packet_loss}, _from, %{signaling_state: :closed} = state) do + {:reply, {:error, :invalid_state}, state} + end + + @impl true + def handle_call({:set_packet_loss, packet_loss}, _from, state) do + DTLSTransport.set_packet_loss(state.dtls_transport, packet_loss) + {:reply, :ok, state} + end + + @impl true + def handle_call(:close, _from, %{signaling_state: :closed} = state) do + {:reply, :ok, state} + end + + @impl true + def handle_call(:close, _from, state) do + Logger.debug("Closing peer connection") + transceivers = Enum.map(state.transceivers, &RTPTransceiver.stop(&1)) + sctp_transport = SCTPTransport.close_abruptly(state.sctp_transport) + :ok = DTLSTransport.close(state.dtls_transport) + :ok = state.ice_transport.close(state.ice_pid) + + state = + %{ + state + | ice_state: :closed, + dtls_state: :closed, + transceivers: transceivers, + sctp_transport: sctp_transport + } + |> update_signaling_state(:closed, notify: false) + |> update_conn_state(:closed, notify: false) + + {:reply, :ok, state} + end + @impl true def handle_cast({:send_rtp, track_id, packet, opts}, %{conn_state: conn_state} = state) - when conn_state != :failed do + when conn_state not in [:failed, :closed] do rtx? = Keyword.get(opts, :rtx?, false) # TODO: iterating over transceivers is not optimal @@ -1331,7 +1467,7 @@ defmodule ExWebRTC.PeerConnection do @impl true def handle_cast({:send_pli, track_id, rid}, %{conn_state: conn_state} = state) - when conn_state != :failed do + when conn_state not in [:failed, :closed] do state.transceivers |> Enum.with_index() |> Enum.find(fn {tr, _idx} -> tr.receiver.track.id == track_id end) @@ -1365,7 +1501,7 @@ defmodule ExWebRTC.PeerConnection do if Code.ensure_loaded?(ExSCTP) do @impl true def handle_cast({:send_data, channel_ref, data_type, data}, %{conn_state: conn_state} = state) - when conn_state != :failed do + when conn_state not in [:failed, :closed] do {events, sctp_transport} = SCTPTransport.send(state.sctp_transport, channel_ref, data_type, data) @@ -1385,12 +1521,6 @@ defmodule ExWebRTC.PeerConnection do end end - @impl true - def handle_cast({:set_packet_loss, packet_loss}, state) do - DTLSTransport.set_packet_loss(state.dtls_transport, packet_loss) - {:noreply, state} - end - @impl true def handle_info({:ex_ice, _from, {:connection_state_change, new_ice_state}}, state) do state = %{state | ice_state: new_ice_state} @@ -1402,6 +1532,10 @@ defmodule ExWebRTC.PeerConnection do :ok = DTLSTransport.set_ice_connected(state.dtls_transport) end + if new_ice_state == :failed do + :ok = DTLSTransport.set_ice_disconnected(state.dtls_transport) + end + {:noreply, state} end @@ -1955,15 +2089,24 @@ defmodule ExWebRTC.PeerConnection do defp next_signaling_state(:have_remote_pranswer, :remote, :answer), do: {:ok, :stable} defp next_signaling_state(:have_remote_pranswer, _, _), do: {:error, :invalid_state} - defp update_signaling_state(%{signaling_state: signaling_state} = state, signaling_state), - do: state + defp update_signaling_state(state, signaling_state, opts \\ []) + + defp update_signaling_state( + %{signaling_state: signaling_state} = state, + signaling_state, + _opts + ), + do: state - defp update_signaling_state(state, new_signaling_state) do + defp update_signaling_state(state, new_signaling_state, opts) do Logger.debug( - "Changing PeerConnection signaling state state: #{state.signaling_state} -> #{new_signaling_state}" + "Changing PeerConnection signaling state: #{state.signaling_state} -> #{new_signaling_state}" ) - notify(state.owner, {:signaling_state_change, new_signaling_state}) + if opts[:notify] != false do + notify(state.owner, {:signaling_state_change, new_signaling_state}) + end + %{state | signaling_state: new_signaling_state} end @@ -2192,12 +2335,10 @@ defmodule ExWebRTC.PeerConnection do defp get_last_answer(%{current_remote_desc: {:answer, desc}}), do: desc # TODO support :disconnected state - our ICE doesn't provide disconnected state for now - # TODO support :closed state # the order of these clauses is important defp next_conn_state(ice_state, dtls_state) - defp next_conn_state(ice_state, dtls_state) when ice_state == :failed or dtls_state == :failed, - do: :failed + defp next_conn_state(:closed, _dtls_state), do: :closed defp next_conn_state(:failed, _dtls_state), do: :failed @@ -2209,14 +2350,24 @@ defmodule ExWebRTC.PeerConnection do when ice_state in [:new, :checking] or dtls_state in [:new, :connecting], do: :connecting - defp next_conn_state(ice_state, :connected) when ice_state in [:connected, :completed], - do: :connected + # DTLS closed state does not indicate peer connection closed state. + # When DTLS was closed by receiving close_notify alert, it can still + # be reused after doing an ice-restart with a different peer. + # See https://github.com/w3c/webrtc-pc/issues/3053. + defp next_conn_state(ice_state, dtls_state) + when ice_state in [:connected, :completed] and dtls_state in [:connected, :closed], + do: :connected - defp update_conn_state(%{conn_state: conn_state} = state, conn_state), do: state + defp update_conn_state(state, conn_state, opts \\ []) + defp update_conn_state(%{conn_state: conn_state} = state, conn_state, _opts), do: state - defp update_conn_state(state, new_conn_state) do + defp update_conn_state(state, new_conn_state, opts) do Logger.debug("Changing PeerConnection state: #{state.conn_state} -> #{new_conn_state}") - notify(state.owner, {:connection_state_change, new_conn_state}) + + if opts[:notify] != false do + notify(state.owner, {:connection_state_change, new_conn_state}) + end + %{state | conn_state: new_conn_state} end diff --git a/lib/ex_webrtc/rtp_transceiver.ex b/lib/ex_webrtc/rtp_transceiver.ex index 5c431a0f..20478572 100644 --- a/lib/ex_webrtc/rtp_transceiver.ex +++ b/lib/ex_webrtc/rtp_transceiver.ex @@ -469,7 +469,7 @@ defmodule ExWebRTC.RTPTransceiver do @doc false @spec stop(transceiver(), (-> term())) :: transceiver() - def stop(transceiver, on_track_ended) do + def stop(transceiver, on_track_ended \\ fn -> :ok end) do transceiver = if transceiver.stopping, do: transceiver, diff --git a/lib/ex_webrtc/sctp_transport.ex b/lib/ex_webrtc/sctp_transport.ex index 8d9778ad..af7779c4 100644 --- a/lib/ex_webrtc/sctp_transport.ex +++ b/lib/ex_webrtc/sctp_transport.ex @@ -21,7 +21,7 @@ if Code.ensure_loaded?(ExSCTP) do def new do %{ ref: ExSCTP.new(), - connected: false, + state: :new, id_type: nil, timer: nil, channels: %{}, @@ -30,13 +30,14 @@ if Code.ensure_loaded?(ExSCTP) do end @spec connect(t()) :: {[event()], t()} - def connect(%{connected: true} = sctp_transport), do: {[], sctp_transport} - - def connect(sctp_transport) do + def connect(%{state: :new} = sctp_transport) do :ok = ExSCTP.connect(sctp_transport.ref) + sctp_transport = %{sctp_transport | state: :connecting} handle_events(sctp_transport) end + def connect(sctp_transport), do: {[], sctp_transport} + @spec set_role(t(), :active | :passive) :: t() def set_role(%{id_type: t} = sctp_transport, _type) when t != nil, do: sctp_transport def set_role(sctp_transport, :active), do: %{sctp_transport | id_type: :even} @@ -74,7 +75,18 @@ if Code.ensure_loaded?(ExSCTP) do non_neg_integer() | nil, non_neg_integer() | nil ) :: - {[event()], DataChannel.t(), t()} + {:ok, [event()], DataChannel.t(), t()} | {:error, :closed, t()} + def add_channel( + %{state: :closed} = sctp_transport, + _label, + _ordered, + _protocol, + _lifetime, + _max_rtx + ) do + {:error, :closed, sctp_transport} + end + def add_channel(sctp_transport, label, ordered, protocol, lifetime, max_rtx) do channel = %DataChannel{ ref: make_ref(), @@ -92,17 +104,21 @@ if Code.ensure_loaded?(ExSCTP) do sctp_transport = %{sctp_transport | channels: channels, stats: stats} {events, sctp_transport} = - if sctp_transport.connected do + if sctp_transport.state == :connected do sctp_transport = handle_pending_channels(sctp_transport) handle_events(sctp_transport) else {[], sctp_transport} end - {events, channel, sctp_transport} + {:ok, events, channel, sctp_transport} end @spec close_channel(t(), DataChannel.ref()) :: {[event()], t()} + def close_channel(%{state: :closed} = sctp_transport, _ref) do + {[], sctp_transport} + end + def close_channel(sctp_transport, ref) do # TODO: according to spec, this should move to `closing` state # and only then be closed, but oh well... @@ -132,6 +148,10 @@ if Code.ensure_loaded?(ExSCTP) do def get_channel(sctp_transport, ref), do: Map.get(sctp_transport.channels, ref) @spec send(t(), DataChannel.ref(), :string | :binary, binary()) :: {[event()], t()} + def send(%{state: :closed} = sctp_transport, _ref, _type, _data) do + {[], sctp_transport} + end + def send(sctp_transport, ref, type, data) do {ppi, data} = to_raw_data(data, type) @@ -142,10 +162,7 @@ if Code.ensure_loaded?(ExSCTP) do handle_events(%{sctp_transport | stats: stats}) {:ok, %DataChannel{id: id}} -> - Logger.warning( - "Trying to send data over DataChannel with id #{id} that is not opened yet" - ) - + Logger.warning("Trying to send data over DataChannel with id #{id} that is not opened") {[], sctp_transport} :error -> @@ -158,17 +175,37 @@ if Code.ensure_loaded?(ExSCTP) do end @spec handle_timeout(t()) :: {[event()], t()} + def handle_timeout(%{state: :closed} = sctp_transport) do + {[], sctp_transport} + end + def handle_timeout(sctp_transport) do ExSCTP.handle_timeout(sctp_transport.ref) handle_events(sctp_transport) end @spec handle_data(t(), binary()) :: {[event()], t()} + def handle_data(%{state: :closed} = sctp_transport) do + {[], sctp_transport} + end + def handle_data(sctp_transport, data) do :ok = ExSCTP.handle_data(sctp_transport.ref, data) handle_events(sctp_transport) end + @spec close_abruptly(t()) :: t() + def close_abruptly(sctp_transport) do + # This function closes SCTP transport according to + # https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close + channels = + Map.new(sctp_transport.channels, fn {ref, channel} -> + {ref, %{channel | ready_state: :closed}} + end) + + %{sctp_transport | channels: channels, state: :closed} + end + defp handle_pending_channels(sctp_transport) do sctp_transport.channels |> Map.values() @@ -216,7 +253,9 @@ if Code.ensure_loaded?(ExSCTP) do end # if SCTP disconnected, most likely DTLS disconnected, so we won't handle this here explcitly - defp handle_event(sctp_transport, :disconnected), do: {nil, sctp_transport} + defp handle_event(sctp_transport, :disconnected), + do: {nil, %{sctp_transport | state: :closed}} + defp handle_event(sctp_transport, :none), do: {:none, sctp_transport} defp handle_event(sctp_transport, {:transmit, _data} = event), do: {event, sctp_transport} @@ -244,7 +283,7 @@ if Code.ensure_loaded?(ExSCTP) do Logger.debug("SCTP connection has been established") sctp_transport = - %{sctp_transport | connected: true} + %{sctp_transport | state: :connected} |> handle_pending_channels() {nil, sctp_transport} @@ -500,6 +539,8 @@ else {[], nil} end + def close_abruptly(_), do: nil + defp error do text = "DataChannel support is turned off." raise("#{text} #{@tip}") diff --git a/mix.exs b/mix.exs index 40bc7ad3..4ec99ed6 100644 --- a/mix.exs +++ b/mix.exs @@ -57,8 +57,8 @@ defmodule ExWebRTC.MixProject do defp deps do [ {:ex_sdp, "~> 1.0"}, - {:ex_ice, "~> 0.12.0"}, - {:ex_dtls, "~> 0.17.0"}, + {:ex_ice, github: "elixir-webrtc/ex_ice"}, + {:ex_dtls, github: "elixir-webrtc/ex_dtls"}, {:ex_libsrtp, "~> 0.7.1"}, {:ex_rtp, "~> 0.4.0"}, {:ex_rtcp, "~> 0.4.0"}, diff --git a/mix.lock b/mix.lock index 827508a0..f6a3bfa6 100644 --- a/mix.lock +++ b/mix.lock @@ -4,15 +4,15 @@ "bundlex": {:hex, :bundlex, "1.5.4", "3726acd463f4d31894a59bbc177c17f3b574634a524212f13469f41c4834a1d9", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:elixir_uuid, "~> 1.2", [hex: :elixir_uuid, repo: "hexpm", optional: false]}, {:qex, "~> 0.5", [hex: :qex, repo: "hexpm", optional: false]}, {:req, ">= 0.4.0", [hex: :req, repo: "hexpm", optional: false]}, {:zarex, "~> 1.0", [hex: :zarex, repo: "hexpm", optional: false]}], "hexpm", "e745726606a560275182a8ac1c8ebd5e11a659bb7460d8abf30f397e59b4c5d2"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "crc": {:hex, :crc, "0.10.5", "ee12a7c056ac498ef2ea985ecdc9fa53c1bfb4e53a484d9f17ff94803707dfd8", [:mix, :rebar3], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3e673b6495a9525c5c641585af1accba59a1eb33de697bedf341e247012c2c7f"}, - "credo": {:hex, :credo, "1.7.11", "d3e805f7ddf6c9c854fd36f089649d7cf6ba74c42bc3795d587814e3c9847102", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "56826b4306843253a66e47ae45e98e7d284ee1f95d53d1612bb483f88a8cf219"}, + "credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"}, "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, "elixir_uuid": {:hex, :elixir_uuid, "1.2.1", "dce506597acb7e6b0daeaff52ff6a9043f5919a4c3315abb4143f0b00378c097", [:mix], [], "hexpm", "f7eba2ea6c3555cea09706492716b0d87397b88946e6380898c2889d68585752"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, - "ex_doc": {:hex, :ex_doc, "0.37.3", "f7816881a443cd77872b7d6118e8a55f547f49903aef8747dbcb345a75b462f9", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "e6aebca7156e7c29b5da4daa17f6361205b2ae5f26e5c7d8ca0d3f7e18972233"}, - "ex_dtls": {:hex, :ex_dtls, "0.17.0", "dbe1d494583a307c26148cb5ea5d7c14e65daa8ec96cc73002cc3313ce4b9a81", [:mix], [{:bundlex, "~> 1.5.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "3eaa7221ec08fa9e4bc9430e426cbd5eb4feb8d8f450b203cf39b2114a94d713"}, - "ex_ice": {:hex, :ex_ice, "0.12.0", "b52ec3ff878d5fb632ef9facc7657dfdf59e2ff9f23e634b0918e6ce1a05af48", [:mix], [{:elixir_uuid, "~> 1.0", [hex: :elixir_uuid, repo: "hexpm", optional: false]}, {:ex_stun, "~> 0.2.0", [hex: :ex_stun, repo: "hexpm", optional: false]}, {:ex_turn, "~> 0.2.0", [hex: :ex_turn, repo: "hexpm", optional: false]}], "hexpm", "a86024a5fbf9431082784be4bb3606d3cde9218fb325a9f208ccd6e0abfd0d73"}, + "ex_doc": {:hex, :ex_doc, "0.38.2", "504d25eef296b4dec3b8e33e810bc8b5344d565998cd83914ffe1b8503737c02", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "732f2d972e42c116a70802f9898c51b54916e542cc50968ac6980512ec90f42b"}, + "ex_dtls": {:git, "https://github.com/elixir-webrtc/ex_dtls.git", "2773c01f0593cf31d5bf5534c40b2def17abc921", []}, + "ex_ice": {:git, "https://github.com/elixir-webrtc/ex_ice.git", "4854d812cd41d28974a95e0f76cdb7f139758965", []}, "ex_libsrtp": {:hex, :ex_libsrtp, "0.7.2", "211bd89c08026943ce71f3e2c0231795b99cee748808ed3ae7b97cd8d2450b6b", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2e20645d0d739a4ecdcf8d4810a0c198120c8a2f617f2b75b2e2e704d59f492a"}, "ex_rtcp": {:hex, :ex_rtcp, "0.4.0", "f9e515462a9581798ff6413583a25174cfd2101c94a2ebee871cca7639886f0a", [:mix], [], "hexpm", "28956602cf210d692fcdaf3f60ca49681634e1deb28ace41246aee61ee22dc3b"}, "ex_rtp": {:hex, :ex_rtp, "0.4.0", "1f1b5c1440a904706011e3afbb41741f5da309ce251cb986690ce9fd82636658", [:mix], [], "hexpm", "0f72d80d5953a62057270040f0f1ee6f955c08eeae82ac659c038001d7d5a790"}, @@ -29,14 +29,14 @@ "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, "membrane_precompiled_dependency_provider": {:hex, :membrane_precompiled_dependency_provider, "0.1.2", "8af73b7dc15ba55c9f5fbfc0453d4a8edfb007ade54b56c37d626be0d1189aba", [:mix], [{:bundlex, "~> 1.4", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "7fe3e07361510445a29bee95336adde667c4162b76b7f4c8af3aeb3415292023"}, - "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, + "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, "mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "qex": {:hex, :qex, "0.5.1", "0d82c0f008551d24fffb99d97f8299afcb8ea9cf99582b770bd004ed5af63fd6", [:mix], [], "hexpm", "935a39fdaf2445834b95951456559e9dc2063d0a055742c558a99987b38d6bab"}, "req": {:hex, :req, "0.5.10", "a3a063eab8b7510785a467f03d30a8d95f66f5c3d9495be3474b61459c54376c", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "8a604815743f8a2d3b5de0659fa3137fa4b1cffd636ecb69b30b2b9b2c2559be"}, - "rustler": {:hex, :rustler, "0.36.1", "2d4b1ff57ea2789a44756a40dbb5fbb73c6ee0a13d031dcba96d0a5542598a6a", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.7", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "f3fba4ad272970e0d1bc62972fc4a99809651e54a125c5242de9bad4574b2d02"}, + "rustler": {:hex, :rustler, "0.36.2", "6c2142f912166dfd364017ab2bf61242d4a5a3c88e7b872744642ae004b82501", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.7", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "93832a6dbc1166739a19cd0c25e110e4cf891f16795deb9361dfcae95f6c88fe"}, "shmex": {:hex, :shmex, "0.5.1", "81dd209093416bf6608e66882cb7e676089307448a1afd4fc906c1f7e5b94cf4", [:mix], [{:bunch_native, "~> 0.5.0", [hex: :bunch_native, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.0", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "c29f8286891252f64c4e1dac40b217d960f7d58def597c4e606ff8fbe71ceb80"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, "toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"}, diff --git a/test/ex_webrtc/dtls_transport_test.exs b/test/ex_webrtc/dtls_transport_test.exs index e556cb68..0efbe646 100644 --- a/test/ex_webrtc/dtls_transport_test.exs +++ b/test/ex_webrtc/dtls_transport_test.exs @@ -10,8 +10,10 @@ defmodule ExWebRTC.DTLSTransportTest do |> Utils.hex_dump() @rtp_header <<1::1, 0::1, 0::1, 0::1, 0::4, 0::1, 96::7, 1::16, 1::32, 1::32>> + @next_rtp_header <<1::1, 0::1, 0::1, 0::1, 0::4, 0::1, 96::7, 2::16, 1::32, 1::32>> @rtp_payload <<0>> @rtp_packet <<@rtp_header::binary, @rtp_payload::binary>> + @next_rtp_packet <<@next_rtp_header::binary, @rtp_payload::binary>> # empty rr packet @rtcp_rr_header <<2::2, 0::1, 0::5, 201::8, 1::16, 1::32>> @@ -68,6 +70,9 @@ defmodule ExWebRTC.DTLSTransportTest do @impl true def get_stats(_ice_pid), do: %{} + @impl true + def close(ice_pid), do: GenServer.call(ice_pid, :close) + @impl true def stop(ice_pid), do: GenServer.stop(ice_pid) @@ -87,6 +92,12 @@ defmodule ExWebRTC.DTLSTransportTest do {:reply, state.role, state} end + @impl true + def handle_call(:close, _from, state) do + # TODO implement + {:reply, :ok, state} + end + @impl true def handle_cast({:set_role, role}, state) do {:noreply, %{state | role: role}} @@ -114,7 +125,7 @@ defmodule ExWebRTC.DTLSTransportTest do %{dtls: dtls, ice_transport: MockICETransport, ice_pid: ice_pid} end - test "cannot send data when handshake not finished", %{dtls: dtls} do + test "cannot send data when handshake has not finished", %{dtls: dtls} do DTLSTransport.send_rtp(dtls, @rtp_packet) refute_receive {:mock_ice, _data} @@ -128,7 +139,7 @@ defmodule ExWebRTC.DTLSTransportTest do :ok = DTLSTransport.set_ice_connected(dtls) remote_dtls = ExDTLS.init(mode: :client, dtls_srtp: true) - {packets, _timeout} = ExDTLS.do_handshake(remote_dtls) + {:ok, packets, _timeout} = ExDTLS.do_handshake(remote_dtls) Enum.each(packets, &ice_transport.send_dtls(ice_pid, {:data, &1})) refute_receive {:mock_ice, _packets} @@ -139,11 +150,42 @@ defmodule ExWebRTC.DTLSTransportTest do assert is_binary(packets) end - test "cannot start dtls more than once", %{dtls: dtls} do + test "cannot start dtls more than once if it is not closed", %{dtls: dtls} do assert :ok = DTLSTransport.start_dtls(dtls, :passive, @fingerprint) assert {:error, :already_started} = DTLSTransport.start_dtls(dtls, :passive, @fingerprint) end + test "can restart dtls if it is closed", %{ + dtls: dtls, + ice_transport: ice_transport, + ice_pid: ice_pid + } do + :ok = DTLSTransport.start_dtls(dtls, :active, @fingerprint) + remote_dtls = ExDTLS.init(mode: :server, dtls_srtp: true) + :ok = DTLSTransport.set_ice_connected(dtls) + + # perform DTLS-SRTP handshake + assert {:ok, _remote_lkm, _remote_rkm, _remote_profile} = + check_handshake(dtls, ice_transport, ice_pid, remote_dtls) + + assert_receive {:dtls_transport, ^dtls, {:state_change, :connecting}} + assert_receive {:dtls_transport, ^dtls, {:state_change, :connected}} + + # close and send close_notify from remote to local + assert {:ok, packets} = ExDTLS.close(remote_dtls) + Enum.each(packets, &ice_transport.send_dtls(ice_pid, {:data, &1})) + + # assert local received close_notify and moved to the closed state + assert_receive {:dtls_transport, ^dtls, {:state_change, :closed}} + + # start dtls transport again + # flush old messages + flush_mailbox(dtls) + assert :ok = DTLSTransport.start_dtls(dtls, :active, @fingerprint) + # assert dlts moved to the new state + assert_receive {:dtls_transport, ^dtls, {:state_change, :new}} + end + test "initiates DTLS handshake when in active mode", %{dtls: dtls} do :ok = DTLSTransport.start_dtls(dtls, :active, @fingerprint) @@ -180,7 +222,7 @@ defmodule ExWebRTC.DTLSTransportTest do :ok = DTLSTransport.start_dtls(dtls, :passive, @fingerprint) remote_dtls = ExDTLS.init(mode: :client, dtls_srtp: true) - {packets, _timeout} = ExDTLS.do_handshake(remote_dtls) + {:ok, packets, _timeout} = ExDTLS.do_handshake(remote_dtls) Enum.each(packets, &ice_transport.send_dtls(ice_pid, {:data, &1})) refute_receive {:mock_ice, _packets} @@ -200,7 +242,7 @@ defmodule ExWebRTC.DTLSTransportTest do :ok = DTLSTransport.set_ice_connected(dtls) - assert :ok = check_handshake(dtls, ice_transport, ice_pid, remote_dtls) + assert {:ok, _, _, _} = check_handshake(dtls, ice_transport, ice_pid, remote_dtls) assert_receive {:dtls_transport, ^dtls, {:state_change, :connecting}} assert_receive {:dtls_transport, ^dtls, {:state_change, :connected}} @@ -228,12 +270,12 @@ defmodule ExWebRTC.DTLSTransportTest do :ok = DTLSTransport.start_dtls(dtls, :passive, remote_fingerprint) - {packets, _timeout} = ExDTLS.do_handshake(remote_dtls) + {:ok, packets, _timeout} = ExDTLS.do_handshake(remote_dtls) :ok = DTLSTransport.set_ice_connected(dtls) Enum.each(packets, &ice_transport.send_dtls(ice_pid, {:data, &1})) - assert :ok == check_handshake(dtls, ice_transport, ice_pid, remote_dtls) + assert {:ok, _, _, _} = check_handshake(dtls, ice_transport, ice_pid, remote_dtls) assert_receive {:dtls_transport, ^dtls, {:state_change, :connecting}} assert_receive {:dtls_transport, ^dtls, {:state_change, :connected}} @@ -256,7 +298,7 @@ defmodule ExWebRTC.DTLSTransportTest do :ok = DTLSTransport.set_ice_connected(dtls) - assert :ok = check_handshake(dtls, ice_transport, ice_pid, remote_dtls) + assert {:ok, _, _, _} = check_handshake(dtls, ice_transport, ice_pid, remote_dtls) assert_receive {:dtls_transport, ^dtls, {:state_change, :connecting}} assert_receive {:dtls_transport, ^dtls, {:state_change, :connected}} @@ -278,6 +320,112 @@ defmodule ExWebRTC.DTLSTransportTest do refute_receive {:mock_ice, _datachannel_packet} end + test "closes on calling close/1", %{ + dtls: dtls, + ice_transport: ice_transport, + ice_pid: ice_pid + } do + :ok = DTLSTransport.start_dtls(dtls, :active, @fingerprint) + remote_dtls = ExDTLS.init(mode: :server, dtls_srtp: true) + + :ok = DTLSTransport.set_ice_connected(dtls) + + # perform DTLS-SRTP handshake + assert {:ok, remote_lkm, remote_rkm, remote_profile} = + check_handshake(dtls, ice_transport, ice_pid, remote_dtls) + + assert_receive {:dtls_transport, ^dtls, {:state_change, :connecting}} + assert_receive {:dtls_transport, ^dtls, {:state_change, :connected}} + + # create SRTP for remote side + {_remote_in_srtp, remote_out_srtp} = setup_srtp(remote_lkm, remote_rkm, remote_profile) + + # assert packets can flow from local to remote + :ok = DTLSTransport.send_rtp(dtls, @rtp_packet) + assert_receive {:mock_ice, _rtp_packet} + + # close transport + assert :ok = DTLSTransport.close(dtls) + + # flush close notify + assert_receive {:mock_ice, _dtls_close_notify} + + # assert that data cannot be sent by local + :ok = DTLSTransport.send_rtp(dtls, @rtp_packet) + refute_receive {:mock_ice, _rtp_packet} + :ok = DTLSTransport.send_rtcp(dtls, @rtcp_rr_packet) + refute_receive {:mock_ice, _rtcp_packet} + :ok = DTLSTransport.send_data(dtls, <<1, 2, 3>>) + refute_receive {:mock_ice, _datachannel_packet} + + # assert that incoming data is ignored by local + {:ok, protected} = ExLibSRTP.protect(remote_out_srtp, @rtp_packet) + ice_transport.send_dtls(ice_pid, {:data, protected}) + refute_receive {:dtls_transport, ^dtls, {:rtp, _data}} + + # assert getting certs still works + assert %{local_cert_info: local_cert, remote_cert_info: remote_cert} = + DTLSTransport.get_certs_info(dtls) + + assert local_cert != nil + assert remote_cert != nil + assert DTLSTransport.get_fingerprint(dtls) != nil + end + + test "closes on receiving close_notify DTLS alert", %{ + dtls: dtls, + ice_transport: ice_transport, + ice_pid: ice_pid + } do + :ok = DTLSTransport.start_dtls(dtls, :active, @fingerprint) + remote_dtls = ExDTLS.init(mode: :server, dtls_srtp: true) + + :ok = DTLSTransport.set_ice_connected(dtls) + + # perform DTLS-SRTP handshake + assert {:ok, remote_lkm, remote_rkm, remote_profile} = + check_handshake(dtls, ice_transport, ice_pid, remote_dtls) + + assert_receive {:dtls_transport, ^dtls, {:state_change, :connecting}} + assert_receive {:dtls_transport, ^dtls, {:state_change, :connected}} + + # create SRTP for remote side + {_remote_in_srtp, remote_out_srtp} = setup_srtp(remote_lkm, remote_rkm, remote_profile) + + # assert packets can flow from remote to local + {:ok, protected} = ExLibSRTP.protect(remote_out_srtp, @rtp_packet) + ice_transport.send_dtls(ice_pid, {:data, protected}) + assert_receive {:dtls_transport, ^dtls, {:rtp, @rtp_packet}} + + # close and send close_notify from remote to local + assert {:ok, packets} = ExDTLS.close(remote_dtls) + Enum.each(packets, &ice_transport.send_dtls(ice_pid, {:data, &1})) + + # assert local received close_notify and moved to the closed state + assert_receive {:dtls_transport, ^dtls, {:state_change, :closed}} + + # assert that data cannot be sent by local + :ok = DTLSTransport.send_rtp(dtls, @rtp_packet) + refute_receive {:mock_ice, _rtp_packet} + :ok = DTLSTransport.send_rtcp(dtls, @rtcp_rr_packet) + refute_receive {:mock_ice, _rtcp_packet} + :ok = DTLSTransport.send_data(dtls, <<1, 2, 3>>) + refute_receive {:mock_ice, _datachannel_packet} + + # assert that incoming data is ignored by local + {:ok, protected} = ExLibSRTP.protect(remote_out_srtp, @next_rtp_packet) + ice_transport.send_dtls(ice_pid, {:data, protected}) + refute_receive {:dtls_transport, ^dtls, {:rtp, _data}} + + # assert getting certs still works + assert %{local_cert_info: local_cert, remote_cert_info: remote_cert} = + DTLSTransport.get_certs_info(dtls) + + assert local_cert != nil + assert remote_cert != nil + assert DTLSTransport.get_fingerprint(dtls) != nil + end + defp check_handshake(dtls, ice_transport, ice_pid, remote_dtls) do assert_receive {:mock_ice, packets} @@ -289,12 +437,12 @@ defmodule ExWebRTC.DTLSTransportTest do Enum.each(packets, &ice_transport.send_dtls(ice_pid, {:data, &1})) check_handshake(dtls, ice_transport, ice_pid, remote_dtls) - {:handshake_finished, _, _, _, packets} -> + {:handshake_finished, lkm, rkm, profile, packets} -> Enum.each(packets, &ice_transport.send_dtls(ice_pid, {:data, &1})) - :ok + {:ok, lkm, rkm, profile} - {:handshake_finished, _, _, _} -> - :ok + {:handshake_finished, lkm, rkm, profile} -> + {:ok, lkm, rkm, profile} end end @@ -302,4 +450,40 @@ defmodule ExWebRTC.DTLSTransportTest do assert :ok == DTLSTransport.stop(dtls) assert false == Process.alive?(dtls) end + + defp setup_srtp(lkm, rkm, profile) do + in_srtp = ExLibSRTP.new() + out_srtp = ExLibSRTP.new() + + {:ok, crypto_profile} = + ExLibSRTP.Policy.crypto_profile_from_dtls_srtp_protection_profile(profile) + + inbound_policy = %ExLibSRTP.Policy{ + ssrc: :any_inbound, + key: rkm, + rtp: crypto_profile, + rtcp: crypto_profile + } + + :ok = ExLibSRTP.add_stream(in_srtp, inbound_policy) + + outbound_policy = %ExLibSRTP.Policy{ + ssrc: :any_outbound, + key: lkm, + rtp: crypto_profile, + rtcp: crypto_profile + } + + :ok = ExLibSRTP.add_stream(out_srtp, outbound_policy) + + {in_srtp, out_srtp} + end + + defp flush_mailbox(pid) do + receive do + {:dtls_transport, ^pid, _msg} -> flush_mailbox(pid) + after + 0 -> :ok + end + end end diff --git a/test/ex_webrtc/peer_connection_test.exs b/test/ex_webrtc/peer_connection_test.exs index 246e57fc..39fdfc78 100644 --- a/test/ex_webrtc/peer_connection_test.exs +++ b/test/ex_webrtc/peer_connection_test.exs @@ -206,17 +206,21 @@ defmodule ExWebRTC.PeerConnectionTest do test "controlling process" do test_pid = self() - spawn(fn -> - # The first notifications are sent in PeerConnection's init callback - - # assert they will land in the outer process. - {:ok, pid} = PeerConnection.start_link(controlling_process: test_pid) - # From now, all notifications should land in the inner process. - assert :ok = PeerConnection.controlling_process(pid, self()) - :ok = PeerConnection.add_transceiver(pid, :audio) - assert_receive {:ex_webrtc, _pid, :negotiation_needed} - end) + task = + Task.async(fn -> + # The first notifications are sent in PeerConnection's init callback - + # assert they will land in the outer process. + {:ok, pid} = PeerConnection.start_link(controlling_process: test_pid) + # From now, all notifications should land in the inner process. + assert :ok = PeerConnection.controlling_process(pid, self()) + {:ok, _tr} = PeerConnection.add_transceiver(pid, :audio) + assert_receive {:ex_webrtc, _pid, :negotiation_needed} + :ok + end) assert_receive {:ex_webrtc, _pid, {:connection_state_change, :new}} + + assert Task.await(task) == :ok end test "set_sender_codec/3" do @@ -289,13 +293,13 @@ defmodule ExWebRTC.PeerConnectionTest do # we can't compare `all` and `expected` with `==`. assert MapSet.subset?(expected, all) - :ok = PeerConnection.close(pc1) + :ok = PeerConnection.stop(pc1) all = PeerConnection.get_all_running() |> MapSet.new() refute MapSet.member?(all, pc1) assert MapSet.member?(all, pc2) - :ok = PeerConnection.close(pc2) + :ok = PeerConnection.stop(pc2) end test "get_dtls_transport_state/1" do @@ -1068,10 +1072,95 @@ defmodule ExWebRTC.PeerConnectionTest do assert length(Map.get(groups, :candidate_pair, [])) > 0 end - test "close/1" do + describe "close/1" do + test "caller" do + # test how does the side that calls close/1 behave + {:ok, pc1} = PeerConnection.start_link() + {:ok, pc2} = PeerConnection.start_link() + track = MediaStreamTrack.new(:audio) + {:ok, _sender} = PeerConnection.add_track(pc1, track) + {:ok, data_channel} = PeerConnection.create_data_channel(pc1, "label") + :ok = negotiate(pc1, pc2) + :ok = connect(pc1, pc2) + + # wait for data channel to be open + assert_receive {:ex_webrtc, ^pc1, {:data_channel_state_change, _dc_ref, :open}} + + assert :ok = PeerConnection.close(pc1) + assert true = Process.alive?(pc1) + + # Calling close/1 shouldn't result in emitting following events. + # See https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close + refute_received {:ex_webrtc, ^pc1, {:signaling_state_change, :closed}} + refute_received {:ex_webrtc, ^pc1, {:ice_connection_state_change, :closed}} + refute_received {:ex_webrtc, ^pc1, {:dtls_transport_state_change, :closed}} + refute_received {:ex_webrtc, ^pc1, {:connection_state_change, :closed}} + + # Transceivers and data channels should be stopped/closed + [%{stopped: true}] = PeerConnection.get_transceivers(pc1) + %{ready_state: :closed} = PeerConnection.get_data_channel(pc1, data_channel.ref) + + # Try to send RTP, it should be ignored + packet = ExRTP.Packet.new(<<1, 2, 3>>) + :ok = PeerConnection.send_rtp(pc1, track.id, packet) + refute_receive {:ex_webrtc, ^pc2, {:rtp, _track_id, nil, _packet}} + + # Try to send data, it should be ignored + :ok = PeerConnection.send_data(pc1, data_channel.ref, <<1, 2, 3>>) + refute_receive {:ex_webrtc, ^pc2, {:data, _dc_ref, _data}} + + # Try to send RTCP, it should be ignored + :ok = PeerConnection.send_pli(pc1, track.id) + refute_receive {:ex_webrtc, ^pc2, {:rtcp, _}} + end + + test "receiver" do + # test how does the side that receives close_notify alert behave + {:ok, pc1} = PeerConnection.start_link() + {:ok, pc2} = PeerConnection.start_link() + track = MediaStreamTrack.new(:audio) + {:ok, _sender} = PeerConnection.add_track(pc1, track) + {:ok, data_channel} = PeerConnection.create_data_channel(pc1, "label") + :ok = negotiate(pc1, pc2) + :ok = connect(pc1, pc2) + + # wait for data channel to be open + assert_receive {:ex_webrtc, ^pc1, {:data_channel_state_change, _dc_ref, :open}} + + assert :ok = PeerConnection.close(pc2) + + # only dtls transport should move to the closed state + # this way peer connection can do ice restart with a different peer + assert_receive {:ex_webrtc, ^pc1, {:dtls_transport_state_change, :closed}} + refute_receive {:ex_webrtc, ^pc1, {:signaling_state_change, :closed}} + refute_receive {:ex_webrtc, ^pc1, {:ice_connection_state_change, :closed}} + refute_receive {:ex_webrtc, ^pc1, {:connection_state_change, :closed}} + + # the process should still be alive + assert true = Process.alive?(pc1) + + [%{stopped: false}] = PeerConnection.get_transceivers(pc1) + %{ready_state: :open} = PeerConnection.get_data_channel(pc1, data_channel.ref) + + # try to do ice-restart with a different peer + flush_mailbox(pc1) + + {:ok, pc3} = PeerConnection.start_link() + + assert :ok = negotiate(pc1, pc3, ice_restart: true) + assert :ok = connect(pc1, pc3) + + assert_received {:ex_webrtc, ^pc1, {:connection_state_change, :connecting}} + + :ok = PeerConnection.send_rtp(pc1, track.id, ExRTP.Packet.new(<<1, 2, 3>>)) + assert_receive {:ex_webrtc, ^pc3, {:rtp, _track_id, nil, _packet}} + end + end + + test "stop/1" do {:ok, pc} = PeerConnection.start() {:links, links} = Process.info(pc, :links) - assert :ok == PeerConnection.close(pc) + assert :ok == PeerConnection.stop(pc) assert false == Process.alive?(pc) Enum.each(links, fn link -> @@ -1361,8 +1450,8 @@ defmodule ExWebRTC.PeerConnectionTest do assert ExSDP.get_attributes(audio1, :fmtp) == ExSDP.get_attributes(audio2, :fmtp) - :ok = PeerConnection.close(pc1) - :ok = PeerConnection.close(pc2) + :ok = PeerConnection.stop(pc1) + :ok = PeerConnection.stop(pc2) end test "reject incoming track" do @@ -1433,4 +1522,12 @@ defmodule ExWebRTC.PeerConnectionTest do refute_receive {:ex_webrtc, ^pc1, :negotiation_needed} refute_receive {:ex_webrtc, ^pc2, :negotiation_needed}, 0 end + + defp flush_mailbox(pid) do + receive do + {:ex_webrtc, ^pid, _msg} -> flush_mailbox(pid) + after + 0 -> :ok + end + end end diff --git a/test/ex_webrtc/rtp_transceiver_test.exs b/test/ex_webrtc/rtp_transceiver_test.exs index b8c25dc1..c9fc58c7 100644 --- a/test/ex_webrtc/rtp_transceiver_test.exs +++ b/test/ex_webrtc/rtp_transceiver_test.exs @@ -5,7 +5,7 @@ defmodule ExWebRTC.RTPTransceiverTest do {:ok, pc} = PeerConnection.start_link() @config PeerConnection.get_configuration(pc) - :ok = PeerConnection.close(pc) + :ok = PeerConnection.stop(pc) @ssrc 1234 @rtx_ssrc 2345 diff --git a/test/support/test_utils.ex b/test/support/test_utils.ex index 60065366..e56e6cfa 100644 --- a/test/support/test_utils.ex +++ b/test/support/test_utils.ex @@ -3,9 +3,10 @@ defmodule ExWebRTC.Support.TestUtils do alias ExWebRTC.PeerConnection - @spec negotiate(PeerConnection.peer_connection(), PeerConnection.peer_connection()) :: :ok - def negotiate(pc1, pc2) do - {:ok, offer} = PeerConnection.create_offer(pc1) + @spec negotiate(PeerConnection.peer_connection(), PeerConnection.peer_connection(), Keyword.t()) :: + :ok + def negotiate(pc1, pc2, opts \\ []) do + {:ok, offer} = PeerConnection.create_offer(pc1, ice_restart: opts[:ice_restart] || false) :ok = PeerConnection.set_local_description(pc1, offer) :ok = PeerConnection.set_remote_description(pc2, offer) {:ok, answer} = PeerConnection.create_answer(pc2)