diff --git a/.env.example b/.env.example index b08a815..97c59bf 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,43 @@ +# AWS S3 Configuration (existing) S3_BUCKET= S3_REGION= S3_ACCESS_KEY_ID= S3_SECRET_ACCESS_KEY= S3_CLOUDFRONT_DISTRIBUTION_ID= + +# OpenSIPS-JS Testing Framework Environment Configuration + +# Test execution +SAMPLETOEXECUTE=tests2/samples/e2e/sample.json +PORT=5173 + +# GIGAPIPE Configuration for qryn integration +# DEFAULT configuration is used as fallback for all services if no specific config provided + +# Default configuration (fallback for all services) +GIGAPIPE.DEFAULT.url=https://your-qryn-instance.com +GIGAPIPE.DEFAULT.scope=test +GIGAPIPE.DEFAULT.headers.Authorization=Bearer your-token +GIGAPIPE.DEFAULT.headers.X-Scope-OrgID=your-org-id + +# Service-specific overrides (optional) +# GIGAPIPE.TRACING.url=https://your-qryn-tracing.com +# GIGAPIPE.TRACING.scope=tracing +# GIGAPIPE.TRACING.headers.Authorization=Bearer your-tracing-token + +# GIGAPIPE.METRICS.url=https://your-qryn-metrics.com +# GIGAPIPE.METRICS.scope=metrics +# GIGAPIPE.METRICS.headers.Authorization=Bearer your-metrics-token + +# GIGAPIPE.LOGS.url=https://your-qryn-logs.com +# GIGAPIPE.LOGS.scope=logs +# GIGAPIPE.LOGS.headers.Authorization=Bearer your-logs-token + +# SIP test parameters +PARAMETERS.CALLER.sip_domain=your-sip-domain.com +PARAMETERS.CALLER.username=caller +PARAMETERS.CALLER.password=your-password + +PARAMETERS.CALLEE.sip_domain=your-sip-domain.com +PARAMETERS.CALLEE.username=callee +PARAMETERS.CALLEE.password=your-password \ No newline at end of file diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 1872f78..349e5b1 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -9,10 +9,12 @@ module.exports = { ], ignorePatterns: [ 'src/helpers/webrtcmetrics/', - 'docs' + 'docs', + 'tests/ui' ], rules: { - 'space-before-blocks': 'off' + 'space-before-blocks': 'off', + 'no-dupe-class-members': 'off' }, env: { es2021: true, diff --git a/.gitignore b/.gitignore index 6eef829..ccd5bea 100644 --- a/.gitignore +++ b/.gitignore @@ -188,3 +188,9 @@ fabric.properties # .pnp.* # End of https://www.toptal.com/developers/gitignore/api/webstorm,node,yarn + +# Playwright +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..94c91ec --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,63 @@ +stages: + - test_stage + - build_stage + - deploy_stage + +variables: + IMAGE_NAME: "registry.voicenter.co/voicenter/mirrored-opensips-js" + GITHUB_IMAGE_NAME: "ghcr.io/voicenterteam/opensipsjs-test" + +test: + stage: test_stage + image: mcr.microsoft.com/playwright:v1.52.0-jammy + script: + - yarn install --frozen-lockfile + - npx playwright install + - chmod +x start.sh + - ./start.sh + only: + - branches + artifacts: + paths: + - node_modules/ + - dist/ + expire_in: 1 hour + tags: + - gitRunCT + + +build: + stage: build_stage + image: docker:latest + services: + - docker:dind + script: + - docker login registry.voicenter.co -u $dplUser -p $dplPswd + - docker build -t $IMAGE_NAME:$CI_COMMIT_BRANCH . + - docker tag $IMAGE_NAME:$CI_COMMIT_BRANCH $IMAGE_NAME:latest + - docker push $IMAGE_NAME:${CI_COMMIT_BRANCH} + - docker push $IMAGE_NAME:latest + dependencies: + - test + only: + - branches + tags: + - gitRunCT + +deploy: + stage: deploy_stage + image: docker:latest + services: + - docker:dind + script: + - docker login registry.voicenter.co -u $dplUser -p $dplPswd + - docker pull $IMAGE_NAME:$CI_COMMIT_BRANCH + - echo "$GHCR_TOKEN" | docker login ghcr.io -u $GHCR_USER --password-stdin + - docker tag $IMAGE_NAME:$CI_COMMIT_BRANCH $GITHUB_IMAGE_NAME:$CI_COMMIT_BRANCH + - docker push $GITHUB_IMAGE_NAME:$CI_COMMIT_BRANCH + only: + - branches + dependencies: + - build + tags: + - gitRunCT \ No newline at end of file diff --git a/DEBUG_SETUP.md b/DEBUG_SETUP.md new file mode 100644 index 0000000..79bf9ff --- /dev/null +++ b/DEBUG_SETUP.md @@ -0,0 +1,140 @@ +# Debug Server Setup for OpenSIPS-JS Testing Framework + +This setup provides a local debug server to collect and visualize logs, metrics, and traces from your OpenSIPS-JS testing framework. + +## Quick Start + +### 1. Start the Debug Server + +```bash +yarn debug-server +``` + +This starts the debug server at `http://localhost:3001` + +### 2. Access the Dashboard + +Open your browser and go to: +``` +http://localhost:3001 +``` + +You'll see a real-time dashboard showing: +- šŸ“ **Logs**: All QrynLogger output with structured metadata +- šŸ“Š **Metrics**: Test event metrics and custom metrics +- šŸ”— **Traces**: OpenTelemetry traces with spans +- šŸŽµ **WebRTC**: Real-time audio quality metrics + +### 3. Run Tests with Debug Mode + +```bash +yarn run-test-debug +``` + +This automatically: +1. Copies the debug configuration (`.env.debug` → `.env`) +2. Runs your tests with all telemetry pointing to the local debug server + +## What You'll See + +### Dashboard Features + +- **Real-time Updates**: Auto-refreshes every 5 seconds +- **Scenario Correlation**: All data tagged with scenario names for easy filtering +- **Structured Data**: Logs include metadata, metrics include labels +- **Clear Functionality**: Reset collected data anytime +- **Color Coding**: Errors highlighted in red, different data types color-coded + +### Logs Section +``` +2024-01-15T10:30:15.123Z [My Test Scenario] [ActionsExecutor] - Executing register action +{ + "data": { + "username": "caller", + "sip_domain": "example.com" + } +} +``` + +### Metrics Section +``` +opensips_test_events_total: 1 +Labels: { + "scenario_name": "My Test Scenario", + "event_name": "register", + "stage": "triggered", + "status": "success" +} +``` + +### WebRTC Section +``` +opensips_webrtc_jitter_ms: 12.5 +Labels: { + "scenario_name": "My Test Scenario", + "metric_type": "webrtc_audio" +} +``` + +## Debug Server Endpoints + +The server exposes the same endpoints that qryn would use: + +- **Logs**: `POST /loki/api/v1/push` (Loki format) +- **Traces**: `POST /v1/traces` (OpenTelemetry format) +- **Metrics**: `POST /v1/metrics` (OpenTelemetry format) +- **Prometheus**: `POST /api/v1/prom/remote/write` (Prometheus format) +- **Legacy**: `POST /collect-metrics` (Fallback format) + +## Configuration + +The debug configuration (`.env.debug`) sets: + +```bash +# Local debug server configuration +GIGAPIPE.DEFAULT.url=http://localhost:3001 +GIGAPIPE.DEFAULT.scope=debug +GIGAPIPE.DEFAULT.headers.Content-Type=application/json +``` + +This makes all services (logs, traces, metrics) use the local debug server. + +## Troubleshooting + +### No Data Appearing? + +1. **Check Server**: Ensure debug server is running on port 3001 +2. **Check Configuration**: Verify `.env` points to `http://localhost:3001` +3. **Check Console**: Look for connection errors in the debug server console +4. **Check Browser**: Open browser dev tools to see any frontend errors + +### Port Conflicts? + +If port 3001 is busy, edit `debug-server.js`: +```javascript +const PORT = 3002 // Change to available port +``` + +And update `.env.debug`: +```bash +GIGAPIPE.DEFAULT.url=http://localhost:3002 +``` + +### Clear Old Data + +Click "Clear All" in the dashboard or restart the debug server. + +## Production vs Debug + +- **Debug Mode**: Uses local server for easy visualization +- **Production Mode**: Configure real qryn endpoints in `.env` + +The same code works for both - just change the endpoint URLs! + +## Benefits + +- āœ… **Verify Integration**: See that all telemetry is working +- āœ… **Debug Issues**: Trace problems across logs/metrics/traces +- āœ… **Performance Monitoring**: Watch WebRTC quality in real-time +- āœ… **Scenario Correlation**: Filter by scenario name +- āœ… **No External Dependencies**: Works offline locally \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..732108f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM mcr.microsoft.com/playwright:v1.52.0-jammy + +WORKDIR /app + +COPY package.json yarn.lock ./ +RUN apt-get update && yarn install --ignore-engines +RUN npx playwright install + + +COPY . . + +EXPOSE 5173 + +COPY start.sh ./ +RUN chmod +x start.sh + +CMD ["./start.sh"] diff --git a/README.md b/README.md index cd40bdc..7793404 100644 --- a/README.md +++ b/README.md @@ -119,27 +119,33 @@ If you prefer using ES modules in the browser and your environment supports them import OpensipsJS from 'https://cdn.opensipsjs.org/opensipsjs/v0.1.1/opensips-js.es.js';\ const opensipsJS = new OpensipsJS({ - configuration: { - session_timers: false, - uri: 'sip:extension_user@domain', - // --- Use password or authorization_jwt to authorize - password: 'password', - // or - authorization_jwt: 'token', - }, - socketInterfaces: ['wss://domain'], - pnExtraHeaders: { - 'pn-provider': 'acme', - 'pn-param': 'acme-param', - 'pn-prid': 'ZH11Y4ZDJlMNzODE1NgKi0K>', - }, - sipDomain: 'domain', - sipOptions: { - session_timers: false, - extraHeaders: ['X-Bar: bar'], - pcConfig: {}, - }, - modules: ['audio', 'video', 'msrp'], + configuration: { + session_timers: false, + noiseReductionOptions: { + mode: 'dynamic', + noiseThreshold: 0.004 + checkEveryMs: 500 + noiseCheckInterval: 2000 + }, + uri: 'sip:extension_user@domain', + // --- Use password or authorization_jwt to authorize + password: 'password', + // or + authorization_jwt: 'token', + }, + socketInterfaces: ['wss://domain'], + pnExtraHeaders: { + 'pn-provider': 'acme', + 'pn-param': 'acme-param', + 'pn-prid': 'ZH11Y4ZDJlMNzODE1NgKi0K>', + }, + sipDomain: 'domain', + sipOptions: { + session_timers: false, + extraHeaders: ['X-Bar: bar'], + pcConfig: {}, + }, + modules: ['audio', 'video', 'msrp'], }); // Use the modules as before @@ -223,6 +229,7 @@ Also, there are next public fields on OpensipsJS instance: - `setSpeakerVolume(value: Number): void` - set volume of callers. Value should be in range from 0 to 1 - `setDND(value: Boolean): void` - set the agent "Do not disturb" status - `setMetricsConfig(config: WebrtcMetricsConfigType): void` - set the metric config (used for audio quality indicator) +- `setVADConfiguration(options: Partial>): void` - update noise reduction configuration at runtime. **Requires `vadModule` to be passed in the constructor, otherwise throws an error** ### Audio instance fields - `sipOptions: Object` - returns sip options @@ -237,6 +244,127 @@ Also, there are next public fields on OpensipsJS instance: - `isDND: Boolean` - returns if the agent is in "Do not disturb" status - `isMuted: Boolean` - returns if the agent is muted +### Noise Reduction Options (VAD) + +**Important**: Voice Activity Detection (VAD) is an **optional feature** that requires installing an additional peer dependency. It is **NOT compatible with React Native**. + +#### Critical: VAD Module Must Be Passed to Constructor + +**If you plan to use noise reduction features (including `setVADConfiguration` in runtime), you MUST pass `vadModule` to the OpenSIPSJS constructor during initialization.** + +- āœ… **Required**: Pass `vadModule` in the constructor if you want to use noise reduction +- āŒ **Will throw error**: Calling `setVADConfiguration()` without `vadModule` in the constructor will throw an error +- āš ļø **Cannot be changed later**: The `vadModule` cannot be set after initialization - it must be provided in the constructor + +#### For Web Applications (with VAD support) + +Install the VAD library: +```bash +npm install @ricky0123/vad-web +# or +yarn add @ricky0123/vad-web +``` + +Then import and inject it in your configuration **during initialization**: +```javascript +import OpenSIPSJS from 'opensips-js' +import * as VAD from '@ricky0123/vad-web' + +const opensipsJS = new OpenSIPSJS({ + configuration: { + // ... other configuration + noiseReductionOptions: { + mode: 'dynamic', // or 'enabled' + vadModule: VAD, // āš ļø REQUIRED: Must be passed here if you plan to use noise reduction + noiseThreshold: 0.004, + checkEveryMs: 500, + noiseCheckInterval: 2000 + } + }, + // ... rest of configuration +}) + +// āœ… Now you can use setVADConfiguration +opensipsJS.audio.setVADConfiguration({ + mode: 'enabled', + noiseThreshold: 0.005 +}) +``` + +#### For Chrome MV3 Extensions + +Chrome Manifest V3 extensions block dynamic imports from external sources at the browser level. To use VAD in a Chrome MV3 extension, you must bundle the VAD assets locally. + +**1. Copy required files to your extension:** + +From `node_modules/@ricky0123/vad-web/dist/`: +- `silero_vad_legacy.onnx` +- `silero_vad_v5.onnx` +- `vad.worklet.bundle.min.js` + +From `node_modules/onnxruntime-web/dist/`: +- `ort-wasm-simd-threaded.mjs` +- `ort-wasm-simd-threaded.wasm` + +Place them in your extension directory (e.g., `assets/vad/` and `assets/onnx/`). + +**Note**: The exact ONNX files needed may vary depending on browser capabilities. If you encounter loading errors, you may also need `ort-wasm-simd-threaded.jsep.wasm` or other variants from the `onnxruntime-web/dist/` folder. + +**2. Configure OpenSIPSJS with local paths:** + +```javascript +import OpenSIPSJS from 'opensips-js' +import * as VAD from '@ricky0123/vad-web' + +const opensipsJS = new OpenSIPSJS({ + configuration: { + // ... other configuration + noiseReductionOptions: { + mode: 'dynamic', + vadModule: VAD, + // Point to locally bundled assets + baseAssetPath: browser.runtime.getURL('assets/vad/'), + onnxWASMBasePath: browser.runtime.getURL('assets/onnx/') + } + }, + // ... rest of configuration +}) +``` + +**Note**: If your extension page is opened via `browser.windows.create()` or similar (extension's own context), you don't need `web_accessible_resources`. The extension can access its bundled files directly. + +#### For React Native Applications (VAD not supported) + +Simply omit the VAD module and disable noise reduction: +```javascript +import OpenSIPSJS from 'opensips-js' + +const opensipsJS = new OpenSIPSJS({ + configuration: { + // ... other configuration + noiseReductionOptions: { + mode: 'disabled' // or omit noiseReductionOptions entirely + } + // NO vadModule needed + }, + // ... rest of configuration +}) +``` + +**See [VAD_USAGE.md](VAD_USAGE.md) and [EXAMPLES.md](EXAMPLES.md) for detailed usage examples.** + +#### Configuration Parameters + +| Parameter | Type | Default | Description | +|----------------------|----------------------------------|----------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `mode` | `disabled \| enabled \| dynamic` | `disabled` | Noise reduction mode. **Note**: `enabled` and `dynamic` modes require `vadModule` to be provided | +| `vadConfig` | `Partial` | `{}` | VAD configuration | +| `noiseThreshold` | `number` | `0.004` | Noise threshold | +| `noiseCheckInterval` | `number` | `2000` | The interval, used to check if we need to disable/enable outgoing audio every N-milliseconds | +| `checkEveryMs` | `number` | `500` | The interval, used inside noiseCheckInterval loop, checks current noise state every N-milliseconds, to define the average noise level. Then on every noiseCheckInterval iteration, the values getting on checkEveryMs will be summed, then divided by it's number and compared to noiseThreshold | +| `baseAssetPath` | `string` | `https://cdn.jsdelivr.net/npm/@ricky0123/vad-web@0.0.28/dist/` | Base path for VAD web assets. For Chrome MV3 extensions, use local bundled path via `browser.runtime.getURL()` | +| `onnxWASMBasePath` | `string` | `https://cdn.jsdelivr.net/npm/onnxruntime-web@1.22.0/dist/` | Base path for ONNX runtime WASM files. For Chrome MV3 extensions, use local bundled path via `browser.runtime.getURL()` | + ## MSRP ### MSRP methods diff --git a/debug-server.js b/debug-server.js new file mode 100644 index 0000000..354f1c6 --- /dev/null +++ b/debug-server.js @@ -0,0 +1,394 @@ +const express = require('express') +const cors = require('cors') +const fs = require('fs') +const path = require('path') + +const app = express() +const PORT = 3001 + +// Enable CORS and JSON parsing +app.use(cors()) +app.use(express.json()) +app.use(express.text({ type: 'text/plain' })) + +// Storage for collected data +const storage = { + logs: [], + traces: [], + metrics: [], + webrtcMetrics: [] +} + +// Utility function to add timestamp and format data +function addToStorage (type, data) { + const entry = { + timestamp: new Date().toISOString(), + data: data + } + storage[type].push(entry) + + // Keep only last 1000 entries to prevent memory issues + if (storage[type].length > 1000) { + storage[type] = storage[type].slice(-1000) + } + + console.log(`[${type.toUpperCase()}] ${entry.timestamp}:`, typeof data === 'string' ? data.substring(0, 100) : JSON.stringify(data).substring(0, 100)) +} + +// Loki API endpoint for logs +app.post('/loki/api/v1/push', (req, res) => { + try { + const payload = req.body + if (payload.streams) { + payload.streams.forEach(stream => { + stream.values.forEach(([ timestamp, message ]) => { + const logEntry = { + timestamp: new Date(parseInt(timestamp) / 1000000).toISOString(), + labels: stream.stream, + message: typeof message === 'string' ? JSON.parse(message) : message + } + addToStorage('logs', logEntry) + }) + }) + } + res.status(204).send() + } catch (error) { + console.error('Error processing logs:', error) + res.status(400).json({ error: error.message }) + } +}) + +// OpenTelemetry traces endpoint +app.post('/v1/traces', (req, res) => { + try { + const traces = req.body + if (traces.resourceSpans) { + traces.resourceSpans.forEach(resourceSpan => { + resourceSpan.scopeSpans?.forEach(scopeSpan => { + scopeSpan.spans?.forEach(span => { + const traceEntry = { + traceId: span.traceId, + spanId: span.spanId, + name: span.name, + attributes: span.attributes, + startTime: span.startTimeUnixNano, + endTime: span.endTimeUnixNano, + status: span.status + } + addToStorage('traces', traceEntry) + }) + }) + }) + } + res.status(200).json({ status: 'success' }) + } catch (error) { + console.error('Error processing traces:', error) + res.status(400).json({ error: error.message }) + } +}) + +// OpenTelemetry metrics endpoint +app.post('/v1/metrics', (req, res) => { + try { + const metrics = req.body + if (metrics.resourceMetrics) { + metrics.resourceMetrics.forEach(resourceMetric => { + resourceMetric.scopeMetrics?.forEach(scopeMetric => { + scopeMetric.metrics?.forEach(metric => { + const metricEntry = { + name: metric.name, + description: metric.description, + unit: metric.unit, + data: metric.gauge || metric.sum || metric.histogram + } + addToStorage('metrics', metricEntry) + }) + }) + }) + } + res.status(200).json({ status: 'success' }) + } catch (error) { + console.error('Error processing metrics:', error) + res.status(400).json({ error: error.message }) + } +}) + +// Prometheus metrics endpoint +app.post('/api/v1/prom/remote/write', (req, res) => { + try { + const metricsText = req.body + console.log('[DEBUG] Raw metrics received:', metricsText.substring(0, 200) + '...') + + const lines = metricsText.split('\n').filter(line => line.trim()) + console.log('[DEBUG] Parsed lines count:', lines.length) + + lines.forEach(line => { + const match = line.match(/^([a-zA-Z_][a-zA-Z0-9_]*){([^}]*)} ([0-9.-]+) ([0-9]+)$/) + if (match) { + const [ , metricName, labelsStr, value, timestamp ] = match + const labels = {} + + // Parse labels + labelsStr.split(',').forEach(label => { + const [ key, val ] = label.split('=') + if (key && val) { + labels[key.trim()] = val.trim().replace(/"/g, '') + } + }) + + const metricEntry = { + name: metricName, + labels: labels, + value: parseFloat(value), + timestamp: new Date(parseInt(timestamp)).toISOString() + } + + // Separate WebRTC metrics from general metrics + if (metricName.startsWith('opensips_webrtc_')) { + console.log('[DEBUG] Adding WebRTC metric:', metricName, metricEntry) + addToStorage('webrtcMetrics', metricEntry) + } else { + addToStorage('metrics', metricEntry) + } + } + }) + + res.status(200).send('OK') + } catch (error) { + console.error('Error processing Prometheus metrics:', error) + res.status(400).json({ error: error.message }) + } +}) + +// Legacy visualization server endpoint (fallback) +app.post('/collect-metrics', (req, res) => { + try { + addToStorage('metrics', req.body) + res.status(200).json({ status: 'received' }) + } catch (error) { + console.error('Error processing legacy metrics:', error) + res.status(400).json({ error: error.message }) + } +}) + +// Dashboard HTML page +app.get('/', (req, res) => { + const html = ` + + + + + + OpenSIPS-JS Debug Dashboard + + + +
+
+

šŸ” OpenSIPS-JS Debug Dashboard

+

Real-time monitoring of logs, metrics, and traces

+
+ +
+
+
šŸ“ Total Logs
+
0
+
+
+
šŸ“Š Total Metrics
+
0
+
+
+
šŸ”— Total Traces
+
0
+
+
+
šŸŽµ WebRTC Metrics
+
0
+
+
+ +
+
šŸ“ Logs
+
šŸ“Š Metrics
+
šŸ”— Traces
+
šŸŽµ WebRTC
+
+ + +
+
+ +
+ Loading... +
+
+ + + + + ` + res.send(html) +}) + +// API endpoint for dashboard data +app.get('/api/data', (req, res) => { + const limit = parseInt(req.query.limit) || 50 + + res.json({ + stats: { + logs: storage.logs.length, + metrics: storage.metrics.length, + traces: storage.traces.length, + webrtcMetrics: storage.webrtcMetrics.length + }, + logs: storage.logs.slice(-limit).reverse(), + metrics: storage.metrics.slice(-limit).reverse(), + traces: storage.traces.slice(-limit).reverse(), + webrtcMetrics: storage.webrtcMetrics.slice(-limit).reverse() + }) +}) + +// Clear data endpoint +app.post('/api/clear', (req, res) => { + storage.logs = [] + storage.traces = [] + storage.metrics = [] + storage.webrtcMetrics = [] + res.json({ status: 'cleared' }) +}) + +app.listen(PORT, () => { + console.log(`šŸ” Debug Server running at http://localhost:${PORT}`) + console.log(`šŸ“Š Dashboard: http://localhost:${PORT}`) + console.log(`šŸ“ Logs endpoint: http://localhost:${PORT}/loki/api/v1/push`) + console.log(`šŸ”— Traces endpoint: http://localhost:${PORT}/v1/traces`) + console.log(`šŸ“Š Metrics endpoint: http://localhost:${PORT}/v1/metrics`) + console.log(`šŸ“ˆ Prometheus endpoint: http://localhost:${PORT}/api/v1/prom/remote/write`) + console.log('\nšŸš€ Ready to collect telemetry data!') +}) diff --git a/src/helpers/volume.helper.ts b/demo/helpers/volume.helper.ts similarity index 89% rename from src/helpers/volume.helper.ts rename to demo/helpers/volume.helper.ts index c16d7ff..2656676 100644 --- a/src/helpers/volume.helper.ts +++ b/demo/helpers/volume.helper.ts @@ -1,14 +1,11 @@ -import { IntervalType } from '@/types/rtc' -import audioContext from '@/helpers/audioContext' - const height = 20 const lineWidth = 4 -let intervals: { [key: string]: IntervalType | undefined } = {} +let intervals: { [key: string]: ReturnType | undefined } = {} -export const runIndicator = (stream: MediaStream, deviceId: string) => { +export const runIndicator = (audioContext: AudioContext, stream: MediaStream, deviceId: string) => { if (stream && stream.getTracks().length) { - requestAnimationFrame(() => getVolumeLevelBar(stream, deviceId)) + requestAnimationFrame(() => getVolumeLevelBar(audioContext, stream, deviceId)) } else { clearVolumeInterval(deviceId) } @@ -32,7 +29,7 @@ const getMaxSmallIndicatorHeight = (value: number) => { return value < halfLineHeight ? value : halfLineHeight } -const getVolumeLevelBar = (stream: MediaStream, deviceId: string) => { +const getVolumeLevelBar = (audioContext: AudioContext, stream: MediaStream, deviceId: string) => { clearVolumeInterval(deviceId) const analyser = audioContext.createAnalyser() diff --git a/demo/index.html b/demo/index.html index 6c9e399..6e86698 100644 --- a/demo/index.html +++ b/demo/index.html @@ -17,23 +17,23 @@ @@ -84,6 +84,18 @@

Audio Calls


+ + +
+ Dynamic noise reduction: Not active +
+ +
+