Skip to content

Commit

Permalink
chore: improve docs (8xFF#221)
Browse files Browse the repository at this point in the history
  • Loading branch information
giangndm authored Feb 5, 2024
1 parent 686e948 commit 3113446
Show file tree
Hide file tree
Showing 76 changed files with 3,178 additions and 73 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ name: Rust
on:
push:
branches: ["master"]
paths-ignore:
- "docs/**"

pull_request:
branches: ["master"]
paths-ignore:
- "docs/**"

env:
CARGO_TERM_COLOR: always
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
tarpaulin-report.html
.atm0s
/maxminddb-data
.vscode
.vscode
book
9 changes: 9 additions & 0 deletions book.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[book]
title = "Atm0s Media Server development and user guide"
src = "docs"

[preprocessor.mermaid]
command = "mdbook-mermaid"

[output.html]
additional-js = ["mermaid.min.js", "mermaid-init.js"]
Empty file added docs/README.md
Empty file.
58 changes: 58 additions & 0 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Summary

- [Introduction](README.md)
- [Getting started](getting-started/README.md)
- [Installation](getting-started/installation/README.md)
- [Single zone](getting-started/installation/single-zone.md)
- [Multi zones](getting-started/installation/multi-zones.md)
- [Kubernetes](getting-started/installation/kubernetes.md)

- [Quick Start](getting-started/quick-start/README.md)
- [Whip/Whep](getting-started/quick-start/whip-whep.md)
- [RTMP](getting-started/quick-start/rtmp.md)
- [WebRTC SDK](getting-started/quick-start/webrtc-sdk.md)
- [Sample application](getting-started/quick-start/sample-application.md)

- [FAQ](getting-started/faq.md)
- [Troubleshooting](getting-started/troubleshooting.md)

- [User guide](user-guide/README.md)
- [Concepts](user-guide/concepts.md)
- [SDKs](user-guide/sdks.md)
- [Configuration](user-guide/configuration.md)
- [Features](user-guide/features/README.md)
- [Audio Mixer](user-guide/features/audio-mixer.md)
- [Cluster](user-guide/features/cluster.md)
- [Authentication and multi-tenancy](user-guide/features/authentication-and-multi-tenancy.md)
- [Simulcast/Svc](user-guide/features/simulcast-svc.md)
- [Recording](user-guide/features/recording.md)

- [Integration](user-guide/integration.md)
- [Usage examples](user-guide/usage-examples.md)
- [Upgrade](user-guide/upgrade.md)

- [Contributor guide](contributor-guide/README.md)
- [Getting Started](contributor-guide/getting-started.md)
- [Architecture](contributor-guide/architecture.md)
- [Features](contributor-guide/features/README.md)
- [Audio Mixer](contributor-guide/features/audio-mixer.md)
- [Simulcast/Svc](contributor-guide/features/simulcast-svc.md)
- [Recording](contributor-guide/features/recording.md)
- [Cluster](contributor-guide/features/cluster.md)
- [Authentication](contributor-guide/features/authentication.md)

- [Transports](contributor-guide/transports/README.md)
- [WebRTC](contributor-guide/transports/webrtc.md)
- [SIP](contributor-guide/transports/sip.md)
- [RTMP](contributor-guide/transports/rtmp.md)
- [Whip-Whep](contributor-guide/transports/whip-whep.md)

- [Middlewares](contributor-guide/middlewares/README.md)
- [Mix-minus](contributor-guide/middlewares/mix-minus.md)
- [Logging](contributor-guide/middlewares/logging.md)
- [Whep](contributor-guide/middlewares/whep.md)

- [Servers](contributor-guide/servers/README.md)
- [Gateway](contributor-guide/servers/gateway.md)
- [Media Server](contributor-guide/servers/media-server.md)
- [Connector](contributor-guide/servers/connector.md)
38 changes: 0 additions & 38 deletions docs/architecture.md

This file was deleted.

13 changes: 13 additions & 0 deletions docs/contributor-guide/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Contributor guide

This document is intended for developers who want to contribute to atm0s-media-server. It contains information about the project, the codebase, development environment setup, and more.

## Table of contents

- [Getting started](./getting-started.md)
- [Architecture](./architecture.md)
- [Features](./features/)
- [Transports](./transports/)
- [Middlewares](./middlewares/)
- [Servers](./servers/)
- [RFCs](./rfcs/)
119 changes: 119 additions & 0 deletions docs/contributor-guide/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Architecture

To understand atm0s-media-server, this document will introduce some design approaches about it. The document is split into 2 parts: abstract design and implementation design.

## Abstract design

Different from other media servers that are based on single node architecture and manual relay between nodes, atm0s-media-server is designed with a global cluster from the start. We don't rely on any single node or single source of data, you can imagine that we have a huge cluster across many zones which supports:

- Key Value store: supports HashMap, Set, Del, Sub
- Publish and Subscribe: supports publish and subscribe

For any media server, we only need some base features:

- Clients connect to the server and join a room
- Clients receive room events: peer joined, left, or stream started, updated, ended
- Clients receive stream data from other peers

Next, we will show how easy it is to implement with the KeyValue and Pubsub mechanisms.

KeyValue store:

- MapID: room identifier
- Key: peer identifier, stream identifier
- Value: peer info, stream info
- Subscriber: each peer in the room

PubSub:

- Channel: room/peer/stream as identifier
- Publisher: the peer who publishes the stream`
- Subscriber: the peers who subscribe to the stream

Each time a peer joins a room, we will set the key-value according to the peer info and the stream it published. Other peers will receive events from the key-value store and subscribe to the stream channel if needed. By doing that, audio and video data will be transferred to the peers.

When a peer leaves a room, we will remove the key-value and unsubscribe from the stream channel. Other peers will receive events from the key-value store and unsubscribe from the stream channel if needed.

![How it works](../imgs/architecture/how-it-works.excalidraw.png)

About PubSub between nodes, atm0s-sdn overlay network ensures both bandwidth saving and smooth data flow. The nodes automatically select the best path between the publisher and subscriber, optimizing bandwidth usage. For example, in the network diagram below, the route path is selected to provide the most optimized and smooth data flow. With this approach, node 1 sends data to node 2 only once, and then node 2 takes care of sending the data to both node 3 and node 4, resulting in an optimized and bandwidth-saving data flow.

![Why it's fast](../imgs/architecture/why-it-fast.excalidraw.png)

## Implementations

To implement the above mechanism, the source code is divided into three main components:

- Transport: This component handles communication with clients, including SIP, RTMP, and WebRTC (SDK, Whip, Whep).
- Endpoint: The endpoint component is responsible for processing the inner logic of the system, such as managing rooms and handling RPC. Additionally, middleware can be added to the endpoint to implement additional features like logging, audio mixing, and custom behaviors.
- Cluster: The cluster component facilitates communication with the key-value store and pub-sub system.

The relationship between these components is illustrated in the diagram below:

![Architecture](../imgs/architecture/implement-layers.excalidraw.png)

### Transport

Transport is create with single trait atm0s-media-server-transport::Transport. The trait is defined as bellow:

```Rust
#[async_trait::async_trait]
pub trait Transport<E, RmIn, RrIn, RlIn, RmOut, RrOut, RlOut> {
fn on_tick(&mut self, now_ms: u64) -> Result<(), TransportError>;
fn on_event(&mut self, now_ms: u64, event: TransportOutgoingEvent<RmOut, RrOut, RlOut>) -> Result<(), TransportError>;
fn on_custom_event(&mut self, now_ms: u64, event: E) -> Result<(), TransportError>;
async fn recv(&mut self, now_ms: u64) -> Result<TransportIncomingEvent<RmIn, RrIn, RlIn>, TransportError>;
async fn close(&mut self, now_ms: u64);
}
```

Each transport instance will be managed by the endpoint in a simple way:

- The endpoint will call `on_tick` periodically, for example, every 100ms.
- The endpoint will pass events to the transport using `on_event`.
- The endpoint will pass custom events to the transport using `on_custom_event`. Custom events are from external sources like RPC and may be removed in the future.
- The endpoint will call `recv` to retrieve events from the transport.

The event to the Transport is defined as below:

```Rust
#[derive(PartialEq, Eq, Debug)]
pub enum TransportOutgoingEvent<RE, RR, RL> {
RemoteTrackEvent(TrackId, RemoteTrackOutgoingEvent<RR>),
LocalTrackEvent(TrackId, LocalTrackOutgoingEvent<RL>),
ConfigEgressBitrate { current: u32, desired: u32 },
LimitIngressBitrate(u32),
Rpc(RE),
}
```

The event from Transport is defined as bellow:

```Rust
#[derive(PartialEq, Eq, Debug)]
pub enum TransportIncomingEvent<RE, RR, RL> {
State(TransportStateEvent),
Continue,
RemoteTrackAdded(TrackName, TrackId, TrackMeta),
RemoteTrackEvent(TrackId, RemoteTrackIncomingEvent<RR>),
RemoteTrackRemoved(TrackName, TrackId),
LocalTrackAdded(TrackName, TrackId, TrackMeta),
LocalTrackEvent(TrackId, LocalTrackIncomingEvent<RL>),
LocalTrackRemoved(TrackName, TrackId),
Rpc(RE),
Stats(TransportStats),
EgressBitrateEstimate(u64),
}
```

### Endpoint

The Endpoint is the core logic of atm0s-media-server. It manages how to process events from the transport and how to communicate with the cluster. The Endpoint is designed with a SAN IO style, where all internal logic is independent of I/O and processed without async/await. It is implemented inside the `packages/endpoint` crate and can be defined as follows:

![Endpoint](../imgs/architecture/endpoint.excalidraw.png)

### Task scheduler

To support a large number of peers, we will have multiple tasks. However, for simplicity, we will only have one task for each endpoint. These tasks are created using `async_std::task::spawn`. The relationship between each task is illustrated in the diagram below:

![Tasks](../imgs/architecture/tasks.excalidraw.png)
11 changes: 11 additions & 0 deletions docs/contributor-guide/features/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Features

In this document, we will explore the implementation of key features of atm0s-media-server. Currently, we have the following features:

| Feature | Status |
|---------------------------------|--------|
| [Audio-mixer](./audio-mixer.md) | Alpha |
| [Auth/Multi tenancy](./authentication-and-multi-tenancy.md) | Alpha |
| [Simulcast/SVC](./simulcast-svc.md) | Alpha |
| [Recording](./recording.md) | TODO |
| [Cluster](./cluster.md) | Alpha |
57 changes: 57 additions & 0 deletions docs/contributor-guide/features/audio-mixer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Audio Mixer

![Audio Mixer](../../imgs/features/audio-mixer.excalidraw.png)

For additional information on our audio mixer implementation, please consult the [Audio Mixer](/user-guide/features/audio-mixer.md) section in the User Guide documentation.

We have divided the fundamental virtual mix-minus logic into a distinct module known as the 'audio-mixer' module. This module operates independently and is adaptable for use in any other application.

To facilitate integration with an endpoint, we have developed middleware designed to seamlessly integrate with the endpoint's pipeline.

## Abstract Design

We have developed a new module called "audio-mixer," which receives all audio streams from other peers and selects the loudest audio track to send to the client. For flexibility, we offer two modes:

- Mixing all audio streams.
- Mixing only the most interesting audio streams.

The number of output tracks can be configured.

## Implementation Details

The core audio-mixer session will involve both input and output interactions:

**Input types:**

- `Source added` (source ID)
- `Source pkt` (source ID, audio level, audio data)
- `Source removed` (source ID)

**Output types:**

- `Output Pin` (output ID, source ID)
- `Output pkt` (output ID, audio level, audio data)
- `Output UnPin` (Output ID, source ID)

Each time a source is added, the core checks if there is a free output track. If available, it pins that output track to the respective source. When a source is removed, the core unpins that source from the output track.

Upon receiving a new audio packet, the core updates the audio level of the corresponding source. If the source wasn't pinned, the core checks if there is a pinned output track with a lower audio level than the source beyond a certain threshold. If found, the core unpins that output track and pins the source to that output track.

At periodic intervals, the core clears the audio level of all timed-out sources, which haven't received any audio packet within a specified time period.

## Potential Impact and Risks

This method relies on the assumption that the audio level of a source does not change significantly in a short period. If the audio level of a source undergoes substantial changes quickly, the core will unpin that source from the output track and pin it to another source, potentially resulting in an audio glitch.

Another challenge with this method is its dependency on receiving accurate audio level information from the source. If the source fails to send the audio level or provides incorrect information, the core may not function as intended.

## Future improvements

To optimize bandwidth usage, we employ a strategy of dividing audio stream data into multiple levels: stream metadata (which includes audio level) and actual audio data. When selecting a source for mixing, we subscribe only to the audio data level, minimizing the data transferred and reducing overall bandwidth consumption.

Additionally, we can have a normalization step to address the issue of rapid changes in audio levels. This step helps ensure smoother transitions and prevents abrupt fluctuations in audio, enhancing the overall stability of the system.

## Open Questions

- Optimizing for Many Sources in a Room: Strategies for efficiently handling scenarios where the room contains numerous sources.
- Handling Rapid Audio Level Changes: Techniques to ensure smooth transitions and prevent disruptions when the audio level of a source changes rapidly.
16 changes: 16 additions & 0 deletions docs/contributor-guide/features/authentication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Authentication
We can extend with custom authentication by implementing the `SessionTokenSigner` trait.

```rust
pub trait SessionTokenSigner {
fn sign_media_session(&self, token: &MediaSessionToken) -> String;
fn sign_conn_id(&self, conn_id: &MediaConnId) -> String;
}

pub trait SessionTokenVerifier {
fn verify_media_session(&self, token: &str) -> Option<MediaSessionToken>;
fn verify_conn_id(&self, token: &str) -> Option<MediaConnId>;
}
```

We have a simple static secret signer and verifier in the [`cluster` crate](https://github.com/8xFF/atm0s-media-server/blob/master/packages/cluster/src/implement/secure/jwt_static.rs).
21 changes: 21 additions & 0 deletions docs/contributor-guide/features/cluster.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Cluster

Cluster feature is implemented following [RFC-0003-media-global-cluster](https://github.com/8xFF/rfcs/pull/3).
More info can be found in user guide [here](../../user-guide/features/cluster.md).

## Implementation details

The cluster module is implemented in the `cluster` package. It is responsible for managing the cluster of media servers.

Each time new peer joined to media-server, cluster will create a new `ClusterEndpoint` to attach to the peer.

The `ClusterEndpoint` is responsible for managing pubsub channels, and also room information for the peer.
We use event based communication to interact with `ClusterEndpoint`:

```rust
#[async_trait::async_trait]
pub trait ClusterEndpoint: Send + Sync {
fn on_event(&mut self, event: ClusterEndpointOutgoingEvent) -> Result<(), ClusterEndpointError>;
async fn recv(&mut self) -> Result<ClusterEndpointIncomingEvent, ClusterEndpointError>;
}
```
3 changes: 3 additions & 0 deletions docs/contributor-guide/features/recording.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Recording

TODO: write about recording queue and compose utils
Loading

0 comments on commit 3113446

Please sign in to comment.