Skip to content
This repository has been archived by the owner on Aug 25, 2021. It is now read-only.

Medea server 1 <=> 1 WebRTC P2P #10

Closed
16 tasks done
alexlapa opened this issue Jan 10, 2019 · 5 comments
Closed
16 tasks done

Medea server 1 <=> 1 WebRTC P2P #10

alexlapa opened this issue Jan 10, 2019 · 5 comments
Assignees
Labels
feature New feature or request k::design Related to overall design and/or architecture roadmap Roadmap for multiple steps
Milestone

Comments

@alexlapa
Copy link
Collaborator

alexlapa commented Jan 10, 2019

Архитектура

Control API пока нет. Будет одна захардкоженая комната с двумя захардкожеными пользователями.

Комнату в терминах Control API можно выразить следующим образом:

kind: Room
spec:
  pipeline:
    caller:
      kind: Member
      spec:
        pipeline:
          publish:
            kind: WebRtcPublishEndpoint
            spec:
              p2p: Always
          play:
            kind: WebRtcPlayEndpoint
            spec:
              src: "local://video-call-2/responder/publish"
    responder:
      kind: Member
      spec:
        pipeline:
          publish:
            kind: WebRtcPublishEndpoint
            spec:
              p2p: Always
          play:
            kind: WebRtcPlayEndpoint
            spec:
              src: "local://video-call-2/caller/publish"

Сценарий обмена сообщениями - первые пять пунктов из примера 1 <=> 1 P2P with unpublish and republish в 0002-webrc-client-api.

Ознакомление с 0001-control-api и 0002-webrc-client-api строго обязательное.

Примерное распределение по пакетам следующее:

  1. /api/client - все что связяно с Client API, а пока это: инициализация WsListener, обработка получаемых по сокету сообщений, протокольные ДТО, WsSessionsRepository.
  2. '/api/control' - все что связано с Control API. Пока это: Member, MemberRepository.
  3. /media - предметная область: Peer, Track. PeerRepository.
  4. /log - враппер предоставляющий средства для логирования внутри приложения чего угодно.
  5. /conf - слой реализующий конфигурацию приложения и предоставляющий средства для работы с ней.

Elements considerations

Важный момент - разделение между терминами Control API и терминами нашей внутренней модели данных. Так как основной задачей Control API было максимально упрощение задачи взаимодействия управляющего сервиса c Medea, его термины не ложаться на реальную модель данных 1 к 1.

Например

WebRtc<_>Endpoint'ы интуитивно приравниваются к RTCPeerConnection. Т.е. каждый пользователь будет иметь по два RTCPeerConnection. Но это будет верно только для некоторых сценариев с использование hub сервера.

И хотя такая реализация для N<=>N P2P возможна, намного эффективней будет держать по одному дюплексному RTCPeerConnection'у у каждого пользователя на каждого из его собеседников. Таким образом, в данном примере, WebRtcPublishEndpoint фактически является RTCRtpSender, а WebRtcPlayEndpoint = RTCRtpReceiver.

Так как Control API в 0.1.0 milestone не фигурирует, задача трансляции его модели данных в модель данных Medea(парсинг Control API спеки) пока не стоит.

Предлагается придерживаться примитивов обозначенных в 0002-webrc-client-api:

  1. Member
  2. Peer
  3. Track

В добавлении Room из 0001-control-api пока смысла нет - комната одна будет.

Что хочется получить:

  1. Удобные top-level абстракции.
  2. Корректная Peer state-machine. Тут можно частично вдохновится списоком состояний RTCPeerConnection: signaling_state, connection_state.
  3. Peer сам знает что отправить удаленному клиенту чтобы тот синхронизировал свое состояние. В идеале, синхронизация состояний должна происходить по вызову специального метода (допустим Peer.update_tracks()).

Последний пункт достаточно неоднозначный. Draft подразумевает что все так, но над этим еще надо будет подумать. Сокет, дергающий Peerа, дергающего сокет - может привести к проблемам.

Peer and Track state-machine draft

Постарался набросать как это примерно может выглядить. На прямое руководство к действию пока не тянет и многие моменты опущены.

struct Peer<S> {
    id: peer::Id,
    member_id: member::Id,
    signaling_state: S,
}

// signaling_states
struct New {}
struct WaitLocalSDP {}
struct WaitLocalHaveRemote {}
struct WaitRemoteSDP {}
struct Stable {}

impl Peer<New> {
    fn new(id: peer::Id, member_id: member::Id) -> Peer<New> {}

    fn add_sender<T>(&mut self, track: Track<T, New>) -> Track<T, Send> {}

    fn add_receiver<T>(&mut self, track: Track<T, Send>) -> Track<T, SendRecv> {}
    
    fn update_tracks(self) -> Peer<WaitLocalSDP> {} // sends PeerCreated

    fn set_local_sdp(self, offer: &str) -> Peer<WaitRemoteSDP> {}
    
    fn set_remote_sdp(self, offer: &str) -> Peer<WaitLocalHaveRemote> {}     // sends PeerCreated
}

impl Peer<WaitRemoteSDP> {
    fn set_remote_sdp(self, offer: &str) -> Peer<Stable> {}     // sends SdpAnswerMade
}

impl Peer<WaitLocalSDP> {
    fn set_local_sdp(self, offer: &str) -> Peer<WaitRemoteSDP> {}
}

impl Peer<WaitLocalHaveRemote> {
    fn set_local_sdp(self, offer: &str) -> Peer<Stable> {
    }
}

struct Track<T, S> {
    id: u64,
    media_type: T,
    track_state: S,
}

// media_types

struct Audio {}
struct Video {}

// track_states

struct Send {}
struct SendRecv {}
struct Active {}

И пример как это будет выглядить:

fn main() {

    // init peers and tracks
    let mut peer1: Peer<New> = Peer::new(peer::Id(1), member::Id(String::from("caller")));

    let peer1_audio: Track<Audio, Send> = peer1.add_sender(Track::<Audio, New>::new(1, Audio {})); //ads new audio track to list of peer1 sending tracks
    let peer1_video: Track<Video, Send> = peer1.add_sender(Track::<Video, New>::new(2, Video {})); //ads new video track to list of peer1 sending tracks

    let mut peer2: Peer<New> = Peer::new(peer::Id(2), member::Id(String::from("responder")));

    let peer2_audio: Track<Audio, Send> = peer2.add_sender(Track::<Audio, New>::new(3, Audio {})); //ads new audio track to list of peer1 sending tracks
    let peer2_video: Track<Video, Send> = peer2.add_sender(Track::<Video, New>::new(4, Video {})); //ads new video track to list of peer1 sending tracks

    // connect tracks
    let peer2_to_peer1_audio: Track<Audio, SendRecv> = peer1.add_receiver(peer2_audio); // ads peer2_audio track to list of peer1 receiving tracks
    let peer2_to_peer1_video: Track<Video, SendRecv> = peer1.add_receiver(peer2_video); // ads peer2_video track to list of peer1 receiving tracks

    let peer1_to_peer2_audio: Track<Audio, SendRecv> = peer2.add_receiver(peer1_audio); // ads peer1_audio track to list of peer2 receiving tracks
    let peer1_to_peer2_video: Track<Video, SendRecv> = peer2.add_receiver(peer1_video); // ads peer1_video track to list of peer2 receiving tracks

    // init connection
    let peer1: Peer<WaitLocalSDP> = peer1.update_tracks(); // => PeerCreated {1, no_offer};

    // MakeSdpOffer from caller
    let peer1: Peer<WaitRemoteSDP> = peer1.set_local_sdp("caller_offer");
    let peer2: Peer<WaitLocalHaveRemote> = peer2.set_remote_sdp("caller_offer"); // => PeerCreated {2, offer}

    // MakeSdpAnswer from responder
    let peer1: Peer<Stable> = peer1.set_remote_sdp("responder_answer"); // => SdpAnswerMade {1}
    let peer2: Peer<Stable> = peer2.set_local_sdp("responder_answer");
}

Roadmap

@alexlapa alexlapa added feature New feature or request k::design Related to overall design and/or architecture labels Jan 10, 2019
@alexlapa alexlapa added this to the 0.1.0 milestone Jan 10, 2019
@alexlapa alexlapa self-assigned this Jan 10, 2019
@alexlapa alexlapa mentioned this issue Jan 10, 2019
5 tasks
@alexlapa
Copy link
Collaborator Author

cc @tyranron

@tyranron
Copy link
Member

tyranron commented Jan 14, 2019

@alexlapa I think it's OK at the moment. Just do not forget to update roadmap when things change.

@alexlapa alexlapa added the roadmap Roadmap for multiple steps label Jan 14, 2019
@alexlapa alexlapa changed the title Medea server 1 <=> 1 P2P Medea server 1 <=> 1 WebRTC P2P Jan 14, 2019
@alexlapa alexlapa changed the title Medea server 1 <=> 1 WebRTC P2P Medea server 1 <=> 1 WebRTC P2P, Test User Application Jan 14, 2019
@alexlapa alexlapa changed the title Medea server 1 <=> 1 WebRTC P2P, Test User Application Medea server 1 <=> 1 WebRTC P2P Jan 14, 2019
Kirguir added a commit that referenced this issue Jan 18, 2019
Kirguir added a commit that referenced this issue Jan 31, 2019
- add default caller and responder into repository
Kirguir added a commit that referenced this issue Jan 31, 2019
- add default caller and responder into repository
@Kirguir
Copy link
Contributor

Kirguir commented Mar 5, 2019

@alexlapa объясните за треки, пожалуйста, а то не складывается описание из rfc-0002 и здешнего примера:

  1. когда мы добавляем трек `peer.add_sender(Track):
    а. трек сохраняется в пире, и создается и возвращается ответный пир
    б. в пире сохраняется только ид трека, а сам трек, реализованый в виде state machine, изменяет состояние и возвращается
  2. когда трек становится Active?
  3. что делать с треками SendRecv?

@alexlapa
Copy link
Collaborator Author

alexlapa commented Mar 5, 2019

@Kirguir ,

когда мы добавляем трек `peer.add_sender(Track):

Думаю, треки лучше хранить внутри Peer'ов. Например так:

Peer {
    recv: HashMap<TrackId, Arc<Track>>
    send: HashMap<TrackId, Arc<Track>>
} 

Один трек может быть у нескольких пиров. Например, если Peer 1 публикует в Peer 2, то у Peer 1 он будет в send, у Peer 2 в recv. Делать ли их как state machine - смотрите как будет лучше клеиться.

когда трек становится Active?

Пока предлагаю ограничиться таким условием: Все треки можно считать active, если оба пира заэмитили и получили ice кандидатов. Но не вижу необходимости обрабатывать это в текущем milestone.

что делать с треками SendRecv?

Meh, тут возникла небольшая путаница в терминологии - мой косяк. В случае с треками я хотел в треке отразить что трек имеет "два конца" - и сендера и ресивера. На самом деле, sendonly/recvonly/sendrecv - характеристика RTCPeerConnection, которая означает что он будет только отправлять/получать/отправлять и получать медиа. Это содержиться в теле sdp.

А сам вопрос "что с ними делать" требует уточнения.

@instrumentisto instrumentisto deleted a comment from Kirguir Mar 28, 2019
@Kirguir
Copy link
Contributor

Kirguir commented Mar 29, 2019

@alexlapa по результатам дисскусию:

  1. Изучить правильное использование cancel_future.
  2. Создавать Peer's при старте комнаты.
  3. Если в процессе сигналинга произошла ошибка, пиры переходят в состояние Failure, клиентам отправляются эвенты об удалении пиров.
  4. Эвенты должны отправляться, по-возможности (есть живая сессия), сразу. Если нельзя отправить эвент, то это ошибка и пиры тоже переходят в состояние Failure. Т.к. нельзя отправить эвент (используя ту же цепочку футур в которой обрабатывается команда) тому же клиенту, что отправил команду используя метод RpcConnection.send_event, эвент можно возвращать как результат футуры.

@Kirguir Kirguir mentioned this issue Apr 11, 2019
4 tasks
nWacky pushed a commit that referenced this issue Jun 13, 2019
- provide ICE servers to use for WebRTC connections on client side
- impl static Coturn user authorization scheme
- impl dynamic authorization scheme backed by Redis database of Coturn users
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
feature New feature or request k::design Related to overall design and/or architecture roadmap Roadmap for multiple steps
Projects
None yet
Development

No branches or pull requests

3 participants