feat(chromium): use bundled ffmpeg instead of npm deps (#3771)
Fixes #3680
This commit is contained in:
parent
f09145e504
commit
bbe2233f08
|
|
@ -3,7 +3,7 @@
|
||||||
"browsers": [
|
"browsers": [
|
||||||
{
|
{
|
||||||
"name": "chromium",
|
"name": "chromium",
|
||||||
"revision": "792639",
|
"revision": "792639.1",
|
||||||
"download": true
|
"download": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
56
package-lock.json
generated
56
package-lock.json
generated
|
|
@ -1081,62 +1081,6 @@
|
||||||
"sumchecker": "^3.0.1"
|
"sumchecker": "^3.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@ffmpeg-installer/darwin-x64": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/darwin-x64/-/darwin-x64-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-Z4EyG3cIFjdhlY8wI9aLUXuH8nVt7E9SlMVZtWvSPnm2sm37/yC2CwjUzyCQbJbySnef1tQwGG2Sx+uWhd9IAw==",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@ffmpeg-installer/ffmpeg": {
|
|
||||||
"version": "1.0.20",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/ffmpeg/-/ffmpeg-1.0.20.tgz",
|
|
||||||
"integrity": "sha512-wbgd//6OdwbFXYgV68ZyKrIcozEQpUKlvV66XHaqO2h3sFbX0jYLzx62Q0v8UcFWN21LoxT98NU2P+K0OWsKNA==",
|
|
||||||
"requires": {
|
|
||||||
"@ffmpeg-installer/darwin-x64": "4.1.0",
|
|
||||||
"@ffmpeg-installer/linux-arm": "4.1.3",
|
|
||||||
"@ffmpeg-installer/linux-arm64": "4.1.4",
|
|
||||||
"@ffmpeg-installer/linux-ia32": "4.1.0",
|
|
||||||
"@ffmpeg-installer/linux-x64": "4.1.0",
|
|
||||||
"@ffmpeg-installer/win32-ia32": "4.1.0",
|
|
||||||
"@ffmpeg-installer/win32-x64": "4.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@ffmpeg-installer/linux-arm": {
|
|
||||||
"version": "4.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-arm/-/linux-arm-4.1.3.tgz",
|
|
||||||
"integrity": "sha512-NDf5V6l8AfzZ8WzUGZ5mV8O/xMzRag2ETR6+TlGIsMHp81agx51cqpPItXPib/nAZYmo55Bl2L6/WOMI3A5YRg==",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@ffmpeg-installer/linux-arm64": {
|
|
||||||
"version": "4.1.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-arm64/-/linux-arm64-4.1.4.tgz",
|
|
||||||
"integrity": "sha512-dljEqAOD0oIM6O6DxBW9US/FkvqvQwgJ2lGHOwHDDwu/pX8+V0YsDL1xqHbj1DMX/+nP9rxw7G7gcUvGspSoKg==",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@ffmpeg-installer/linux-ia32": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-ia32/-/linux-ia32-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-0LWyFQnPf+Ij9GQGD034hS6A90URNu9HCtQ5cTqo5MxOEc7Rd8gLXrJvn++UmxhU0J5RyRE9KRYstdCVUjkNOQ==",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@ffmpeg-installer/linux-x64": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-x64/-/linux-x64-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-Y5BWhGLU/WpQjOArNIgXD3z5mxxdV8c41C+U15nsE5yF8tVcdCGet5zPs5Zy3Ta6bU7haGpIzryutqCGQA/W8A==",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@ffmpeg-installer/win32-ia32": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/win32-ia32/-/win32-ia32-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-FV2D7RlaZv/lrtdhaQ4oETwoFUsUjlUiasiZLDxhEUPdNDWcH1OU9K1xTvqz+OXLdsmYelUDuBS/zkMOTtlUAw==",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@ffmpeg-installer/win32-x64": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/win32-x64/-/win32-x64-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-Drt5u2vzDnIONf4ZEkKtFlbvwj6rI3kxw1Ck9fpudmtgaZIHD4ucsWB2lCZBXRxJgXR+2IMSti+4rtM4C4rXgg==",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@jest/types": {
|
"@jest/types": {
|
||||||
"version": "26.3.0",
|
"version": "26.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ffmpeg-installer/ffmpeg": "^1.0.20",
|
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
"extract-zip": "^2.0.1",
|
"extract-zip": "^2.0.1",
|
||||||
"https-proxy-agent": "^5.0.0",
|
"https-proxy-agent": "^5.0.0",
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import { rewriteErrorMessage } from '../../utils/stackTrace';
|
||||||
import { BrowserTypeBase } from '../browserType';
|
import { BrowserTypeBase } from '../browserType';
|
||||||
import { ConnectionTransport, ProtocolRequest } from '../transport';
|
import { ConnectionTransport, ProtocolRequest } from '../transport';
|
||||||
import type { BrowserDescriptor } from '../../utils/browserPaths';
|
import type { BrowserDescriptor } from '../../utils/browserPaths';
|
||||||
|
import { browserDirectory, browsersPath, ffmpegPath} from '../../utils/browserPaths';
|
||||||
import { CRDevTools } from './crDevTools';
|
import { CRDevTools } from './crDevTools';
|
||||||
import { BrowserOptions } from '../browser';
|
import { BrowserOptions } from '../browser';
|
||||||
import * as types from '../types';
|
import * as types from '../types';
|
||||||
|
|
@ -32,6 +33,7 @@ import { isDebugMode, getFromENV } from '../../utils/utils';
|
||||||
export class Chromium extends BrowserTypeBase {
|
export class Chromium extends BrowserTypeBase {
|
||||||
private _devtools: CRDevTools | undefined;
|
private _devtools: CRDevTools | undefined;
|
||||||
private _debugPort: number | undefined;
|
private _debugPort: number | undefined;
|
||||||
|
private _ffmpegPath: string;
|
||||||
|
|
||||||
constructor(packagePath: string, browser: BrowserDescriptor) {
|
constructor(packagePath: string, browser: BrowserDescriptor) {
|
||||||
const debugPortStr = getFromENV('PLAYWRIGHT_CHROMIUM_DEBUG_PORT');
|
const debugPortStr = getFromENV('PLAYWRIGHT_CHROMIUM_DEBUG_PORT');
|
||||||
|
|
@ -43,6 +45,8 @@ export class Chromium extends BrowserTypeBase {
|
||||||
|
|
||||||
super(packagePath, browser, debugPort ? { webSocketRegex: /^DevTools listening on (ws:\/\/.*)$/, stream: 'stderr' } : null);
|
super(packagePath, browser, debugPort ? { webSocketRegex: /^DevTools listening on (ws:\/\/.*)$/, stream: 'stderr' } : null);
|
||||||
this._debugPort = debugPort;
|
this._debugPort = debugPort;
|
||||||
|
const browserDir = browserDirectory(browsersPath(packagePath), browser);
|
||||||
|
this._ffmpegPath = ffmpegPath(browserDir, browser);
|
||||||
if (isDebugMode())
|
if (isDebugMode())
|
||||||
this._devtools = this._createDevTools();
|
this._devtools = this._createDevTools();
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +61,7 @@ export class Chromium extends BrowserTypeBase {
|
||||||
devtools = this._createDevTools();
|
devtools = this._createDevTools();
|
||||||
await (options as any).__testHookForDevTools(devtools);
|
await (options as any).__testHookForDevTools(devtools);
|
||||||
}
|
}
|
||||||
return CRBrowser.connect(transport, options, devtools);
|
return CRBrowser.connect(transport, options, this._ffmpegPath, devtools);
|
||||||
}
|
}
|
||||||
|
|
||||||
_rewriteStartupError(error: Error): Error {
|
_rewriteStartupError(error: Error): Error {
|
||||||
|
|
|
||||||
|
|
@ -44,10 +44,11 @@ export class CRBrowser extends Browser {
|
||||||
private _tracingRecording = false;
|
private _tracingRecording = false;
|
||||||
private _tracingPath: string | null = '';
|
private _tracingPath: string | null = '';
|
||||||
private _tracingClient: CRSession | undefined;
|
private _tracingClient: CRSession | undefined;
|
||||||
|
private _ffmpegPath: string;
|
||||||
|
|
||||||
static async connect(transport: ConnectionTransport, options: BrowserOptions, devtools?: CRDevTools): Promise<CRBrowser> {
|
static async connect(transport: ConnectionTransport, options: BrowserOptions, ffmpegPath: string, devtools?: CRDevTools): Promise<CRBrowser> {
|
||||||
const connection = new CRConnection(transport);
|
const connection = new CRConnection(transport);
|
||||||
const browser = new CRBrowser(connection, options);
|
const browser = new CRBrowser(connection, options, ffmpegPath);
|
||||||
browser._devtools = devtools;
|
browser._devtools = devtools;
|
||||||
const session = connection.rootSession;
|
const session = connection.rootSession;
|
||||||
const version = await session.send('Browser.getVersion');
|
const version = await session.send('Browser.getVersion');
|
||||||
|
|
@ -88,9 +89,10 @@ export class CRBrowser extends Browser {
|
||||||
return browser;
|
return browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(connection: CRConnection, options: BrowserOptions) {
|
constructor(connection: CRConnection, options: BrowserOptions, ffmpegPath: string) {
|
||||||
super(options);
|
super(options);
|
||||||
this._connection = connection;
|
this._connection = connection;
|
||||||
|
this._ffmpegPath = ffmpegPath;
|
||||||
this._session = this._connection.rootSession;
|
this._session = this._connection.rootSession;
|
||||||
this._connection.on(ConnectionEvents.Disconnected, () => this._didClose());
|
this._connection.on(ConnectionEvents.Disconnected, () => this._didClose());
|
||||||
this._session.on('Target.attachedToTarget', this._onAttachedToTarget.bind(this));
|
this._session.on('Target.attachedToTarget', this._onAttachedToTarget.bind(this));
|
||||||
|
|
@ -146,7 +148,7 @@ export class CRBrowser extends Browser {
|
||||||
assert(!this._serviceWorkers.has(targetInfo.targetId), 'Duplicate target ' + targetInfo.targetId);
|
assert(!this._serviceWorkers.has(targetInfo.targetId), 'Duplicate target ' + targetInfo.targetId);
|
||||||
|
|
||||||
if (targetInfo.type === 'background_page') {
|
if (targetInfo.type === 'background_page') {
|
||||||
const backgroundPage = new CRPage(session, targetInfo.targetId, context, null, false);
|
const backgroundPage = new CRPage(session, targetInfo.targetId, context, null, false, this._ffmpegPath);
|
||||||
this._backgroundPages.set(targetInfo.targetId, backgroundPage);
|
this._backgroundPages.set(targetInfo.targetId, backgroundPage);
|
||||||
backgroundPage.pageOrError().then(() => {
|
backgroundPage.pageOrError().then(() => {
|
||||||
context!.emit(CRBrowserContext.CREvents.BackgroundPage, backgroundPage._page);
|
context!.emit(CRBrowserContext.CREvents.BackgroundPage, backgroundPage._page);
|
||||||
|
|
@ -156,7 +158,7 @@ export class CRBrowser extends Browser {
|
||||||
|
|
||||||
if (targetInfo.type === 'page') {
|
if (targetInfo.type === 'page') {
|
||||||
const opener = targetInfo.openerId ? this._crPages.get(targetInfo.openerId) || null : null;
|
const opener = targetInfo.openerId ? this._crPages.get(targetInfo.openerId) || null : null;
|
||||||
const crPage = new CRPage(session, targetInfo.targetId, context, opener, true);
|
const crPage = new CRPage(session, targetInfo.targetId, context, opener, true, this._ffmpegPath);
|
||||||
this._crPages.set(targetInfo.targetId, crPage);
|
this._crPages.set(targetInfo.targetId, crPage);
|
||||||
crPage.pageOrError().then(pageOrError => {
|
crPage.pageOrError().then(pageOrError => {
|
||||||
const page = crPage._page;
|
const page = crPage._page;
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ export class CRPage implements PageDelegate {
|
||||||
readonly _browserContext: CRBrowserContext;
|
readonly _browserContext: CRBrowserContext;
|
||||||
private readonly _pagePromise: Promise<Page | Error>;
|
private readonly _pagePromise: Promise<Page | Error>;
|
||||||
_initializedPage: Page | null = null;
|
_initializedPage: Page | null = null;
|
||||||
|
readonly _ffmpegPath: string;
|
||||||
|
|
||||||
// Holds window features for the next popup being opened via window.open,
|
// Holds window features for the next popup being opened via window.open,
|
||||||
// until the popup target arrives. This could be racy if two oopifs
|
// until the popup target arrives. This could be racy if two oopifs
|
||||||
|
|
@ -64,9 +65,10 @@ export class CRPage implements PageDelegate {
|
||||||
// of new popup targets.
|
// of new popup targets.
|
||||||
readonly _nextWindowOpenPopupFeatures: string[][] = [];
|
readonly _nextWindowOpenPopupFeatures: string[][] = [];
|
||||||
|
|
||||||
constructor(client: CRSession, targetId: string, browserContext: CRBrowserContext, opener: CRPage | null, hasUIWindow: boolean) {
|
constructor(client: CRSession, targetId: string, browserContext: CRBrowserContext, opener: CRPage | null, hasUIWindow: boolean, ffmpegPath: string) {
|
||||||
this._targetId = targetId;
|
this._targetId = targetId;
|
||||||
this._opener = opener;
|
this._opener = opener;
|
||||||
|
this._ffmpegPath = ffmpegPath;
|
||||||
this.rawKeyboard = new RawKeyboardImpl(client, browserContext._browser._isMac);
|
this.rawKeyboard = new RawKeyboardImpl(client, browserContext._browser._isMac);
|
||||||
this.rawMouse = new RawMouseImpl(client);
|
this.rawMouse = new RawMouseImpl(client);
|
||||||
this._pdf = new CRPDF(client);
|
this._pdf = new CRPDF(client);
|
||||||
|
|
@ -750,7 +752,7 @@ class FrameSession {
|
||||||
async _startScreencast(screencastId: string, options: types.PageScreencastOptions): Promise<void> {
|
async _startScreencast(screencastId: string, options: types.PageScreencastOptions): Promise<void> {
|
||||||
if (this._screencastState !== 'stopped')
|
if (this._screencastState !== 'stopped')
|
||||||
throw new Error('Already started');
|
throw new Error('Already started');
|
||||||
const videoRecorder = await VideoRecorder.launch(options);
|
const videoRecorder = await VideoRecorder.launch(this._crPage._ffmpegPath, options);
|
||||||
this._screencastState = 'starting';
|
this._screencastState = 'starting';
|
||||||
try {
|
try {
|
||||||
await this._client.send('Page.startScreencast', {
|
await this._client.send('Page.startScreencast', {
|
||||||
|
|
|
||||||
|
|
@ -30,20 +30,22 @@ export class VideoRecorder {
|
||||||
private _lastFrameBuffer: Buffer | null = null;
|
private _lastFrameBuffer: Buffer | null = null;
|
||||||
private _lastWriteTimestamp: number = 0;
|
private _lastWriteTimestamp: number = 0;
|
||||||
private readonly _progress: Progress;
|
private readonly _progress: Progress;
|
||||||
|
private readonly _ffmpegPath: string;
|
||||||
|
|
||||||
static async launch(options: types.PageScreencastOptions): Promise<VideoRecorder> {
|
static async launch(ffmpegPath: string, options: types.PageScreencastOptions): Promise<VideoRecorder> {
|
||||||
if (!options.outputFile.endsWith('.webm'))
|
if (!options.outputFile.endsWith('.webm'))
|
||||||
throw new Error('File must have .webm extension');
|
throw new Error('File must have .webm extension');
|
||||||
|
|
||||||
return await runAbortableTask(async progress => {
|
return await runAbortableTask(async progress => {
|
||||||
const recorder = new VideoRecorder(progress);
|
const recorder = new VideoRecorder(ffmpegPath, progress);
|
||||||
await recorder._launch(options);
|
await recorder._launch(options);
|
||||||
return recorder;
|
return recorder;
|
||||||
}, 0, 'browser');
|
}, 0, 'browser');
|
||||||
}
|
}
|
||||||
|
|
||||||
private constructor(progress: Progress) {
|
private constructor(ffmpegPath: string, progress: Progress) {
|
||||||
this._progress = progress;
|
this._progress = progress;
|
||||||
|
this._ffmpegPath = ffmpegPath;
|
||||||
this._lastWritePromise = Promise.resolve();
|
this._lastWritePromise = Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,10 +56,8 @@ export class VideoRecorder {
|
||||||
const args = `-loglevel error -f image2pipe -c:v mjpeg -i - -y -an -r ${fps} -c:v vp8 -vf pad=${w}:${h}:0:0:gray,crop=${w}:${h}:0:0`.split(' ');
|
const args = `-loglevel error -f image2pipe -c:v mjpeg -i - -y -an -r ${fps} -c:v vp8 -vf pad=${w}:${h}:0:0:gray,crop=${w}:${h}:0:0`.split(' ');
|
||||||
args.push(options.outputFile);
|
args.push(options.outputFile);
|
||||||
const progress = this._progress;
|
const progress = this._progress;
|
||||||
// Use ffmpeg provided by the host system.
|
|
||||||
const ffmpegPath = process.platform === 'linux' ? 'ffmpeg' : require('@ffmpeg-installer/ffmpeg').path;
|
|
||||||
const { launchedProcess, gracefullyClose } = await launchProcess({
|
const { launchedProcess, gracefullyClose } = await launchProcess({
|
||||||
executablePath: ffmpegPath,
|
executablePath: this._ffmpegPath,
|
||||||
args,
|
args,
|
||||||
pipeStdin: true,
|
pipeStdin: true,
|
||||||
progress,
|
progress,
|
||||||
|
|
@ -126,4 +126,4 @@ export class VideoRecorder {
|
||||||
private _isRunning(): boolean {
|
private _isRunning(): boolean {
|
||||||
return !!this._gracefullyClose;
|
return !!this._gracefullyClose;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ export class Electron {
|
||||||
close: gracefullyClose,
|
close: gracefullyClose,
|
||||||
kill
|
kill
|
||||||
};
|
};
|
||||||
const browser = await CRBrowser.connect(chromeTransport, { name: 'electron', headful: true, persistent: { noDefaultViewport: true }, browserProcess });
|
const browser = await CRBrowser.connect(chromeTransport, { name: 'electron', headful: true, persistent: { noDefaultViewport: true }, browserProcess }, '');
|
||||||
app = new ElectronApplication(browser, nodeConnection);
|
app = new ElectronApplication(browser, nodeConnection);
|
||||||
await app._init();
|
await app._init();
|
||||||
return app;
|
return app;
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,18 @@ export function windowsExeAndDllDirectories(browserPath: string, browser: Browse
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ffmpegPath(browserPath: string, browser: BrowserDescriptor): string {
|
||||||
|
if (browser.name !== 'chromium')
|
||||||
|
return '';
|
||||||
|
if (os.platform() === 'linux')
|
||||||
|
return 'ffmpeg';
|
||||||
|
if (os.platform() === 'win32')
|
||||||
|
return path.join(browserPath, 'chrome-win', 'ffmpeg.exe');
|
||||||
|
if (os.platform() === 'darwin')
|
||||||
|
return path.join(browserPath, 'chrome-mac', 'ffmpeg');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
export function executablePath(browserPath: string, browser: BrowserDescriptor): string | undefined {
|
export function executablePath(browserPath: string, browser: BrowserDescriptor): string | undefined {
|
||||||
let tokens: string[] | undefined;
|
let tokens: string[] | undefined;
|
||||||
if (browser.name === 'chromium') {
|
if (browser.name === 'chromium') {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue