### Build AES67 Stream Monitor from Source Source: https://context7.com/philhartung/aes67-monitor/llms.txt Clone the repository, install Node.js dependencies, and use npm scripts to build, serve, or start the application in development mode. Lint and format code with provided scripts. ```bash # Clone the repository git clone https://github.com/philhartung/aes67-monitor.git cd aes67-monitor # Install dependencies (audify prebuilds available for most platforms) npm install # Build binaries for current platform (macOS, Windows, or Linux) npm run build # For development with hot-reload frontend npm run serve # Start electron in development mode (requires npm run serve running) npm start # Lint and format code npm run lint npm run format ``` -------------------------------- ### Install Dependencies Source: https://github.com/philhartung/aes67-monitor/blob/main/README.md Clone the repository and install required Node.js dependencies. ```bash git clone https://github.com/philhartung/aes67-monitor.git cd aes67-monitor npm install ``` -------------------------------- ### Development Commands Source: https://github.com/philhartung/aes67-monitor/blob/main/README.md Commands for running the development environment, starting Electron, and maintaining code quality. ```bash npm run serve ``` ```bash npm start ``` ```bash npm run lint npm run format ``` -------------------------------- ### SDP Format Examples for AES67 Source: https://context7.com/philhartung/aes67-monitor/llms.txt Provides examples of Session Description Protocol (SDP) formats for AES67/RAVENNA streams, including standard, Dante-style, and ST 2022-7 redundant streams. ```sdp v=0 o=- 1234567890 1 IN IP4 192.168.1.100 s=Studio A - Main Mix i=48kHz/24bit stereo program audio c=IN IP4 239.69.1.1/32 t=0 0 a=recvonly m=audio 5004 RTP/AVP 97 a=rtpmap:97 L24/48000/2 a=ptime:1 a=ts-refclk:ptp=IEEE1588-2008:00-1D-C1-FF-FE-00-00-01:0 a=mediaclk:direct=0 # Dante-style stream with keyword identifier v=0 o=DanteDev 1234 1 IN IP4 10.0.0.50 s=Dante Device Ch1-8 c=IN IP4 239.255.1.1/32 t=0 0 a=keywds:Dante m=audio 4321 RTP/AVP 97 a=rtpmap:97 L24/48000/8 a=ptime:1 # ST 2022-7 redundant stream with dual media v=0 o=- 9876543210 1 IN IP4 192.168.10.1 s=Redundant Audio Feed c=IN IP4 239.100.1.1/32 t=0 0 a=group:DUP 1 2 m=audio 5004 RTP/AVP 97 a=mid:1 a=rtpmap:97 L24/48000/2 a=ptime:1 c=IN IP4 239.100.1.1/32 m=audio 5004 RTP/AVP 97 a=mid:2 a=rtpmap:97 L24/48000/2 a=ptime:1 c=IN IP4 239.100.1.2/32 ``` -------------------------------- ### Install AES67 Stream Monitor Binaries Source: https://context7.com/philhartung/aes67-monitor/llms.txt Download pre-built binaries for macOS, Windows, or Linux from the GitHub releases page. Note that binaries are not currently signed. ```bash # Pre-built binaries are available for: # - macOS (.dmg) # - Windows (.exe) # - Linux (.AppImage) # Note: Binaries are currently not signed ``` -------------------------------- ### Main Process IPC: Save Settings Source: https://context7.com/philhartung/aes67-monitor/llms.txt Persist application settings by sending a 'save' message with a key and the stringified data. This example shows saving buffer, filtering, and UI state settings. ```javascript { type: "save", key: "settings", data: JSON.stringify({ bufferSize: 16, bufferEnabled: true, hideUnsupported: true, sdpDeleteTimeout: 300, sidebarCollapsed: false }) } ``` -------------------------------- ### Control RTP Audio Playback via IPC Source: https://context7.com/philhartung/aes67-monitor/llms.txt Use these message types to start, restart, or stop audio playback in the audio child process. ```javascript // Audio process message handlers (src/lib/audio.js) // Handles RTP reception and audio playback // Start audio playback for a stream process.send({ type: "start", data: { id: "stream-id", mcast: "239.69.1.1", // Multicast group address port: 5004, // RTP port codec: "L24", // L16 or L24 ptime: 1, // Packet time in ms samplerate: 48000, // Sample rate Hz channels: 8, // Total channels in stream ch1Map: 0, // Channel index for left output ch2Map: 1, // Channel index for right output audioAPI: 4, // RtAudioApi constant networkInterface: "192.168.1.10", selected: { // Target audio device id: 0, name: "MacBook Pro Speakers", inputChannels: 0, outputChannels: 2 }, jitterBufferEnabled: true, jitterBufferSize: 16, // Packets to buffer filter: false, // Source filter enabled filterAddr: "" // Filter source IP } }); // Restart playback with updated parameters (e.g., device change) process.send({ type: "restart", data: { networkInterface: "192.168.1.10", selected: { name: "External DAC", ... } } }); // Stop audio playback process.send({ type: "stop" }); ``` -------------------------------- ### Build Application Source: https://github.com/philhartung/aes67-monitor/blob/main/README.md Compile the application binary for the current platform. ```bash npm run build ``` -------------------------------- ### Configure Persistent Application Settings Source: https://context7.com/philhartung/aes67-monitor/llms.txt Sets up default persistent data structure and selects the appropriate audio API based on the operating system using electron-store. ```javascript // Settings structure and defaults (main.js) const Store = require("electron-store"); const store = new Store(); // Default persistent data structure let persistentData = store.get("persistentData", { settings: { bufferSize: 16, // Jitter buffer size in packets bufferEnabled: true, // Enable/disable jitter buffering hideUnsupported: true, // Hide streams with unsupported formats sdpDeleteTimeout: 300, // Remove stale streams after N seconds sidebarCollapsed: false // Sidebar UI state } }); // Audio API selection based on platform const { RtAudio, RtAudioApi } = require("audify"); let audioAPI = RtAudioApi.UNSPECIFIED; switch (process.platform) { case "darwin": audioAPI = RtAudioApi.MACOSX_CORE; // CoreAudio on macOS break; case "win32": audioAPI = RtAudioApi.WINDOWS_WASAPI; // WASAPI on Windows break; case "linux": audioAPI = RtAudioApi.LINUX_ALSA; // ALSA on Linux break; } // Stored configuration keys: // - "interface": Last selected network interface // - "audioInterface": Last selected audio output device // - "persistentData": All user settings // - "streams": Manually added streams (persisted) ``` -------------------------------- ### Main Process IPC: Update Request Source: https://context7.com/philhartung/aes67-monitor/llms.txt Send an 'update' message to request initial data and initialize the SDP process. The main process will respond with 'updatePersistentData' and 'interfaces'. ```javascript { type: "update" } ``` -------------------------------- ### Main Process IPC: Set Audio Interface Source: https://context7.com/philhartung/aes67-monitor/llms.txt Configure the active audio output device by sending a 'setAudioInterface' message with the device name and channel information. ```javascript { type: "setAudioInterface", data: { name: "MacBook Pro Speakers", inputChannels: 0, outputChannels: 2 } } ``` -------------------------------- ### Manage SDP Stream Discovery via IPC Source: https://context7.com/philhartung/aes67-monitor/llms.txt Use these message types to initialize discovery, add manual streams, or manage the stream list in the SDP child process. ```javascript // SDP process message handlers (src/lib/sdp.js) // Communicates via Node.js child process IPC // Initialize SDP discovery on a network interface // Binds to port 9875 and joins multicast group 239.255.255.255 process.send({ type: "init", data: "192.168.1.10" // Local IP address to bind }); // Add a manual stream from raw SDP process.send({ type: "add", data: { sdp: `v=0 o=- 1234567890 1 IN IP4 192.168.1.100 s=Studio A Mix i=Main stereo mix c=IN IP4 239.69.1.1/32 t=0 0 a=recvonly m=audio 5004 RTP/AVP 97 a=rtpmap:97 L24/48000/2 a=ptime:1`, announce: true // Broadcast this stream via SAP } }); // Delete a stream by its ID (MD5 hash of origin) process.send({ type: "delete", data: "abc123def456..." }); // Change the network interface for multicast process.send({ type: "interface", data: "10.0.0.5" }); // Set timeout for removing stale streams (in seconds) process.send({ type: "deleteTimeout", data: 300 // 5 minutes }); // Request current streams list update process.send({ type: "update" }); ``` -------------------------------- ### Main Process IPC: Add Stream via SDP Source: https://context7.com/philhartung/aes67-monitor/llms.txt Add a new stream manually by providing its raw SDP data in an 'addStream' message. Set 'announce' to true to also broadcast it via SAP. ```javascript { type: "addStream", data: { sdp: "v=0\no=- 123456 1 IN IP4 192.168.1.100\n...", announce: true // Also broadcast via SAP } } ``` -------------------------------- ### Main Process IPC: Restart Audio Playback Source: https://context7.com/philhartung/aes67-monitor/llms.txt Use the 'restart' message to reinitialize audio playback with the current settings, typically after a device change. ```javascript { type: "restart" } ``` -------------------------------- ### Electron Preload Script for IPC Source: https://context7.com/philhartung/aes67-monitor/llms.txt Exposes a secure API via Electron's context bridge for renderer-to-main process communication. It defines methods for receiving messages from the main process and sending messages to it. ```javascript // Preload script (preload.js) const { contextBridge, ipcRenderer } = require("electron/renderer"); contextBridge.exposeInMainWorld("electronAPI", { // Receive messages from main process recvMessage: (callback) => ipcRenderer.on("send-message", (_event, value) => callback(value)), // Send messages to main process sendMessage: (message) => ipcRenderer.send("recv-message", message) }); // Usage in renderer (src/app.js): if (window.electronAPI) { // Listen for messages from main process window.electronAPI.recvMessage((message) => { switch (message.type) { case "streams": streams.value = message.data; streamCount.value = message.data.length; break; case "interfaces": networkInterfaces.value = message.data; break; case "audioDevices": audioInterfaces.value = message.data; break; case "updatePersistentData": persistentData.value = message.data; break; } }); // Request initial data sendMessage({ type: "update" }); } ``` -------------------------------- ### Main Process IPC: Play AES67 Stream Source: https://context7.com/philhartung/aes67-monitor/llms.txt Initiate playback of an AES67 stream by sending a 'play' message with detailed stream parameters including ID, multicast address, port, codec, sample rate, channels, and jitter buffer settings. ```javascript { type: "play", data: { id: "stream-unique-id", mcast: "239.69.1.1", port: 5004, codec: "L24", ptime: 1, samplerate: 48000, channels: 8, ch1Map: 0, // Left channel index (0-based) ch2Map: 1, // Right channel index (0-based) jitterBufferEnabled: true, jitterBufferSize: 16, filter: false, filterAddr: "" } } ``` -------------------------------- ### Main Process IPC: Set Network Interface Source: https://context7.com/philhartung/aes67-monitor/llms.txt Change the network interface used for stream discovery by sending a 'setNetwork' message with the desired IP address. ```javascript { type: "setNetwork", data: "192.168.1.10" } ``` -------------------------------- ### Main Process IPC: Stop Audio Playback Source: https://context7.com/philhartung/aes67-monitor/llms.txt Send a 'stop' message to halt the current audio playback. ```javascript { type: "stop" } ``` -------------------------------- ### Audio Process API Source: https://context7.com/philhartung/aes67-monitor/llms.txt The audio child process is responsible for receiving RTP multicast streams, decoding PCM audio, and outputting to the selected audio device using RtAudio. ```APIDOC ## Audio Process API ### Description Receives RTP multicast streams, decodes PCM audio, and outputs to the selected audio device using RtAudio. ### Method Child Process IPC Messages ### Endpoint N/A (Child Process Communication) ### Parameters #### Start Playback - **type** (string) - Required - Message type, should be "start" - **data** (object) - Required - Playback configuration - **id** (string) - Required - Stream ID to play - **mcast** (string) - Required - Multicast group address - **port** (integer) - Required - RTP port - **codec** (string) - Required - Audio codec (L16 or L24) - **ptime** (integer) - Required - Packet time in ms - **samplerate** (integer) - Required - Sample rate in Hz - **channels** (integer) - Required - Total channels in the stream - **ch1Map** (integer) - Required - Channel index for left output - **ch2Map** (integer) - Required - Channel index for right output - **audioAPI** (integer) - Required - RtAudioApi constant - **networkInterface** (string) - Required - Network interface IP address - **selected** (object) - Required - Target audio device - **id** (integer) - Required - Device ID - **name** (string) - Required - Device name - **inputChannels** (integer) - Required - Input channels - **outputChannels** (integer) - Required - Output channels - **jitterBufferEnabled** (boolean) - Optional - Enable jitter buffer - **jitterBufferSize** (integer) - Optional - Jitter buffer size in packets - **filter** (boolean) - Optional - Enable source filter - **filterAddr** (string) - Optional - Source IP address for filter #### Restart Playback - **type** (string) - Required - Message type, should be "restart" - **data** (object) - Required - Updated playback parameters (e.g., networkInterface, selected device) #### Stop Playback - **type** (string) - Required - Message type, should be "stop" ### Supported Configurations - **Codecs**: L16 (16-bit PCM), L24 (24-bit PCM) - **Sample rates**: 16000, 32000, 44100, 48000, 88200, 96000, 192000 Hz - **Channels**: 1-64 per stream - **Packet times**: 0.125ms, 1ms, 2ms, 4ms ``` -------------------------------- ### Vue 3 Reactive State Management Source: https://context7.com/philhartung/aes67-monitor/llms.txt Manages application state using Vue 3's composition API with reactive refs. Includes state for UI, streams, settings, and functions for filtering, channel selection, and stream playback control. IPC communication is handled via a sendMessage function. ```javascript // Application state exports (src/app.js) import { ref, computed } from "vue"; // Core reactive state export const page = ref("streams"); // Current page view export const search = ref({ streams: "", interfaces: "", devices: "" }); export const streams = ref([]); // Discovered streams export const streamCount = ref(0); // Total stream count export const playing = ref(""); // Currently playing stream ID export const selectedStream = ref(null); // Stream detail view export const selectedChannel = ref([]); // Channel selections per stream export const streamIndex = ref([]); // Media index for redundant streams // Audio and network interfaces export const audioInterfaces = ref([]); export const networkInterfaces = ref([]); // Persistent settings export const persistentData = ref({ settings: { bufferSize: 16, // Jitter buffer packets bufferEnabled: true, // Enable jitter buffering hideUnsupported: true, // Filter unsupported streams sdpDeleteTimeout: 300, // Stream expiry (seconds) sidebarCollapsed: false }, devices: {} // Device name/description cache }); // Stream filtering with search and settings export const searchStreams = () => { return streams.value.filter((stream) => { return ( (stream.name.toLowerCase().includes(search.value.streams.toLowerCase()) || stream.origin.address.includes(search.value.streams) || stream.id.includes(search.value.streams)) && (stream.isSupported || !persistentData.value.settings.hideUnsupported) ); }); }; // Get channel selection options for a stream export const getChannelSelectValues = (stream) => { let mono = [], stereo = []; for (let i = 0; i < stream.channels; i++) { mono.push({ value: `${i},${i}`, string: `${i + 1} Mono` }); if (i % 2 === 0 && stream.channels > 1) { stereo.push({ value: `${i},${i + 1}`, string: `${i + 1} + ${i + 2} Stereo` }); } } return stereo.concat(mono); // Stereo options first }; // Play/stop stream toggle export const playStream = (stream) => { if (playing.value === stream.id) { playing.value = ""; sendMessage({ type: "stop" }); } else { playing.value = stream.id; const [ch1, ch2] = selectedChannel.value[stream.id].split(",").map(Number); const mediaIdx = streamIndex.value[stream.id] || 0; const media = stream.media[mediaIdx]; sendMessage({ type: "play", data: { id: stream.id, mcast: mediaIdx > 0 ? media.connection.ip.split("/")[0] : stream.mcast, port: media.port, codec: media.rtp[0].codec, ptime: media.ptime, samplerate: media.rtp[0].rate, channels: media.rtp[0].encoding, ch1Map: ch1, ch2Map: ch2, jitterBufferEnabled: persistentData.value.settings.bufferEnabled, jitterBufferSize: persistentData.value.settings.bufferSize, filter: !!media.sourceFilter, filterAddr: media.sourceFilter?.srcList || "" } }); } }; // IPC communication with Electron main process export const sendMessage = (message) => { window.electronAPI?.sendMessage(message); }; ``` -------------------------------- ### IPC Message Types Source: https://context7.com/philhartung/aes67-monitor/llms.txt Defines the message structures sent from the renderer process to the main process via window.electronAPI.sendMessage() to control application state. ```APIDOC ## IPC Message Types ### Description These messages are sent from the renderer process to the main process to manage audio playback, stream discovery, and application settings. ### Method IPC (Inter-Process Communication) ### Request Body - **type** (string) - Required - The action type (e.g., "play", "stop", "setAudioInterface") - **data** (object/string) - Optional - Payload associated with the action ### Request Examples #### Play Stream { "type": "play", "data": { "id": "stream-unique-id", "mcast": "239.69.1.1", "port": 5004, "codec": "L24", "ptime": 1, "samplerate": 48000, "channels": 8, "ch1Map": 0, "ch2Map": 1, "jitterBufferEnabled": true, "jitterBufferSize": 16, "filter": false, "filterAddr": "" } } #### Set Audio Interface { "type": "setAudioInterface", "data": { "name": "MacBook Pro Speakers", "inputChannels": 0, "outputChannels": 2 } } #### Add Manual Stream { "type": "addStream", "data": { "sdp": "v=0\no=- 123456 1 IN IP4 192.168.1.100\n...", "announce": true } } ``` -------------------------------- ### Main Process IPC: Delete Stream Source: https://context7.com/philhartung/aes67-monitor/llms.txt Remove a stream from monitoring by sending a 'delete' message with the stream's unique ID. ```javascript { type: "delete", data: "stream-id-hash" } ``` -------------------------------- ### SDP Processing API Source: https://context7.com/philhartung/aes67-monitor/llms.txt The SDP child process is responsible for discovering streams via SAP multicast, parsing SDP data, and maintaining the list of active stream sessions. ```APIDOC ## SDP Processing API ### Description Handles stream discovery via SAP multicast, parses SDP data, and manages the stream session list. ### Method Child Process IPC Messages ### Endpoint N/A (Child Process Communication) ### Parameters #### Init - **type** (string) - Required - Message type, should be "init" - **data** (string) - Required - Local IP address to bind to #### Add Stream - **type** (string) - Required - Message type, should be "add" - **data** (object) - Required - Stream data - **sdp** (string) - Required - Raw SDP data for the stream - **announce** (boolean) - Optional - Broadcast this stream via SAP #### Delete Stream - **type** (string) - Required - Message type, should be "delete" - **data** (string) - Required - Unique stream identifier (MD5 hash of origin) #### Change Interface - **type** (string) - Required - Message type, should be "interface" - **data** (string) - Required - New local IP address for the network interface #### Set Delete Timeout - **type** (string) - Required - Message type, should be "deleteTimeout" - **data** (integer) - Required - Timeout in seconds for removing stale streams #### Request Update - **type** (string) - Required - Message type, should be "update" ### Response Example (Stream Object Structure) ```json { "id": "md5-hash-of-origin", "name": "Studio A Mix", "description": "Main mix", "raw": "v=0\no=...", "origin": { "address": "192.168.1.100", "username": "-", "sessionId": "1234567890" }, "mcast": "239.69.1.1", "codec": "L24", "samplerate": 48000, "channels": 2, "rtpMap": "L24/48000/2", "isSupported": true, "unsupportedReason": null, "dante": false, "manual": false, "announce": false, "lastSeen": 1699123456789, "media": [ { "type": "audio", "port": 5004, "protocol": "RTP/AVP", "payloads": "97", "ptime": 1, "rtp": [ { "payload": 97, "codec": "L24", "rate": 48000, "encoding": 2 } ], "connection": { "ip": "239.69.1.1/32" }, "sourceFilter": null, "description": "Main stereo mix" } ] } ``` ``` -------------------------------- ### Define SDP Stream Object Structure Source: https://context7.com/philhartung/aes67-monitor/llms.txt The structure of the stream object returned by the SDP process for discovered or manually added streams. ```javascript // Stream object structure returned from SDP process: const streamObject = { id: "md5-hash-of-origin", // Unique stream identifier name: "Studio A Mix", // SDP session name description: "Main mix", // Optional description raw: "v=0\no=...", // Original SDP text origin: { address: "192.168.1.100", // Source device IP username: "-", sessionId: "1234567890" }, mcast: "239.69.1.1", // Multicast destination codec: "L24", // Audio codec (L16 or L24) samplerate: 48000, // Sample rate in Hz channels: 2, // Number of audio channels rtpMap: "L24/48000/2", // Full RTP map string isSupported: true, // Whether playback is supported unsupportedReason: null, // Reason if not supported dante: false, // Dante device flag manual: false, // Manually added flag announce: false, // Broadcasting via SAP lastSeen: 1699123456789, // Unix timestamp media: [{ // Media descriptions array type: "audio", port: 5004, protocol: "RTP/AVP", payloads: "97", ptime: 1, rtp: [{ payload: 97, codec: "L24", rate: 48000, encoding: 2 }], connection: { ip: "239.69.1.1/32" }, sourceFilter: null, description: "Main stereo mix" }] }; ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.