= ({ children }) => {
8 | const queryClient = new QueryClient()
9 | return {children}
10 | }
11 |
--------------------------------------------------------------------------------
/packages/media_console_front/react-app/src/routes/index.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | AuthSignIn,
3 | NetworkVisualization,
4 | Summary,
5 | ZonesDetail,
6 | ZonesEvents,
7 | ZonesList,
8 | ZonesPeers,
9 | ZonesRooms,
10 | ZonesSessions,
11 | } from '@/containers'
12 | import { Layout } from '@/layouts'
13 | import { PrivateProvider } from '@/providers/private-provider'
14 | import { createBrowserRouter } from 'react-router-dom'
15 |
16 | export const routes = createBrowserRouter([
17 | {
18 | element: ,
19 | children: [
20 | {
21 | element: ,
22 | children: [
23 | { path: '/', element: },
24 | {
25 | path: '/zones',
26 | element: ,
27 | },
28 | {
29 | path: '/zones/:id',
30 | element: ,
31 | },
32 | {
33 | path: '/zones/rooms',
34 | element: ,
35 | },
36 | {
37 | path: '/zones/peers',
38 | element: ,
39 | },
40 | {
41 | path: '/zones/events',
42 | element: ,
43 | },
44 | {
45 | path: '/zones/sessions',
46 | element: ,
47 | },
48 | {
49 | path: '/network/visualization',
50 | element: ,
51 | },
52 | ],
53 | },
54 | ],
55 | },
56 | {
57 | path: '/auth/sign-in',
58 | element: ,
59 | },
60 | ])
61 |
--------------------------------------------------------------------------------
/packages/media_console_front/react-app/src/types/index.ts:
--------------------------------------------------------------------------------
1 | import { DefinedInitialDataOptions } from '@tanstack/react-query'
2 |
3 | export type TInputQuery = {
4 | payload?: P
5 | options?: Omit, 'initialData' | 'queryKey'>
6 | }
7 |
--------------------------------------------------------------------------------
/packages/media_console_front/react-app/src/utils/common.ts:
--------------------------------------------------------------------------------
1 | export const generateRandomString = (length: number) => {
2 | const date = Date.now().toString()
3 |
4 | let charset = ''
5 | charset += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
6 | charset += 'abcdefghijklmnopqrstuvwxyz'
7 | charset += date
8 |
9 | let password = ''
10 | for (let i = 0; i < length; i++) {
11 | password += charset.charAt(Math.floor(Math.random() * charset.length))
12 | }
13 | return password
14 | }
15 |
--------------------------------------------------------------------------------
/packages/media_console_front/react-app/src/utils/constants.ts:
--------------------------------------------------------------------------------
1 | export const INITIAL_PAGE = 1
2 | export const INITIAL_LIMIT = 20
3 | export const LIMITS = [10, 20, 30, 40, 50]
4 |
--------------------------------------------------------------------------------
/packages/media_console_front/react-app/src/utils/cookies.ts:
--------------------------------------------------------------------------------
1 | import cookie from 'js-cookie'
2 |
3 | export const setCookie = (name: string, value: never, days = 30) => {
4 | cookie.set(name, value, { path: '/', expires: days })
5 | }
6 |
7 | export const getCookie = (name: string) => {
8 | return cookie.get(name)
9 | }
10 |
11 | export const removeCookie = (name: string) => {
12 | cookie.remove(name)
13 | }
14 |
--------------------------------------------------------------------------------
/packages/media_console_front/react-app/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './common'
2 | export * from './constants'
3 | export * from './cookies'
4 | export * from './storage'
5 |
--------------------------------------------------------------------------------
/packages/media_console_front/react-app/src/utils/storage.ts:
--------------------------------------------------------------------------------
1 | export const setLocalStorage = (key: string, value: object | string) => {
2 | if (typeof window === 'undefined') return
3 | localStorage.setItem(key, JSON.stringify(value))
4 | }
5 |
6 | export const getLocalStorage = (key: string) => {
7 | if (typeof window === 'undefined') return
8 | const value = localStorage.getItem(key)
9 | return value ? JSON.parse(value) : null
10 | }
11 |
12 | export const removeLocalStorage = (key: string) => {
13 | if (typeof window === 'undefined') return
14 | localStorage.removeItem(key)
15 | }
16 |
--------------------------------------------------------------------------------
/packages/media_console_front/react-app/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/packages/media_console_front/react-app/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "Bundler",
11 | "allowImportingTsExtensions": true,
12 | "isolatedModules": true,
13 | "moduleDetection": "force",
14 | "noEmit": true,
15 | "jsx": "react-jsx",
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noFallthroughCasesInSwitch": true,
22 | "noUncheckedSideEffectImports": true,
23 |
24 | "baseUrl": ".",
25 | "paths": {
26 | "@/*": ["./src/*"]
27 | }
28 | },
29 | "include": ["src"]
30 | }
31 |
--------------------------------------------------------------------------------
/packages/media_console_front/react-app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }],
4 | "compilerOptions": {
5 | "baseUrl": ".",
6 | "paths": {
7 | "@/*": ["./src/*"]
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/media_console_front/react-app/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2022",
4 | "lib": ["ES2023"],
5 | "module": "ESNext",
6 | "skipLibCheck": true,
7 |
8 | /* Bundler mode */
9 | "moduleResolution": "Bundler",
10 | "allowImportingTsExtensions": true,
11 | "isolatedModules": true,
12 | "moduleDetection": "force",
13 | "noEmit": true,
14 |
15 | /* Linting */
16 | "strict": true,
17 | "noUnusedLocals": true,
18 | "noUnusedParameters": true,
19 | "noFallthroughCasesInSwitch": true,
20 | "noUncheckedSideEffectImports": true
21 | },
22 | "include": ["vite.config.ts"]
23 | }
24 |
--------------------------------------------------------------------------------
/packages/media_console_front/react-app/tsconfig.node.tsbuildinfo:
--------------------------------------------------------------------------------
1 | {"root":["./vite.config.ts"],"version":"5.6.2"}
--------------------------------------------------------------------------------
/packages/media_console_front/react-app/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | import path from 'path'
3 | import { defineConfig } from 'vite'
4 |
5 | // https://vite.dev/config/
6 | export default defineConfig({
7 | plugins: [react()],
8 | resolve: {
9 | alias: {
10 | '@': path.resolve(__dirname, './src'),
11 | },
12 | },
13 | server: {
14 | hmr: {
15 | clientPort: 5173,
16 | },
17 | },
18 | })
19 |
--------------------------------------------------------------------------------
/packages/media_core/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "atm0s-media-server-core"
3 | version = "0.1.0-alpha.2"
4 | authors = ["Giang Minh "]
5 | edition = "2021"
6 | license = "MIT"
7 | description = "Media Core Component for Atm0s Media Server"
8 |
9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
10 |
11 | [dependencies]
12 | media-server-protocol = { workspace = true }
13 | media-server-utils = { workspace = true }
14 | audio-mixer = { workspace = true }
15 |
16 | log = { workspace = true }
17 | num_enum = { workspace = true }
18 | indexmap = { workspace = true }
19 | derivative = { workspace = true }
20 | derive_more = { workspace = true, features = ["full"] }
21 | mockall = { workspace = true }
22 | sans-io-runtime = { workspace = true, default-features = false }
23 | atm0s-sdn = { workspace = true }
24 |
25 | [dev-dependencies]
26 | test-log = { workspace = true }
27 |
--------------------------------------------------------------------------------
/packages/media_core/src/endpoint/internal/local_track/packet_selector/video_single.rs:
--------------------------------------------------------------------------------
1 | //! Single stream video selector.
2 | //! This selector allow all video because parent PacketSelector already wait for key-frame
3 |
4 | use super::{VideoSelector, VideoSelectorCtx};
5 |
6 | #[derive(Default)]
7 | pub struct VideoSingleSelector {}
8 |
9 | impl VideoSelector for VideoSingleSelector {
10 | fn on_init(&mut self, _ctx: &mut VideoSelectorCtx, _now_ms: u64) {}
11 |
12 | fn on_tick(&mut self, _ctx: &mut VideoSelectorCtx, _now_ms: u64) {}
13 |
14 | fn set_target_bitrate(&mut self, _ctx: &mut VideoSelectorCtx, _now_ms: u64, _bitrate: u64) {}
15 |
16 | fn set_limit_layer(&mut self, _ctx: &mut VideoSelectorCtx, _now_ms: u64, _max_spatial: u8, _max_temporal: u8) {}
17 |
18 | fn select(&mut self, _ctx: &mut VideoSelectorCtx, _now_ms: u64, _channel: u64, _pkt: &mut media_server_protocol::media::MediaPacket) -> Option<()> {
19 | Some(())
20 | }
21 |
22 | fn pop_action(&mut self) -> Option {
23 | None
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/media_core/src/endpoint/internal/local_track/voice_activity.rs:
--------------------------------------------------------------------------------
1 | const AUDIO_LEVEL_THRESHOLD: i8 = -40;
2 | const VOICE_ACTIVITY_INTERVAL: u64 = 500;
3 |
4 | #[derive(Default)]
5 | pub struct VoiceActivityDetector {
6 | last_activity: u64,
7 | }
8 |
9 | impl VoiceActivityDetector {
10 | pub fn on_audio(&mut self, now: u64, audio_level: Option) -> Option {
11 | let audio_level = audio_level?;
12 | if audio_level >= AUDIO_LEVEL_THRESHOLD && self.last_activity + VOICE_ACTIVITY_INTERVAL <= now {
13 | self.last_activity = now;
14 | Some(audio_level)
15 | } else {
16 | None
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/media_core/src/errors.rs:
--------------------------------------------------------------------------------
1 | #[derive(Debug, num_enum::TryFromPrimitive, num_enum::IntoPrimitive, derive_more::Display)]
2 | #[repr(u32)]
3 | pub enum EndpointErrors {
4 | EndpointNotInRoom = 0x0001,
5 | LocalTrackNotPinSource = 0x1001,
6 | LocalTrackInvalidPriority = 0x1002,
7 | RemoteTrackInvalidPriority = 0x2001,
8 | RemoteTrackStopped = 0x2002,
9 | AudioMixerWrongMode = 0x3001,
10 | Destroying = 0x4001,
11 | }
12 |
--------------------------------------------------------------------------------
/packages/media_core/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod cluster;
2 | pub mod endpoint;
3 | pub mod errors;
4 | pub mod transport;
5 |
--------------------------------------------------------------------------------
/packages/media_gateway/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "atm0s-media-server-gateway"
3 | version = "0.1.0-alpha.1"
4 | authors = ["Giang Minh "]
5 | edition = "2021"
6 | license = "MIT"
7 | description = "Media Gateway Component for Atm0s Media Server"
8 |
9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
10 |
11 | [dependencies]
12 | log = { workspace = true }
13 | serde = { workspace = true, features = ["derive"] }
14 | media-server-protocol = { workspace = true }
15 | atm0s-sdn = { workspace = true }
16 | prost = { workspace = true }
17 | spin = { workspace = true }
18 | derive_more = { workspace = true }
19 | tokio = { workspace = true, features = ["time"] }
20 |
--------------------------------------------------------------------------------
/packages/media_gateway/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod agent_service;
2 | mod store;
3 | pub mod store_service;
4 |
5 | #[derive(Debug, Clone, Hash, PartialEq, Eq)]
6 | pub enum ServiceKind {
7 | Webrtc,
8 | RtpEngine,
9 | }
10 |
11 | #[derive(Debug, Clone, Default)]
12 | pub struct NodeMetrics {
13 | pub cpu: u8,
14 | pub memory: u8,
15 | pub disk: u8,
16 | }
17 |
18 | pub const DATA_PORT: u16 = 10001;
19 |
20 | pub const STORE_SERVICE_ID: u8 = 101;
21 | pub const STORE_SERVICE_NAME: &str = "gateway_store";
22 |
23 | pub const AGENT_SERVICE_ID: u8 = 102;
24 | pub const AGENT_SERVICE_NAME: &str = "gateway_agent";
25 |
--------------------------------------------------------------------------------
/packages/media_record/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 |
8 | ## [Unreleased]
9 |
10 | ## [0.1.0-alpha.4](https://github.com/8xFF/atm0s-media-server/compare/atm0s-media-server-record-v0.1.0-alpha.3...atm0s-media-server-record-v0.1.0-alpha.4) - 2025-03-02
11 |
12 | ### Other
13 |
14 | - update Cargo.lock dependencies
15 |
16 | ## [0.1.0-alpha.3](https://github.com/8xFF/atm0s-media-server/compare/atm0s-media-server-record-v0.1.0-alpha.2...atm0s-media-server-record-v0.1.0-alpha.3) - 2025-02-27
17 |
18 | ### Other
19 |
20 | - update Cargo.lock dependencies
21 |
22 | ## [0.1.0-alpha.2](https://github.com/8xFF/atm0s-media-server/compare/atm0s-media-server-record-v0.1.0-alpha.1...atm0s-media-server-record-v0.1.0-alpha.2) - 2025-02-26
23 |
24 | ### Other
25 |
26 | - updated the following local packages: atm0s-media-server-codecs
27 |
--------------------------------------------------------------------------------
/packages/media_record/src/convert.rs:
--------------------------------------------------------------------------------
1 | mod codec;
2 | mod composer;
3 | mod transmuxer;
4 |
5 | pub use codec::*;
6 | pub use composer::*;
7 | pub use transmuxer::*;
8 |
9 | #[derive(Debug, Clone)]
10 | pub enum RecordConvertOutputLocation {
11 | S3(String),
12 | Local(String),
13 | }
14 |
15 | pub struct RecordConvertConfig {
16 | pub in_s3: String,
17 | pub transmux: Option,
18 | pub compose: Option,
19 | }
20 |
21 | #[derive(Debug, Clone)]
22 | pub struct RecordConvertOutput {
23 | pub transmux: Option,
24 | pub compose: Option,
25 | }
26 |
27 | pub struct RecordConvert {
28 | cfg: RecordConvertConfig,
29 | }
30 |
31 | impl RecordConvert {
32 | pub fn new(cfg: RecordConvertConfig) -> Self {
33 | Self { cfg }
34 | }
35 |
36 | pub async fn convert(self) -> Result {
37 | let mut transmux = None;
38 | if let Some(out) = self.cfg.transmux {
39 | let transmuxer = RecordTransmuxer::new(self.cfg.in_s3.clone(), out);
40 | transmux = Some(transmuxer.convert().await?);
41 | }
42 | let mut compose = None;
43 | if let Some(cfg) = self.cfg.compose.as_ref() {
44 | if cfg.audio || cfg.video {
45 | let composer = RecordComposer::new(self.cfg.in_s3.clone(), cfg.clone());
46 | compose = Some(composer.compose().await?);
47 | }
48 | }
49 | Ok(RecordConvertOutput { transmux, compose })
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/packages/media_record/src/convert/codec/mod.rs:
--------------------------------------------------------------------------------
1 | use media_server_protocol::media::MediaPacket;
2 |
3 | mod vpx_demuxer;
4 | mod vpx_writer;
5 |
6 | pub use vpx_demuxer::*;
7 | pub use vpx_writer::*;
8 |
9 | pub trait CodecWriter {
10 | fn push_media(&mut self, pkt_ms: u64, pkt: MediaPacket);
11 | }
12 |
--------------------------------------------------------------------------------
/packages/media_record/src/convert/composer/video_composer.rs:
--------------------------------------------------------------------------------
1 | use media_server_protocol::{
2 | endpoint::{TrackMeta, TrackName},
3 | media::MediaPacket,
4 | transport::RemoteTrackId,
5 | };
6 |
7 | #[derive(Default)]
8 | pub struct VideoComposer {}
9 |
10 | impl VideoComposer {
11 | pub fn add_track(&mut self, session_id: u64, remote_track_id: RemoteTrackId, track_name: TrackName, track_meta: TrackMeta) {
12 | log::info!("add track {:?} {:?} {:?} {:?}", session_id, remote_track_id, track_name, track_meta);
13 | }
14 |
15 | pub fn on_media(&mut self, session_id: u64, remote_track_id: RemoteTrackId, media_packet: MediaPacket) {
16 | log::info!("on media {:?} {:?} {:?}", session_id, remote_track_id, media_packet.seq);
17 | }
18 |
19 | pub fn remove_track(&mut self, session_id: u64, remote_track_id: RemoteTrackId) {
20 | log::info!("remove track {:?} {:?}", session_id, remote_track_id);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/packages/media_record/src/raw_record.rs:
--------------------------------------------------------------------------------
1 | mod chunk_reader;
2 | mod chunk_writer;
3 | mod peer_reader;
4 | mod room_reader;
5 | mod session_reader;
6 |
7 | pub use chunk_reader::RecordChunkReader;
8 | pub use chunk_writer::RecordChunkWriter;
9 | pub use peer_reader::PeerReader;
10 | pub use room_reader::RoomReader;
11 | pub use session_reader::SessionReader;
12 |
--------------------------------------------------------------------------------
/packages/media_record/src/raw_record/chunk_writer.rs:
--------------------------------------------------------------------------------
1 | use std::io::Write;
2 |
3 | use media_server_protocol::record::{SessionRecordHeader, SessionRecordRow};
4 |
5 | use crate::storage::{memory::MemoryFile, RecordFile};
6 |
7 | pub struct RecordChunkWriter {
8 | buf: [u8; 1500],
9 | file: MemoryFile,
10 | }
11 |
12 | impl RecordChunkWriter {
13 | pub fn new(room: &str, peer: &str, session: u64, start_ts: u64, end_ts: u64) -> Self {
14 | let mut buf = [0; 1500];
15 | let header = SessionRecordHeader {
16 | room: room.to_owned(),
17 | peer: peer.to_owned(),
18 | session,
19 | start_ts,
20 | end_ts,
21 | };
22 |
23 | let mut file = MemoryFile::default();
24 | file.set_start_ts(start_ts);
25 | file.set_end_ts(end_ts);
26 | let len = header.write_to(&mut buf[4..]).expect("should read");
27 | buf[0..4].copy_from_slice(&(len as u32).to_be_bytes());
28 | file.write_all(&buf[0..len + 4]).expect("should write");
29 |
30 | Self { file, buf }
31 | }
32 |
33 | pub fn push(&mut self, row: SessionRecordRow) {
34 | let len = row.write_to(&mut self.buf[4..]).expect("should read");
35 | self.buf[0..4].copy_from_slice(&(len as u32).to_be_bytes());
36 | self.file.write_all(&self.buf[0..len + 4]).expect("should write");
37 | }
38 |
39 | pub fn take(self) -> MemoryFile {
40 | self.file
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/packages/media_record/src/raw_record/room_reader.rs:
--------------------------------------------------------------------------------
1 | use std::time::Duration;
2 |
3 | use rusty_s3::{actions::ListObjectsV2, Bucket, Credentials, S3Action};
4 |
5 | use super::peer_reader::PeerReader;
6 |
7 | pub struct RoomReader {
8 | s3: Bucket,
9 | credentials: Credentials,
10 | path: String,
11 | }
12 |
13 | impl RoomReader {
14 | pub fn new(s3: Bucket, credentials: Credentials, path: &str) -> Self {
15 | log::info!("create room reader {path}");
16 | Self {
17 | s3,
18 | credentials,
19 | path: path.to_owned(),
20 | }
21 | }
22 |
23 | pub async fn peers(&self) -> std::io::Result> {
24 | let mut files = self.s3.list_objects_v2(Some(&self.credentials));
25 | files.with_prefix(&self.path);
26 | files.with_delimiter("/");
27 |
28 | let res = reqwest::get(files.sign(Duration::from_secs(3600)))
29 | .await
30 | .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
31 | let text = res.text().await.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
32 | let parsed = ListObjectsV2::parse_response(&text).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
33 | Ok(parsed
34 | .common_prefixes
35 | .into_iter()
36 | .map(|f| {
37 | let parts = f.prefix.split('/').collect::>();
38 | let peer = parts[parts.len() - 2];
39 | PeerReader::new(self.s3.clone(), self.credentials.clone(), peer, &f.prefix)
40 | })
41 | .collect::>())
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/packages/media_runner/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "atm0s-media-server-runner"
3 | version = "0.1.0-alpha.3"
4 | authors = ["Giang Minh "]
5 | edition = "2021"
6 | license = "MIT"
7 | description = "Media Runner Component for Atm0s Media Server"
8 |
9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
10 |
11 | [dependencies]
12 | rand = { workspace = true }
13 | log = { workspace = true }
14 | num_enum = { workspace = true }
15 | convert-enum = { workspace = true }
16 | indexmap = { workspace = true }
17 | media-server-protocol = { workspace = true }
18 | media-server-secure = { workspace = true }
19 | media-server-gateway = { workspace = true }
20 | media-server-connector = { workspace = true }
21 | media-server-core = { workspace = true }
22 |
23 | sans-io-runtime = { workspace = true, default-features = false }
24 | atm0s-sdn = { workspace = true }
25 | atm0s-sdn-network = { workspace = true }
26 | transport-webrtc = { workspace = true, optional = true }
27 | transport-rtpengine = { workspace = true, optional = true }
28 |
29 | [features]
30 | default = ["webrtc", "rtpengine"]
31 | webrtc = ["transport-webrtc"]
32 | rtpengine = ["transport-rtpengine"]
33 |
--------------------------------------------------------------------------------
/packages/media_runner/src/lib.rs:
--------------------------------------------------------------------------------
1 | mod worker;
2 |
3 | pub use worker::{Input, MediaConfig, MediaServerWorker, Output, Owner, SdnConfig, UserData, SC, SE, TC, TW};
4 |
--------------------------------------------------------------------------------
/packages/media_secure/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "atm0s-media-server-secure"
3 | version = "0.1.0-alpha.1"
4 | authors = ["Giang Minh "]
5 | edition = "2021"
6 | license = "MIT"
7 | description = "Media Secure Component for Atm0s Media Server"
8 |
9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
10 |
11 | [dependencies]
12 | media-server-protocol = { workspace = true }
13 | jwt-simple = { workspace = true, optional = true, default-features = false, features = [
14 | "pure-rust",
15 | ] }
16 | serde = { workspace = true, features = ["derive"] }
17 |
18 | [features]
19 | default = ["jwt-secure"]
20 | jwt-secure = ["jwt-simple"]
21 |
--------------------------------------------------------------------------------
/packages/media_utils/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "atm0s-media-server-utils"
3 | version = "0.3.0-alpha.1"
4 | authors = ["Giang Minh "]
5 | edition = "2021"
6 | license = "MIT"
7 | description = "Media Utils Component for Atm0s Media Server"
8 |
9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
10 |
11 | [features]
12 | embed-files = ["poem", "rust-embed", "hex", "mime_guess"]
13 |
14 | [dependencies]
15 | sorted-vec = { workspace = true }
16 | indexmap = { workspace = true }
17 | log = { workspace = true }
18 | serde = { workspace = true, features = ["derive"] }
19 | uriparse = { workspace = true }
20 | serde-querystring = { workspace = true }
21 | pin-project-lite = { workspace = true }
22 | spin = { workspace = true }
23 | once_cell = { workspace = true }
24 | urlencoding = { workspace = true }
25 | derive_more = { workspace = true, features = ["full"] }
26 |
27 | poem = { workspace = true, features = [], optional = true }
28 | rust-embed = { workspace = true, features = ["compression"], optional = true }
29 | hex = { workspace = true, optional = true }
30 | mime_guess = { workspace = true, optional = true }
31 |
32 | [dev-dependencies]
33 | criterion = { workspace = true, features = ["html_reports"] }
34 |
35 | [[bench]]
36 | name = "map_bench"
37 | harness = false
38 |
--------------------------------------------------------------------------------
/packages/media_utils/benches/map_bench.rs:
--------------------------------------------------------------------------------
1 | use criterion::{criterion_group, criterion_main, Criterion};
2 | use std::collections::HashMap;
3 |
4 | fn criterion_benchmark(c: &mut Criterion) {
5 | let mut map = HashMap::new();
6 | for i in 0..64 {
7 | map.insert(i, i);
8 | }
9 |
10 | let mut map2 = indexmap::IndexMap::new();
11 | for i in 0..64 {
12 | map2.insert(i, i);
13 | }
14 |
15 | c.bench_function("std::map::iter", |b| b.iter(|| map.iter()));
16 | c.bench_function("indexmap::iter", |b| b.iter(|| map2.iter()));
17 |
18 | c.bench_function("std::map::found", |b| b.iter(|| map.get(&55)));
19 | c.bench_function("indexmap::found", |b| b.iter(|| map2.get(&55)));
20 |
21 | c.bench_function("std::map::notfound", |b| b.iter(|| map.get(&155)));
22 | c.bench_function("indexmap::notfound", |b| b.iter(|| map2.get(&155)));
23 | }
24 |
25 | criterion_group!(benches, criterion_benchmark);
26 | criterion_main!(benches);
27 |
--------------------------------------------------------------------------------
/packages/media_utils/src/f16.rs:
--------------------------------------------------------------------------------
1 | /// F16u is low percison float, which encoded inside u16 for better performance and space
2 | /// F16u is unsigned type, if you need signed type it is F16i
3 | #[derive(Debug, PartialEq, Eq)]
4 | pub struct F16u(u16);
5 |
6 | impl From for f32 {
7 | fn from(val: F16u) -> Self {
8 | val.0 as f32 / 100.0
9 | }
10 | }
11 |
12 | impl F16u {
13 | pub fn value(&self) -> f32 {
14 | self.0 as f32 / 100.0
15 | }
16 | }
17 |
18 | impl PartialOrd for F16u {
19 | fn partial_cmp(&self, other: &Self) -> Option {
20 | Some(self.0.cmp(&other.0))
21 | }
22 | }
23 |
24 | impl Ord for F16u {
25 | fn cmp(&self, other: &Self) -> std::cmp::Ordering {
26 | self.0.cmp(&other.0)
27 | }
28 | }
29 |
30 | #[derive(Debug, PartialEq, Eq)]
31 | pub struct F16i(i16);
32 |
33 | impl From for f32 {
34 | fn from(val: F16i) -> Self {
35 | val.0 as f32 / 100.0
36 | }
37 | }
38 |
39 | impl Ord for F16i {
40 | fn cmp(&self, other: &Self) -> std::cmp::Ordering {
41 | self.0.cmp(&other.0)
42 | }
43 | }
44 |
45 | impl PartialOrd for F16i {
46 | fn partial_cmp(&self, other: &Self) -> Option {
47 | Some(self.0.cmp(&other.0))
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/packages/media_utils/src/indexmap_2d.rs:
--------------------------------------------------------------------------------
1 | use std::hash::Hash;
2 |
3 | use indexmap::IndexMap;
4 |
5 | pub struct IndexMap2d {
6 | data: IndexMap,
7 | reverse: IndexMap,
8 | }
9 |
10 | impl Default for IndexMap2d {
11 | fn default() -> Self {
12 | Self {
13 | data: Default::default(),
14 | reverse: Default::default(),
15 | }
16 | }
17 | }
18 |
19 | impl IndexMap2d {
20 | pub fn insert(&mut self, key: T1, value: T2) {
21 | self.data.insert(key.clone(), value.clone());
22 | self.reverse.insert(value, key);
23 | }
24 |
25 | pub fn keys1(&self) -> Vec {
26 | self.data.keys().cloned().collect::>()
27 | }
28 |
29 | pub fn pairs(&self) -> Vec<(T1, T2)> {
30 | self.data.iter().map(|(k, v)| (k.clone(), v.clone())).collect::>()
31 | }
32 |
33 | pub fn keys2(&self) -> Vec {
34 | self.reverse.keys().cloned().collect::>()
35 | }
36 |
37 | pub fn get1(&self, key: &T1) -> Option<&T2> {
38 | self.data.get(key)
39 | }
40 |
41 | pub fn get2(&self, key: &T2) -> Option<&T1> {
42 | self.reverse.get(key)
43 | }
44 |
45 | pub fn remove1(&mut self, key: &T1) -> Option {
46 | let value = self.data.swap_remove(key)?;
47 | self.reverse.swap_remove(&value);
48 | Some(value)
49 | }
50 |
51 | pub fn remove2(&mut self, key: &T2) -> Option {
52 | let value = self.reverse.swap_remove(key)?;
53 | self.data.swap_remove(&value);
54 | Some(value)
55 | }
56 |
57 | pub fn len(&self) -> usize {
58 | self.data.len()
59 | }
60 |
61 | pub fn is_empty(&self) -> bool {
62 | self.data.is_empty()
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/packages/media_utils/src/lib.rs:
--------------------------------------------------------------------------------
1 | mod count;
2 | #[cfg(feature = "embed-files")]
3 | mod embed_files;
4 | mod f16;
5 | mod indexmap_2d;
6 | mod select;
7 | mod seq_extend;
8 | mod seq_rewrite;
9 | mod state;
10 | mod time;
11 | mod ts_rewrite;
12 | mod uri;
13 |
14 | pub use count::{get_all_counts, Count};
15 | #[cfg(feature = "embed-files")]
16 | pub use embed_files::{EmbeddedFileEndpoint, EmbeddedFilesEndpoint};
17 | pub use f16::{F16i, F16u};
18 | pub use indexmap_2d::IndexMap2d;
19 | pub use select::*;
20 | pub use seq_extend::RtpSeqExtend;
21 | pub use seq_rewrite::SeqRewrite;
22 | pub use state::*;
23 | pub use time::now_ms;
24 | pub use ts_rewrite::TsRewrite;
25 | pub use uri::CustomUri;
26 |
--------------------------------------------------------------------------------
/packages/media_utils/src/select.rs:
--------------------------------------------------------------------------------
1 | pub mod select2;
2 | pub mod select3;
3 |
--------------------------------------------------------------------------------
/packages/media_utils/src/select/select2.rs:
--------------------------------------------------------------------------------
1 | #[doc(no_inline)]
2 | pub use core::future::Future;
3 | use core::pin::Pin;
4 | use core::task::{Context, Poll};
5 | use pin_project_lite::pin_project;
6 |
7 | pub enum OrOutput {
8 | Left(T1),
9 | Right(T2),
10 | }
11 |
12 | pub fn or(future1: F1, future2: F2) -> Or
13 | where
14 | F1: Future