subscriber prototype

This commit is contained in:
Théo Monnom
2022-09-25 22:32:21 +02:00
parent 4067a9add6
commit 6ad2563348
12 changed files with 448 additions and 142 deletions
@@ -26,6 +26,8 @@ class IceCandidate {
std::unique_ptr<webrtc::IceCandidateInterface> ice_candidate_;
};
std::unique_ptr<IceCandidate> create_ice_candidate(rust::String sdp_mid, int sdp_mline_index, rust::String sdp);
static std::unique_ptr<IceCandidate> _unique_ice_candidate() {
return nullptr; // Ignore
}
@@ -43,6 +45,8 @@ class SessionDescription {
std::unique_ptr<webrtc::SessionDescriptionInterface> session_description_;
};
std::unique_ptr<SessionDescription> create_session_description(SdpType type, rust::String sdp);
static std::unique_ptr<SessionDescription> _unique_session_description() {
return nullptr; // Ignore
}
@@ -21,6 +21,8 @@ enum class PeerConnectionState;
enum class SignalingState;
enum class IceConnectionState;
enum class IceGatheringState;
enum class SdpType;
struct SdpParseError;
struct RTCOfferAnswerOptions;
struct RTCError;
struct DataChannelInit;
@@ -4,14 +4,23 @@
#include "livekit/jsep.h"
#include <iomanip>
#include <memory>
#include "libwebrtc-sys/src/jsep.rs.h"
#include "livekit/rtc_error.h"
#include "rtc_base/ref_counted_object.h"
namespace livekit {
const std::string& serialize_sdp_error(webrtc::SdpParseError error) {
std::stringstream ss;
ss << std::hex << std::setfill('0');
ss << std::setw(8) << (uint32_t)error.line.length();
ss << std::dec << std::setw(1) << error.line;
ss << std::dec << std::setw(1) << error.description;
return ss.str();
}
IceCandidate::IceCandidate(
std::unique_ptr<webrtc::IceCandidateInterface> ice_candidate)
: ice_candidate_(std::move(ice_candidate)) {}
@@ -26,6 +35,20 @@ std::unique_ptr<webrtc::IceCandidateInterface> IceCandidate::release() {
return std::move(ice_candidate_);
}
std::unique_ptr<IceCandidate> create_ice_candidate(rust::String sdp_mid,
int sdp_mline_index,
rust::String sdp) {
webrtc::SdpParseError error;
auto ice_rtc = webrtc::CreateIceCandidate(sdp_mid.c_str(), sdp_mline_index,
sdp.c_str(), &error);
if (!ice_rtc) {
throw std::runtime_error(serialize_sdp_error(error));
}
return std::make_unique<IceCandidate>(
std::unique_ptr<webrtc::IceCandidateInterface>(ice_rtc));
}
SessionDescription::SessionDescription(
std::unique_ptr<webrtc::SessionDescriptionInterface> session_description)
: session_description_(std::move(session_description)) {}
@@ -45,6 +68,19 @@ SessionDescription::release() {
return std::move(session_description_);
}
std::unique_ptr<SessionDescription> create_session_description(
SdpType type,
rust::String sdp) {
webrtc::SdpParseError error;
auto rtc_sdp = webrtc::CreateSessionDescription(
static_cast<webrtc::SdpType>(type), sdp.c_str(), &error);
if (!rtc_sdp) {
throw std::runtime_error(serialize_sdp_error(error));
}
return std::make_unique<SessionDescription>(std::move(rtc_sdp));
}
// CreateSdpObserver
NativeCreateSdpObserver::NativeCreateSdpObserver(
@@ -1,4 +1,6 @@
use std::fmt::{Debug, Formatter};
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::str::FromStr;
use cxx::UniquePtr;
@@ -6,6 +8,21 @@ use crate::rtc_error::ffi::RTCError;
#[cxx::bridge(namespace = "livekit")]
pub mod ffi {
#[derive(Debug)]
#[repr(i32)]
pub enum SdpType {
Offer,
PrAnswer,
Answer,
Rollback,
}
#[derive(Debug)]
pub struct SdpParseError {
pub line: String,
pub description: String,
}
extern "Rust" {
type CreateSdpObserverWrapper;
fn on_success(
@@ -47,11 +64,22 @@ pub mod ffi {
observer: Box<SetRemoteSdpObserverWrapper>,
) -> UniquePtr<NativeSetRemoteSdpObserverHandle>;
fn create_ice_candidate(sdp_mid: String, sdp_mline_index: i32, sdp: String) -> Result<UniquePtr<IceCandidate>>;
fn create_session_description(sdp_type: SdpType, sdp: String) -> Result<UniquePtr<SessionDescription>>;
fn _unique_ice_candidate() -> UniquePtr<IceCandidate>; // Ignore
fn _unique_session_description() -> UniquePtr<SessionDescription>; // Ignore
}
}
impl Error for ffi::SdpParseError {}
impl Display for ffi::SdpParseError {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "SdpParseError occurred {}: {}", self.line, self.description)
}
}
impl Debug for ffi::SessionDescription {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "{}", self.stringify())
@@ -59,6 +87,7 @@ impl Debug for ffi::SessionDescription {
}
unsafe impl Send for ffi::SessionDescription {}
unsafe impl Sync for ffi::SessionDescription {}
impl Debug for ffi::IceCandidate {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
@@ -67,9 +96,38 @@ impl Debug for ffi::IceCandidate {
}
unsafe impl Send for ffi::IceCandidate {}
unsafe impl Sync for ffi::IceCandidate {}
impl ffi::SdpParseError {
/// # Safety
/// The value must be correctly encoded
pub unsafe fn from(value: &str) -> Self {
// Parse the hex encoded error from c++
let line_length = u32::from_str_radix(&value[0..8], 16).unwrap() as usize + 8;
let line = String::from(&value[8..line_length]);
let description = String::from(&value[line_length..]);
Self {
line,
description,
}
}
}
impl FromStr for ffi::SdpType {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"offer" => Ok(ffi::SdpType::Offer),
"pranswer" => Ok(ffi::SdpType::PrAnswer),
"answer" => Ok(ffi::SdpType::Answer),
"rollback" => Ok(ffi::SdpType::Rollback),
_ => Err(()),
}
}
}
// CreateSdpObserver
pub trait CreateSdpObserver: Send {
@@ -134,3 +192,37 @@ impl SetRemoteSdpObserverWrapper {
self.observer.on_set_remote_description_complete(error);
}
}
#[cfg(test)]
mod tests {
use log::info;
use crate::jsep::ffi;
#[test]
fn throw_error() {
let sdp_string = "v=0
o=- 6549709950142776241 2 IN IP4 127.0.0.1
s=-
t=0 0
======================== ERROR HERE
a=group:BUNDLE 0
a=extmap-allow-mixed
a=msid-semantic: WMS
m=application 9 UDP/DTLS/SCTP webrtc-datachannel
c=IN IP4 0.0.0.0
a=ice-ufrag:Tw7h
a=ice-pwd:6XOVUD6HpcB4c1M8EB8jXJE9
a=ice-options:trickle
a=fingerprint:sha-256 4F:EC:23:59:5D:A5:E6:3E:3E:5D:8A:09:B6:FA:04:AA:19:99:49:67:BD:65:93:06:BB:EE:AC:D5:21:0F:57:D6
a=setup:actpass
a=mid:0
a=sctp-port:5000
a=max-message-size:262144
";
let sdp = ffi::create_session_description(ffi::SdpType::Offer, sdp_string.to_string());
let err = unsafe { ffi::SdpParseError::from(sdp.err().unwrap().what()) };
info!("parse err: {:?}", err)
}
}
@@ -253,11 +253,23 @@ pub mod ffi {
}
// https://webrtc.github.io/webrtc-org/native-code/native-apis/
unsafe impl Send for ffi::PeerConnection {}
unsafe impl Sync for ffi::PeerConnection {}
unsafe impl Send for ffi::PeerConnection {}
unsafe impl Send for ffi::NativePeerConnectionObserver {}
unsafe impl Sync for ffi::NativePeerConnectionObserver {}
unsafe impl Sync for ffi::NativeAddIceCandidateObserver {}
unsafe impl Send for ffi::NativeAddIceCandidateObserver {}
unsafe impl Sync for ffi::NativeSetRemoteSdpObserverHandle {}
unsafe impl Send for ffi::NativeSetRemoteSdpObserverHandle {}
unsafe impl Sync for ffi::NativeSetLocalSdpObserverHandle {}
unsafe impl Send for ffi::NativeSetLocalSdpObserverHandle {}
unsafe impl Sync for ffi::NativeCreateSdpObserverHandle {}
unsafe impl Send for ffi::NativeCreateSdpObserverHandle {}
impl Default for ffi::RTCOfferAnswerOptions {
/*
@@ -280,17 +292,21 @@ impl Default for ffi::RTCOfferAnswerOptions {
}
}
pub trait AddIceCandidateObserver: Send {
fn on_complete(&self, error: RTCError);
}
pub struct AddIceCandidateObserverWrapper {
observer: Box<dyn Fn(RTCError) + Send>,
observer: Box<dyn AddIceCandidateObserver>,
}
impl AddIceCandidateObserverWrapper {
pub fn new(observer: Box<dyn Fn(RTCError) + Send>) -> Self {
pub fn new(observer: Box<dyn AddIceCandidateObserver>) -> Self {
Self { observer }
}
fn on_complete(&self, error: RTCError) {
(self.observer)(error);
self.observer.on_complete(error);
}
}
+20 -1
View File
@@ -1,7 +1,8 @@
use cxx::UniquePtr;
use libwebrtc_sys::jsep as sys_jsep;
pub use sys_jsep::ffi::{SdpType, SdpParseError};
// TODO Maybe we can replace that by a serialized IceCandidateInit
#[derive(Debug)]
pub struct IceCandidate {
@@ -9,6 +10,15 @@ pub struct IceCandidate {
}
impl IceCandidate {
pub fn from(sdp_mid: &str, sdp_mline_index: i32, sdp: &str) -> Result<IceCandidate, SdpParseError> {
let res = sys_jsep::ffi::create_ice_candidate(sdp_mid.to_string(), sdp_mline_index, sdp.to_string());
match res {
Ok(cxx_handle) => Ok(IceCandidate::new(cxx_handle)),
Err(e) => Err(unsafe { SdpParseError::from(e.what()) }),
}
}
pub(crate) fn new(cxx_handle: UniquePtr<sys_jsep::ffi::IceCandidate>) -> Self {
Self { cxx_handle }
}
@@ -30,6 +40,15 @@ pub struct SessionDescription {
}
impl SessionDescription {
pub fn from(sdp_type: SdpType, description: &str) -> Result<SessionDescription, SdpParseError> {
let res = sys_jsep::ffi::create_session_description(sdp_type, description.to_string());
match res {
Ok(cxx_handle) => Ok(SessionDescription::new(cxx_handle)),
Err(e) => Err(unsafe { SdpParseError::from(e.what()) }),
}
}
pub(crate) fn new(cxx_handle: UniquePtr<sys_jsep::ffi::SessionDescription>) -> Self {
Self { cxx_handle }
}
+36 -45
View File
@@ -1,9 +1,7 @@
use std::fmt::Debug;
use std::sync::{Arc, Mutex};
use cxx::UniquePtr;
use log::trace;
use thiserror::Error;
use tokio::sync::mpsc;
use libwebrtc_sys::data_channel as sys_dc;
@@ -22,14 +20,6 @@ use crate::rtc_error::RTCError;
use crate::rtp_receiver::RtpReceiver;
use crate::rtp_transceiver::RtpTransceiver;
#[derive(Error, Debug)]
pub enum SdpError {
#[error("recv failure: {0}")]
RecvError(String),
#[error("internal libwebrtc error")]
RTCError(#[from] RTCError),
}
pub struct PeerConnection {
cxx_handle: UniquePtr<sys_pc::ffi::PeerConnection>,
observer: Box<InternalObserver>,
@@ -51,7 +41,7 @@ impl PeerConnection {
}
}
pub async fn create_offer(&mut self, options: RTCOfferAnswerOptions) -> Result<SessionDescription, SdpError> {
pub async fn create_offer(&mut self, options: RTCOfferAnswerOptions) -> Result<SessionDescription, RTCError> {
let (tx, mut rx) = mpsc::channel(1);
let wrapper =
@@ -65,13 +55,10 @@ impl PeerConnection {
.create_offer(native_wrapper.pin_mut(), options);
}
match rx.recv().await {
Some(value) => value.map_err(Into::into),
None => Err(SdpError::RecvError("channel closed".to_string())),
}
rx.recv().await.unwrap()
}
pub async fn create_answer(&mut self, options: RTCOfferAnswerOptions) -> Result<SessionDescription, SdpError> {
pub async fn create_answer(&mut self, options: RTCOfferAnswerOptions) -> Result<SessionDescription, RTCError> {
let (tx, mut rx) = mpsc::channel(1);
let wrapper =
@@ -85,16 +72,13 @@ impl PeerConnection {
.create_answer(native_wrapper.pin_mut(), options);
}
match rx.recv().await {
Some(value) => value.map_err(Into::into),
None => Err(SdpError::RecvError("channel closed".to_string())),
}
rx.recv().await.unwrap()
}
pub async fn set_local_description(
&mut self,
desc: SessionDescription,
) -> Result<(), SdpError> {
) -> Result<(), RTCError> {
let (tx, mut rx) = mpsc::channel(1);
let wrapper =
sys_jsep::SetLocalSdpObserverWrapper::new(Box::new(InternalSetLocalSdpObserver { tx }));
@@ -107,16 +91,13 @@ impl PeerConnection {
.set_local_description(desc.release(), native_wrapper.pin_mut());
}
match rx.recv().await {
Some(value) => value.map_err(Into::into),
None => Err(SdpError::RecvError("channel closed".to_string())),
}
rx.recv().await.unwrap()
}
pub async fn set_remote_description(
&mut self,
desc: SessionDescription,
) -> Result<(), SdpError> {
) -> Result<(), RTCError> {
let (tx, mut rx) = mpsc::channel(1);
let wrapper =
sys_jsep::SetRemoteSdpObserverWrapper::new(Box::new(InternalSetRemoteSdpObserver {
@@ -131,10 +112,7 @@ impl PeerConnection {
.set_remote_description(desc.release(), native_wrapper.pin_mut());
}
match rx.recv().await {
Some(value) => value.map_err(Into::into),
None => Err(SdpError::RecvError("channel closed".to_string())),
}
rx.recv().await.unwrap()
}
pub fn create_data_channel(
@@ -155,10 +133,10 @@ impl PeerConnection {
}
// TODO(theomonnom) Use IceCandidateInit instead of IceCandidate
pub async fn add_ice_candidate(&mut self, candidate: IceCandidate) -> Result<(), SdpError> {
pub async fn add_ice_candidate(&mut self, candidate: IceCandidate) -> Result<(), RTCError> {
let (tx, mut rx) = mpsc::channel(1);
let observer = sys_pc::AddIceCandidateObserverWrapper::new(Box::new(move |error| {
tx.blocking_send(error).unwrap();
let observer = sys_pc::AddIceCandidateObserverWrapper::new(Box::new(InternalAddIceCandidateObserver {
tx,
}));
let mut native_observer =
@@ -167,10 +145,7 @@ impl PeerConnection {
.pin_mut()
.add_ice_candidate(candidate.release(), native_observer.pin_mut());
match rx.recv().await {
Some(value) => Ok(()),
None => Err(SdpError::RecvError("channel closed".to_string())),
}
rx.recv().await.unwrap()
}
pub fn local_description(&self) -> Option<SessionDescription> {
@@ -313,6 +288,20 @@ impl PeerConnection {
}
}
// SetLocalSdpObserver
struct InternalAddIceCandidateObserver {
tx: mpsc::Sender<Result<(), RTCError>>,
}
impl sys_pc::AddIceCandidateObserver for InternalAddIceCandidateObserver {
fn on_complete(&self, error: RTCError) {
let res = if error.ok() { Ok(()) } else { Err(error) };
let _ = self.tx.blocking_send(res);
}
}
// CreateSdpObserver
struct InternalCreateSdpObserver {
@@ -324,13 +313,11 @@ impl sys_jsep::CreateSdpObserver for InternalCreateSdpObserver {
&self,
session_description: UniquePtr<libwebrtc_sys::jsep::ffi::SessionDescription>,
) {
self.tx
.blocking_send(Ok(SessionDescription::new(session_description)))
.unwrap();
let _ = self.tx.blocking_send(Ok(SessionDescription::new(session_description)));
}
fn on_failure(&self, error: RTCError) {
self.tx.blocking_send(Err(error)).unwrap();
let _ = self.tx.blocking_send(Err(error));
}
}
@@ -343,7 +330,7 @@ struct InternalSetLocalSdpObserver {
impl sys_jsep::SetLocalSdpObserver for InternalSetLocalSdpObserver {
fn on_set_local_description_complete(&self, error: RTCError) {
let res = if error.ok() { Ok(()) } else { Err(error) };
self.tx.blocking_send(res).unwrap();
let _ = self.tx.blocking_send(res);
}
}
@@ -356,7 +343,7 @@ struct InternalSetRemoteSdpObserver {
impl sys_jsep::SetRemoteSdpObserver for InternalSetRemoteSdpObserver {
fn on_set_remote_description_complete(&self, error: RTCError) {
let res = if error.ok() { Ok(()) } else { Err(error) };
self.tx.blocking_send(res).unwrap();
let _ = self.tx.blocking_send(res);
}
}
@@ -634,6 +621,8 @@ impl sys_pc::PeerConnectionObserver for InternalObserver {
mod tests {
use log::trace;
use tokio::sync::mpsc;
use libwebrtc_sys::peer_connection::ffi::RTCOfferAnswerOptions;
use libwebrtc_sys::peer_connection_factory::ffi::{ContinualGatheringPolicy, IceTransportsType};
use crate::data_channel::{DataChannel, DataChannelInit};
use crate::jsep::IceCandidate;
@@ -657,6 +646,8 @@ mod tests {
username: "".into(),
password: "".into(),
}],
continual_gathering_policy: ContinualGatheringPolicy::GatherOnce,
ice_transport_type: IceTransportsType::All
};
let mut bob = factory.create_peer_connection(config.clone()).unwrap();
@@ -682,12 +673,12 @@ mod tests {
.create_data_channel("test_dc", DataChannelInit::default())
.unwrap();
let offer = bob.create_offer().await.unwrap();
let offer = bob.create_offer(RTCOfferAnswerOptions::default()).await.unwrap();
trace!("Bob offer: {:?}", offer);
bob.set_local_description(offer.clone()).await.unwrap();
alice.set_remote_description(offer).await.unwrap();
let answer = alice.create_answer().await.unwrap();
let answer = alice.create_answer(RTCOfferAnswerOptions::default()).await.unwrap();
trace!("Alice answer: {:?}", answer);
alice.set_local_description(answer.clone()).await.unwrap();
bob.set_remote_description(answer).await.unwrap();