Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

browser(firefox): support screencast frame size and scale configuration #2847

Merged
merged 1 commit into from
Jul 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions browser_patches/firefox/BUILD_NUMBER
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
1122
Changed: yurys@chromium.org Thu Jul 2 14:18:12 PDT 2020
1123
Changed: yurys@chromium.org Mon Jul 6 11:13:23 PDT 2020
7 changes: 6 additions & 1 deletion browser_patches/firefox/juggler/protocol/PageHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,9 +287,14 @@ class PageHandler {
}

startVideoRecording({file, width, height, scale}) {
if (width < 10 || width > 10000 || height < 10 || height > 10000)
throw new Error("Invalid size");
if (scale && (scale <= 0 || scale > 1))
throw new Error("Unsupported scale");

const screencast = Cc['@mozilla.org/juggler/screencast;1'].getService(Ci.nsIScreencastService);
const docShell = this._pageTarget._gBrowser.ownerGlobal.docShell;
this._videoSessionId = screencast.startVideoRecording(docShell, file);
this._videoSessionId = screencast.startVideoRecording(docShell, file, width, height, scale || 0);
}

stopVideoRecording() {
Expand Down
54 changes: 42 additions & 12 deletions browser_patches/firefox/juggler/screencast/ScreencastEncoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

#include "ScreencastEncoder.h"

#include <algorithm>
#include <libyuv.h>
#include <vpx/vp8.h>
#include <vpx/vp8cx.h>
Expand All @@ -47,7 +48,8 @@ const int kMacroBlockSize = 16;

void createImage(unsigned int width, unsigned int height,
std::unique_ptr<vpx_image_t>& out_image,
std::unique_ptr<uint8_t[]>& out_image_buffer) {
std::unique_ptr<uint8_t[]>& out_image_buffer,
int& out_buffer_size) {
std::unique_ptr<vpx_image_t> image(new vpx_image_t());
memset(image.get(), 0, sizeof(vpx_image_t));

Expand Down Expand Up @@ -78,11 +80,11 @@ void createImage(unsigned int width, unsigned int height,
const int uv_rows = y_rows >> image->y_chroma_shift;

// Allocate a YUV buffer large enough for the aligned data & padding.
const int buffer_size = y_stride * y_rows + 2*uv_stride * uv_rows;
std::unique_ptr<uint8_t[]> image_buffer(new uint8_t[buffer_size]);
out_buffer_size = y_stride * y_rows + 2*uv_stride * uv_rows;
std::unique_ptr<uint8_t[]> image_buffer(new uint8_t[out_buffer_size]);

// Reset image value to 128 so we just need to fill in the y plane.
memset(image_buffer.get(), 128, buffer_size);
memset(image_buffer.get(), 128, out_buffer_size);

// Fill in the information for |image_|.
unsigned char* uchar_buffer =
Expand Down Expand Up @@ -180,13 +182,39 @@ class ScreencastEncoder::VPXFrame {
uint8_t* u_data = image->planes[1];
uint8_t* v_data = image->planes[2];

libyuv::I420Copy(src->DataY(), src->StrideY(),
src->DataU(), src->StrideU(),
src->DataV(), src->StrideV(),
y_data, y_stride,
u_data, uv_stride,
v_data, uv_stride,
image->w, image->h);
if (m_scale) {
int src_width = src->width();
double dst_width = src_width * m_scale.value();
if (dst_width > image->w) {
src_width *= image->w / dst_width;
dst_width = image->w;
}
int src_height = src->height();
double dst_height = src_height * m_scale.value();
if (dst_height > image->h) {
src_height *= image->h / dst_height;
dst_height = image->h;
}
libyuv::I420Scale(src->DataY(), src->StrideY(),
src->DataU(), src->StrideU(),
src->DataV(), src->StrideV(),
src_width, src_height,
y_data, y_stride,
u_data, uv_stride,
v_data, uv_stride,
dst_width, dst_height,
libyuv::kFilterBilinear);
} else {
int width = std::min<int>(image->w, src->width());
int height = std::min<int>(image->h, src->height());
libyuv::I420Copy(src->DataY(), src->StrideY(),
src->DataU(), src->StrideU(),
src->DataV(), src->StrideV(),
y_data, y_stride,
u_data, uv_stride,
v_data, uv_stride,
width, height);
}
}

private:
Expand All @@ -212,7 +240,7 @@ class ScreencastEncoder::VPXCodec {

ivf_write_file_header(m_file, &m_cfg, m_fourcc, 0);

createImage(cfg.g_w, cfg.g_h, m_image, m_imageBuffer);
createImage(cfg.g_w, cfg.g_h, m_image, m_imageBuffer, m_imageBufferSize);
}

~VPXCodec() {
Expand All @@ -223,6 +251,7 @@ class ScreencastEncoder::VPXCodec {
void encodeFrameAsync(std::unique_ptr<VPXFrame>&& frame)
{
m_encoderQueue->Dispatch(NS_NewRunnableFunction("VPXCodec::encodeFrameAsync", [this, frame = std::move(frame)] {
memset(m_imageBuffer.get(), 128, m_imageBufferSize);
frame->convertToVpxImage(m_image.get());
// TODO: figure out why passing duration to the codec results in much
// worse visual quality and makes video stutter.
Expand Down Expand Up @@ -292,6 +321,7 @@ class ScreencastEncoder::VPXCodec {
int m_frameCount { 0 };
int64_t m_pts { 0 };
std::unique_ptr<uint8_t[]> m_imageBuffer;
int m_imageBufferSize { 0 };
std::unique_ptr<vpx_image_t> m_image;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ interface nsIDocShell;
[scriptable, uuid(d8c4d9e0-9462-445e-9e43-68d3872ad1de)]
interface nsIScreencastService : nsISupports
{
long startVideoRecording(in nsIDocShell docShell, in ACString fileName);
long startVideoRecording(in nsIDocShell docShell, in ACString fileName, in uint32_t width, in uint32_t height, in double scale);
void stopVideoRecording(in long sessionId);
};
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ nsScreencastService::nsScreencastService() = default;
nsScreencastService::~nsScreencastService() {
}

nsresult nsScreencastService::StartVideoRecording(nsIDocShell* aDocShell, const nsACString& aFileName, int32_t* sessionId) {
nsresult nsScreencastService::StartVideoRecording(nsIDocShell* aDocShell, const nsACString& aFileName, uint32_t width, uint32_t height, double scale, int32_t* sessionId) {
MOZ_RELEASE_ASSERT(NS_IsMainThread(), "Screencast service must be started on the Main thread.");
*sessionId = -1;

Expand Down Expand Up @@ -121,7 +121,10 @@ nsresult nsScreencastService::StartVideoRecording(nsIDocShell* aDocShell, const
# endif
*sessionId = ++mLastSessionId;
nsCString error;
RefPtr<ScreencastEncoder> encoder = ScreencastEncoder::create(error, PromiseFlatCString(aFileName), 1280, 960, Nothing());
Maybe<double> maybeScale;
if (scale)
maybeScale = Some(scale);
RefPtr<ScreencastEncoder> encoder = ScreencastEncoder::create(error, PromiseFlatCString(aFileName), width, height, maybeScale);
if (!encoder) {
fprintf(stderr, "Failed to create ScreencastEncoder: %s\n", error.get());
return NS_ERROR_FAILURE;
Expand Down