puppetcam
puppetcam copied to clipboard
Exported video are different to original video dimension
I try to record a video with custom dimension 720x1280 or 1280x720. I expected the output to have the same dimension with the original video but the recorded video add some black space on top and bottom and (width, height) are different then original dimension.
Tell us about your environment: Puppeteer version: 2.1.1 Platform: docker ubuntu
Landscape example: Dimension used in export.js and background.js (width: 1280, height: 720) Original video:
- http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4
Exported record video:
- https://streamable.com/tl333
If you see the last frame from exported video are the real size from original video size.
Portrait example Dimension used in export.js and background.js (width: 720, height: 1280) Original video:
- https://www.loom.com/share/57555352e3d04139a49c6ce631ff09ce
Exported record video:
- https://streamable.com/9fgrb
Used code for bunny example, in second example just dimension are different. export.js
const express = require ('express');
const puppeteer = require('puppeteer');
const Xvfb = require('xvfb');
var xvfb = new Xvfb({silent: false});
const chromeExtensionPath = `${__dirname}/chrome-extension`;
// export DISPLAY=:99 && /etc/init.d/xvfb start
async function main() {
const PARTIAL_CONTENT_STATUS = 206;
return new Promise(async (resolve, reject) => {
try {
let browser = await puppeteer.launch({
headless: false,
devtools: false,
executablePath: '/usr/bin/google-chrome',
ignoreDefaultArgs: ['--mute-audio'],
defaultViewport: {
width: 1280,
height: 720,
},
args: [
'--enable-usermedia-screen-capturing',
'--allow-http-screen-capture',
'--auto-select-desktop-capture-source=puppetcam',
'--load-extension=' + chromeExtensionPath,
'--disable-extensions-except=' + chromeExtensionPath,
'--disable-infobars',
'--no-sandbox',
'--hide-scrollbars',
'--window-size=1280,720',
],
});
const page = await browser.newPage();
page.setViewport({
width: 1280,
height: 720,
})
page.on('console', msg => console.log('PAGE LOG:', msg.text()));
page.on('error', (e) => {
reject(e);
browser.close().catch(console.error);
});
page.on('requestfailed', (req) => {
const response = req.response();
if (response && response.status() === PARTIAL_CONTENT_STATUS) {
console.log([
'Ignoring failed request for ',
req.url(),
' with status 206 Partial Content. Range: ',
response && response.headers()['content-range'],
].join(''));
} else {
console.error('requestfailed', req.url(), req.failure().errorText);
}
});
const response = await page.goto('http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4', {waitUntil: 'networkidle2'})
await page.waitFor(17000)
xvfb.startSync()
await page.evaluate(filename=>{
window.postMessage({type: 'SET_EXPORT_PATH', filename: filename}, '*')
window.postMessage({type: 'REC_STOP'}, '*')
}, 'bunny.webm')
// Wait for download of webm to complete
await page.waitForSelector('html.downloadComplete', {timeout: 0})
xvfb.stopSync()
if (response.status() < 200 || response.status() >= 400) {
throw new Error(`Preview server response error: ${await response.text()}`);
}
browser.close().catch(console.error);
} catch (e) {
console.error(e);
throw e;
}
});
}
const app = express();
// cors
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
// healthCheck @TODO: make it better
app.get('/health', (req, res) => {
res.status(200).send({ code: 200, body: 'OK' });
});
app.listen(4000, () => {
console.log(`Started to listen on port 4000`);
});
app.get('/export', () => {
console.log('here');
main()
});
background.js:
/* global chrome, MediaRecorder, FileReader */
console.log(`Background script loading`);
let recorder = null;
let filename = null;
chrome.runtime.onConnect.addListener(port => {
port.onMessage.addListener(msg => {
console.log(msg);
switch (msg.type) {
case 'SET_EXPORT_PATH':
console.log(`set export path`);
filename = msg.filename;
break;
case 'REC_STOP':
console.log(`REC STOP`);
recorder.stop();
break;
case 'REC_CLIENT_PLAY':
if (recorder) {
return;
}
const tab = port.sender.tab;
tab.url = msg.data.url;
const size = {width: 1280, height: 720};
chrome.desktopCapture.chooseDesktopMedia(['tab', 'audio'], streamId => {
// Get the stream
navigator.webkitGetUserMedia(
{
audio: {
mandatory: {
chromeMediaSource: 'system'
}
},
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: streamId,
minWidth: size.width,
maxWidth: size.width,
minHeight: size.height,
maxHeight: size.height,
minFrameRate: 60,
},
},
},
stream => {
var chunks = [];
recorder = new MediaRecorder(stream, {
videoBitsPerSecond: 2500000,
ignoreMutedMedia: true,
mimeType: 'video/webm',
});
recorder.ondataavailable = function(event) {
if (event.data.size > 0) {
chunks.push(event.data);
}
};
recorder.onstop = function() {
var superBuffer = new Blob(chunks, {
type: 'video/webm',
});
var url = URL.createObjectURL(superBuffer);
// var a = document.createElement('a');
// document.body.appendChild(a);
// a.style = 'display: none';
// a.href = url;
// a.download = 'test.webm';
// a.click();
chrome.downloads.download(
{
url: url,
filename: filename,
},
() => {
console.log(arguments);
},
);
};
recorder.start();
},
error => console.log('Unable to get user media', error),
);
});
break;
default:
console.log('Unrecognized message', msg);
}
});
chrome.downloads.onChanged.addListener(function(delta) {
if (!delta.state || delta.state.current != 'complete') {
return;
}
try {
port.postMessage({downloadComplete: true});
} catch (e) {}
});
});
Dockerfile:
FROM ubuntu
ARG DEBIAN_FRONTEND=noninteractive
ENV TZ=Europe/Bucharest
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apt-get update
RUN apt-get install -y curl xvfb x11vnc x11-xkb-utils xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic x11-apps nodejs npm tightvncserver xfce4 xfce4-goodies apt-utils gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget x11vnc x11-xkb-utils xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic x11-apps xvfb apt-utils openvpn fping nano libgtk-3.0 libatk-bridge2.0-0
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
&& apt-get update \
&& apt-get install -y google-chrome-stable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf \
--no-install-recommends \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get purge --auto-remove -y curl \
&& rm -rf /src/*.deb
## Sound options for RHEL7 OpenClient
RUN snd_opts="--device /dev/snd \
-e PULSE_SERVER=unix:${XDG_RUNTIME_DIR}/pulse/native \
-v ${XDG_RUNTIME_DIR}/pulse/native:${XDG_RUNTIME_DIR}/pulse/native \
--group-add $(getent group audio | cut -d: -f3)"
RUN groupmod -g 92 audio
ADD xvfb_init /etc/init.d/xvfb
RUN chmod a+x /etc/init.d/xvfb
ADD xvfb_daemon_run /usr/bin/xvfb-daemon-run
RUN chmod a+x /usr/bin/xvfb-daemon-run
WORKDIR /cac
ADD package.json .
RUN npm install
COPY ./app ./app
ENV DISPLAY :99
try to use page.setViewport after const response = await page.goto
You could try setting the screen size during initialization of xvfb and play the video in full screen mode to make sure that entire viewport is recorded.
new Xvfb({silent: true, xvfb_args: ["-screen", "0", `${width}x${height}x24`, "-ac"],});