diff --git a/package.json b/package.json index 701969e..a3aa837 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,6 @@ "prom-client": "^14.2.0", "puppeteer": "^19.11.1", "puppeteer-core": "^19.11.1", - "puppeteer-extra": "^3.3.6", "puppeteer-extra-plugin-stealth": "^2.11.2", "puppeteer-intercept-and-modify-requests": "^1.3.0", "sprintf-js": "^1.1.3", diff --git a/scripts/common.js b/scripts/common.js index f66f51d..c967bd3 100644 --- a/scripts/common.js +++ b/scripts/common.js @@ -440,7 +440,7 @@ window.streamWriter = async ( } return { - write(frameData, pts) { + write(frameData, pts = 0) { //log('write', filename, frameData.byteLength, pts) if (filename.endsWith('.ivf')) { const data = new ArrayBuffer(12 + frameData.byteLength) diff --git a/scripts/get-user-media.js b/scripts/get-user-media.js index 31485b6..0bc9a3f 100644 --- a/scripts/get-user-media.js +++ b/scripts/get-user-media.js @@ -1,4 +1,4 @@ -/* global log, loadScript, sleep, Tesseract, streamWriter, isSenderDisplayTrack */ +/* global log, loadScript, sleep, Tesseract, isSenderDisplayTrack, saveVideoTrack */ const applyOverride = (constraints, override) => { if (override) { @@ -123,72 +123,6 @@ function collectMediaTracks(mediaStream) { }) */ } -/** - * Save the MediaStream video track to disk. - * @param {MediaStreamTrack} videoTrack - */ -window.saveMediaStreamTrack = async (videoTrack, sendrecv, quality = 0.75) => { - const width = window.VIDEO_WIDTH - const height = window.VIDEO_HEIGHT - const frameRate = window.VIDEO_FRAMERATE - const fname = `${window.getParticipantName().split('_')[0]}-${sendrecv}_${ - videoTrack.id - }.ivf` - log(`saveMediaStreamTrack ${fname} ${width}x${height} ${frameRate}fps`) - const writer = await streamWriter(fname, width, height, frameRate, 'MJPG') - - const canvas = new OffscreenCanvas(width, height) - const ctx = canvas.getContext('2d') - let startTimestamp = -1 - const writableStream = new window.WritableStream( - { - async write(videoFrame) { - const { timestamp, codedWidth, codedHeight } = videoFrame - if (!codedWidth || !codedHeight) { - return - } - const bitmap = await createImageBitmap(videoFrame) - try { - ctx.drawImage(bitmap, 0, 0, width, height) - const blob = await canvas.convertToBlob({ - type: 'image/jpeg', - quality, - }) - const data = await blob.arrayBuffer() - if (startTimestamp < 0) { - startTimestamp = timestamp - } - const pts = Math.round( - (frameRate * (timestamp - startTimestamp)) / 1000000, - ) - /* log( - `writer ${data.byteLength} bytes timestamp=${ - videoFrame.timestamp / 1000000 - } pts=${pts}`, - ) */ - writer.write(data, pts) - } catch (err) { - log(`saveMediaStream error: ${err.message}`) - } - videoFrame.close() - bitmap.close() - }, - close() { - writer.close() - }, - abort(err) { - log('saveMediaStream error:', err) - }, - }, - new CountQueuingStrategy({ highWaterMark: frameRate * 2 }), - ) - - const trackProcessor = new window.MediaStreamTrackProcessor({ - track: videoTrack, - }) - trackProcessor.readable.pipeTo(writableStream) -} - /** * Replaces the MediaStream video track with a new generated one with * timestamp watermark. @@ -437,7 +371,7 @@ if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { if (window.PARAMS?.saveMediaStream) { const videoTrack = mediaStream.getVideoTracks()[0] if (videoTrack) { - await window.saveMediaStreamTrack(videoTrack, 'send') + await saveVideoTrack(videoTrack, 'send') } } diff --git a/scripts/peer-connection.js b/scripts/peer-connection.js index 92c60a3..465e273 100644 --- a/scripts/peer-connection.js +++ b/scripts/peer-connection.js @@ -1,4 +1,4 @@ -/* global log, PeerConnections, handleTransceiverForInsertableStreams, handleTransceiverForPlayoutDelayHint, videoEndToEndDelayStats */ +/* global log, PeerConnections, handleTransceiverForInsertableStreams, handleTransceiverForPlayoutDelayHint, videoEndToEndDelayStats, saveVideoTrack, saveAudioTrack */ const timestampInsertableStreams = !!window.PARAMS?.timestampInsertableStreams @@ -131,10 +131,17 @@ window.RTCPeerConnection = function (conf, options) { } if ( - window.PARAMS?.saveMediaStream && - window.WEBRTC_STRESS_TEST_INDEX <= window.PARAMS?.saveMediaStream + window.PARAMS?.saveVideoTrack && + window.WEBRTC_STRESS_TEST_INDEX <= window.PARAMS?.saveVideoTrack + 1 ) { - await window.saveMediaStreamTrack(receiver.track, 'recv') + await saveVideoTrack(receiver.track, 'recv') + } + } else if (receiver.track.kind === 'audio') { + if ( + window.PARAMS?.saveAudioTrack && + window.WEBRTC_STRESS_TEST_INDEX <= window.PARAMS?.saveAudioTrack + 1 + ) { + await saveAudioTrack(receiver.track, 'recv') } } } diff --git a/scripts/save-tracks.js b/scripts/save-tracks.js new file mode 100644 index 0000000..79e0bdf --- /dev/null +++ b/scripts/save-tracks.js @@ -0,0 +1,107 @@ +/* global log, streamWriter */ + +/** + * Save the video track to disk. + * @param {MediaStreamTrack} videoTrack + */ +window.saveVideoTrack = async (videoTrack, sendrecv, quality = 0.75) => { + const width = window.VIDEO_WIDTH + const height = window.VIDEO_HEIGHT + const frameRate = window.VIDEO_FRAMERATE + const fname = `${window.getParticipantName().split('_')[0]}-${sendrecv}_${ + videoTrack.id + }.ivf` + log(`saveVideoTrack ${fname} ${width}x${height} ${frameRate}fps`) + const writer = await streamWriter(fname, width, height, frameRate, 'MJPG') + + const canvas = new OffscreenCanvas(width, height) + const ctx = canvas.getContext('2d') + let startTimestamp = -1 + const writableStream = new window.WritableStream( + { + async write(videoFrame) { + const { timestamp, codedWidth, codedHeight } = videoFrame + if (!codedWidth || !codedHeight) { + return + } + const bitmap = await createImageBitmap(videoFrame) + try { + ctx.drawImage(bitmap, 0, 0, width, height) + const blob = await canvas.convertToBlob({ + type: 'image/jpeg', + quality, + }) + const data = await blob.arrayBuffer() + if (startTimestamp < 0) { + startTimestamp = timestamp + } + const pts = Math.round( + (frameRate * (timestamp - startTimestamp)) / 1000000, + ) + /* log( + `writer ${data.byteLength} bytes timestamp=${ + videoFrame.timestamp / 1000000 + } pts=${pts}`, + ) */ + writer.write(data, pts) + } catch (err) { + log(`saveVideoTrack error: ${err.message}`) + } + videoFrame.close() + bitmap.close() + }, + close() { + writer.close() + }, + abort(err) { + log('saveVideoTrack error:', err) + }, + }, + new CountQueuingStrategy({ highWaterMark: frameRate * 2 }), + ) + + const trackProcessor = new window.MediaStreamTrackProcessor({ + track: videoTrack, + }) + trackProcessor.readable.pipeTo(writableStream) +} + +/** + * Save the audio track to disk. + * @param {MediaStreamTrack} audioTrack + */ +window.saveAudioTrack = async (audioTrack, sendrecv) => { + const fname = `${window.getParticipantName().split('_')[0]}-${sendrecv}_${ + audioTrack.id + }.f32le.raw` + log(`saveAudioTrack ${fname}`) + const writer = await streamWriter(fname) + + const writableStream = new window.WritableStream( + { + async write(frame) { + const { numberOfFrames } = frame + try { + const data = new Float32Array(numberOfFrames) + frame.copyTo(data, { planeIndex: 0 }) + writer.write(data) + } catch (err) { + log(`saveAudioTrack error: ${err.message}`) + } + frame.close() + }, + close() { + writer.close() + }, + abort(err) { + log('saveAudioTrack error:', err) + }, + }, + new CountQueuingStrategy({ highWaterMark: 100 }), + ) + + const trackProcessor = new window.MediaStreamTrackProcessor({ + track: audioTrack, + }) + trackProcessor.readable.pipeTo(writableStream) +} diff --git a/src/session.ts b/src/session.ts index 3c17f89..b972f84 100644 --- a/src/session.ts +++ b/src/session.ts @@ -8,7 +8,6 @@ import { LoremIpsum } from 'lorem-ipsum' import NodeCache from 'node-cache' import os from 'os' import path from 'path' -import * as vanillaPuppeteer from 'puppeteer' import puppeteer, { Browser, BrowserContext, @@ -18,15 +17,10 @@ import puppeteer, { Metrics, Page, } from 'puppeteer-core' -import { addExtra } from 'puppeteer-extra' -import StealthPlugin from 'puppeteer-extra-plugin-stealth' import type { Interception } from 'puppeteer-intercept-and-modify-requests' import { RequestInterceptionManager } from 'puppeteer-intercept-and-modify-requests' import { gunzipSync } from 'zlib' -const puppeteerExtra = addExtra(vanillaPuppeteer) -puppeteerExtra.use(StealthPlugin()) - // For nexe bundler. require('puppeteer-extra-plugin-stealth/evasions/chrome.app') require('puppeteer-extra-plugin-stealth/evasions/chrome.csi') @@ -690,7 +684,7 @@ export class Session extends EventEmitter { } // Create process wrapper. - if (this.throttleIndex > -1) { + if (this.throttleIndex > -1 && os.platform() === 'linux') { const mark = this.throttleIndex + 1 const executableWrapperPath = `/tmp/webrtcperf-launcher-${mark}` const group = `webrtcperf${mark}` @@ -737,10 +731,7 @@ exec sg ${group} -c /tmp/webrtcperf-launcher-${mark}-browser`, try { // log.debug('defaultArgs:', puppeteer.defaultArgs()); - this.browser = (await (process.env.USE_PUPPETEER_EXTRA === 'true' - ? puppeteerExtra - : puppeteer - ).launch({ + this.browser = (await puppeteer.launch({ headless: this.display ? false : 'new', executablePath, handleSIGINT: false, @@ -966,6 +957,7 @@ window.SERVER_USE_HTTPS = ${this.serverUseHttps}; 'scripts/end-to-end-stats.js', 'scripts/playout-delay-hint.js', 'scripts/page-stats.js', + 'scripts/save-tracks.js', ]) { const filePath = resolvePackagePath(name) log.debug(`loading ${name} script from: ${filePath}`) diff --git a/src/throttle.ts b/src/throttle.ts index 432a051..de1ae7c 100644 --- a/src/throttle.ts +++ b/src/throttle.ts @@ -1,4 +1,5 @@ import JSON5 from 'json5' +import os from 'os' import { logger, runShellCommand } from './utils' @@ -235,6 +236,7 @@ sudo -n tc filter add dev ${device} \ * @param config A JSON5 configuration parsed as {@link ThrottleConfig}. */ export async function startThrottle(config: string): Promise { + if (os.platform() !== 'linux') return try { throttleConfig = JSON5.parse(config) as ThrottleConfig[] log.info('Starting throttle with config:', throttleConfig) @@ -251,6 +253,7 @@ export async function startThrottle(config: string): Promise { * Stops the network throttle. */ export async function stopThrottle(): Promise { + if (os.platform() !== 'linux') return try { log.info('Stopping throttle') await cleanup() diff --git a/yarn.lock b/yarn.lock index 78a7e39..5b6b39e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6613,15 +6613,6 @@ puppeteer-extra-plugin@^3.2.3: debug "^4.1.1" merge-deep "^3.0.1" -puppeteer-extra@^3.3.6: - version "3.3.6" - resolved "https://registry.yarnpkg.com/puppeteer-extra/-/puppeteer-extra-3.3.6.tgz#fc16ff396aae52664842da9a557ea8fa51eaa8b7" - integrity sha512-rsLBE/6mMxAjlLd06LuGacrukP2bqbzKCLzV1vrhHFavqQE/taQ2UXv3H5P0Ls7nsrASa+6x3bDbXHpqMwq+7A== - dependencies: - "@types/debug" "^4.1.0" - debug "^4.1.1" - deepmerge "^4.2.2" - puppeteer-intercept-and-modify-requests@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/puppeteer-intercept-and-modify-requests/-/puppeteer-intercept-and-modify-requests-1.3.0.tgz#ed7d60bfd7016fae962e6be01e4a1586664d3dfa"