browser(firefox): support screencast frame size and scale configuration (#2847)
This commit is contained in:
parent
ac2185a9d9
commit
fc18f2f38a
|
|
@ -1,2 +1,2 @@
|
||||||
1122
|
1123
|
||||||
Changed: yurys@chromium.org Thu Jul 2 14:18:12 PDT 2020
|
Changed: yurys@chromium.org Mon Jul 6 11:13:23 PDT 2020
|
||||||
|
|
|
||||||
|
|
@ -287,9 +287,14 @@ class PageHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
startVideoRecording({file, width, height, scale}) {
|
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 screencast = Cc['@mozilla.org/juggler/screencast;1'].getService(Ci.nsIScreencastService);
|
||||||
const docShell = this._pageTarget._gBrowser.ownerGlobal.docShell;
|
const docShell = this._pageTarget._gBrowser.ownerGlobal.docShell;
|
||||||
this._videoSessionId = screencast.startVideoRecording(docShell, file);
|
this._videoSessionId = screencast.startVideoRecording(docShell, file, width, height, scale || 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
stopVideoRecording() {
|
stopVideoRecording() {
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
|
|
||||||
#include "ScreencastEncoder.h"
|
#include "ScreencastEncoder.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <libyuv.h>
|
#include <libyuv.h>
|
||||||
#include <vpx/vp8.h>
|
#include <vpx/vp8.h>
|
||||||
#include <vpx/vp8cx.h>
|
#include <vpx/vp8cx.h>
|
||||||
|
|
@ -47,7 +48,8 @@ const int kMacroBlockSize = 16;
|
||||||
|
|
||||||
void createImage(unsigned int width, unsigned int height,
|
void createImage(unsigned int width, unsigned int height,
|
||||||
std::unique_ptr<vpx_image_t>& out_image,
|
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());
|
std::unique_ptr<vpx_image_t> image(new vpx_image_t());
|
||||||
memset(image.get(), 0, sizeof(vpx_image_t));
|
memset(image.get(), 0, sizeof(vpx_image_t));
|
||||||
|
|
||||||
|
|
@ -78,11 +80,11 @@ void createImage(unsigned int width, unsigned int height,
|
||||||
const int uv_rows = y_rows >> image->y_chroma_shift;
|
const int uv_rows = y_rows >> image->y_chroma_shift;
|
||||||
|
|
||||||
// Allocate a YUV buffer large enough for the aligned data & padding.
|
// Allocate a YUV buffer large enough for the aligned data & padding.
|
||||||
const int buffer_size = y_stride * y_rows + 2*uv_stride * uv_rows;
|
out_buffer_size = y_stride * y_rows + 2*uv_stride * uv_rows;
|
||||||
std::unique_ptr<uint8_t[]> image_buffer(new uint8_t[buffer_size]);
|
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.
|
// 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_|.
|
// Fill in the information for |image_|.
|
||||||
unsigned char* uchar_buffer =
|
unsigned char* uchar_buffer =
|
||||||
|
|
@ -180,13 +182,39 @@ public:
|
||||||
uint8_t* u_data = image->planes[1];
|
uint8_t* u_data = image->planes[1];
|
||||||
uint8_t* v_data = image->planes[2];
|
uint8_t* v_data = image->planes[2];
|
||||||
|
|
||||||
libyuv::I420Copy(src->DataY(), src->StrideY(),
|
if (m_scale) {
|
||||||
src->DataU(), src->StrideU(),
|
int src_width = src->width();
|
||||||
src->DataV(), src->StrideV(),
|
double dst_width = src_width * m_scale.value();
|
||||||
y_data, y_stride,
|
if (dst_width > image->w) {
|
||||||
u_data, uv_stride,
|
src_width *= image->w / dst_width;
|
||||||
v_data, uv_stride,
|
dst_width = image->w;
|
||||||
image->w, image->h);
|
}
|
||||||
|
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:
|
private:
|
||||||
|
|
@ -212,7 +240,7 @@ public:
|
||||||
|
|
||||||
ivf_write_file_header(m_file, &m_cfg, m_fourcc, 0);
|
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() {
|
~VPXCodec() {
|
||||||
|
|
@ -223,6 +251,7 @@ public:
|
||||||
void encodeFrameAsync(std::unique_ptr<VPXFrame>&& frame)
|
void encodeFrameAsync(std::unique_ptr<VPXFrame>&& frame)
|
||||||
{
|
{
|
||||||
m_encoderQueue->Dispatch(NS_NewRunnableFunction("VPXCodec::encodeFrameAsync", [this, frame = std::move(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());
|
frame->convertToVpxImage(m_image.get());
|
||||||
// TODO: figure out why passing duration to the codec results in much
|
// TODO: figure out why passing duration to the codec results in much
|
||||||
// worse visual quality and makes video stutter.
|
// worse visual quality and makes video stutter.
|
||||||
|
|
@ -292,6 +321,7 @@ private:
|
||||||
int m_frameCount { 0 };
|
int m_frameCount { 0 };
|
||||||
int64_t m_pts { 0 };
|
int64_t m_pts { 0 };
|
||||||
std::unique_ptr<uint8_t[]> m_imageBuffer;
|
std::unique_ptr<uint8_t[]> m_imageBuffer;
|
||||||
|
int m_imageBufferSize { 0 };
|
||||||
std::unique_ptr<vpx_image_t> m_image;
|
std::unique_ptr<vpx_image_t> m_image;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,6 @@ interface nsIDocShell;
|
||||||
[scriptable, uuid(d8c4d9e0-9462-445e-9e43-68d3872ad1de)]
|
[scriptable, uuid(d8c4d9e0-9462-445e-9e43-68d3872ad1de)]
|
||||||
interface nsIScreencastService : nsISupports
|
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);
|
void stopVideoRecording(in long sessionId);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ nsScreencastService::nsScreencastService() = default;
|
||||||
nsScreencastService::~nsScreencastService() {
|
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.");
|
MOZ_RELEASE_ASSERT(NS_IsMainThread(), "Screencast service must be started on the Main thread.");
|
||||||
*sessionId = -1;
|
*sessionId = -1;
|
||||||
|
|
||||||
|
|
@ -121,7 +121,10 @@ nsresult nsScreencastService::StartVideoRecording(nsIDocShell* aDocShell, const
|
||||||
# endif
|
# endif
|
||||||
*sessionId = ++mLastSessionId;
|
*sessionId = ++mLastSessionId;
|
||||||
nsCString error;
|
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) {
|
if (!encoder) {
|
||||||
fprintf(stderr, "Failed to create ScreencastEncoder: %s\n", error.get());
|
fprintf(stderr, "Failed to create ScreencastEncoder: %s\n", error.get());
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue