Open alexlapa opened 6 years ago
Внутренности можно разделить на 3 слоя:
Transports - транспорты внешнего API: http
, websockets
, mqtt
, nanomsg
, pfunix
, rabbitmq
.
Связывает пользовательские сессии с plugin handle.
Plugins - весь функционал завернут в ряд плагинов, например:
janus.plugin.echotest
- делает loopback, отправляет медиа на сервер и возвращает обратно пользователю.janus.plugin.videocall
- 1-to-1 call.janus.plugin.videoroom
- n-to-n call.Плагины дергают протоколы.
Protocols - реализации используемых протоколов уровня медиа/транспорта медиа, часть базируется на соответсвующих либах: dtls
(на OpenSSL
, libsrtp
), ice
( на libnice
, 4к строчная оберточка получилась :smile: ), rtcp
, rtp
, sctp
(на libusrsctp
), sdp
.
Внутренности устроены весьма симпатично, но, в нашем случае, учитывая что мы хотим применять абстракции gstreamer, не спускаясь(в идеале) на уровень реализаций протоколов, такой подход неприменим.
There are basically three types/levels of endpoints you can meet:
- The server root (/janus by default, but configurable), which you only POST to in order to create a Janus session;
- The session endpoint (e.g., /janus/12345678, using the identifier retrieved with a previous create), which you either send a GET to (long poll for events and messages from plugins) or a POST (to create plugin handles or manipulate the session);
- The plugin handle endpoint (e.g., /janus/12345678/98765432, appending the handle identifier to the session one) which you only send POST messages to (messages/negotiations for a plugin, handle manipulation), as all events related to this handle would be received in the session endpoint GET (the janus.js library would redirect the incoming messages to the right handle internally).
Фактически, взаимодейтвие выглдядит следующим образом:
/root
; RESP session_id&available_plugins
. /root/session_id?plugin_name
; RESP plugin_handle_id
/root/session_id/plugin_handle_id
.Очевидно, что плагины полностью независимы друг от друга.
В итоге, получается достаточно интересная архитектура. Единственное что меня смущает - сложности в избегании дублирования логики/концептов от плагина к плагину. Ожидаю, что этого можно было бы избежать путем создания дополнительной прослойки между протоколами и плагинами. Стоит подумать над возможностью реализации такой-же модульной архитектуры, но с учетом наших задач.
@alexlapa well... I see these two architectures are pretty same.
Janus plugins are literally pre-configured high-level Kurento pipelines, while Janus protocols are Kurento elements. And Janus transports are GStreamer/Kurento elements which has no sink and just produce the sources.
The difference is that Janus transport "invokes" Janus plugin by its handle, while Kurento pipeline has everything baked in. But such compounding seems to be the result of outer API design choices.
Also, I kinda dislike Janus outer API design, as it introduces too many round-trips.
@tyranron ,
Janus plugins are literally pre-configured high-level Kurento pipelines
Можно так сказать. Но, думаю, это все-таки будет немного некорректно. Плагины - фактически готовые приложения. Pipeline - все-таки штука медиа уровня, они динамические(поэтому не ясно как их можно запреконфигурить), не несут в себе никакой бизнес-логики.
while Janus protocols are Kurento elements
Слишком уж разные уровни абстракции. Но, и то и то является API, с которым будут взаимодействовать разработчики Janus плагинов / Kurento приложений. По такому признаку их можно обьединить.
while Kurento pipeline has everything baked in.
Не сказал бы. Пользователь API сам собирает Pipeline. В комнату добавился новый получатель видео - мы создаем ему endpoint, подключаем этот endpoint к endpoint'у публикующего.
Я бы выделил два основных различия:
В идеале хочется иметь и то и то - удобные абстракции внутри приложения, которые при желании можно открыть для внешних сервисов, room-management внутри медиа сервера, для тех, кому не нужен более низкоуровневый доступ.
К внутренней архитектуре еще вернемся. Сейчас будем формализировать внешнее контрольное API #4 , #5
Kurento design
Kurento построен на gstreamer и унаследовал весь дизайн от него. Кодовую базу Kurento можно разделить на три части:
Как работают плагины тут. В итоге, каждый плагин становится element, в терминологии gst.
Сейчас нам интереснее абстракции высшего уровня. Для начала немного о внутреннем дизайне gstreamer:
Element
- an element is the most important class of objects in GStreamer. An element has one specific function, which can be the reading of data from a file, decoding of this data or outputting this data to your sound card (or anything else).Pad
- pads are element's input and output, where you can connect other elements. For the most part, all data in GStreamer flows one way through a link between elements. Data flows out of one element through one or more source pads, and elements accept incoming data through one or more sink pads. Source and sink elements have only source and sink pads, respectively.Bin
- a bin is a container for a collection of elements. Since bins are subclasses of elements themselves, you can mostly control a bin as if it were an element, thereby abstracting away a lot of complexity for your application.Pipeline
- a pipeline is a top-level bin. It provides a bus for the application and manages the synchronization for its children. As you set it to PAUSED or PLAYING state, data flow will start and media processing will take place. Once started, pipelines will run in a separate thread until you stop them or the end of the data stream is reached.Все нагло скопировано отсюда
По похожей философии построены и внешние абстракции Kurento, единственное, что они достаточно удачно прячут все low-level details. У Kurento точно так-же есть базовый
MediaElement
с функциейconnect()
, но уровень абстракции его наследников значительно выше чем у элементов gstremer. KurentoMediaElement
можно воспринимать как gstreamer bin.Иерархия абстракций верхнего уровня:
В итоге, получается достаточно удобное API. Но, главная фишка - сокрытие всех необходимых транскодирований/трансмуксирований. Соединяя два элемента, пользователь не переживает о совместимости их pad'ов:
Считаю это решении достаточно удачным и предлагаю рассмотреть его или в качестве внешнего API или в качестве одного из внутренних слоев медиа-сервера.