2014-07-21 22:57:28 +04:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
2014-03-26 08:59:01 +04:00
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
2014-04-10 12:49:53 +04:00
|
|
|
#include "MediaEngineTabVideoSource.h"
|
|
|
|
|
|
|
|
#include "mozilla/gfx/2D.h"
|
|
|
|
#include "mozilla/RefPtr.h"
|
2013-10-18 00:48:30 +04:00
|
|
|
#include "nsGlobalWindow.h"
|
|
|
|
#include "nsIDOMClientRect.h"
|
|
|
|
#include "nsIDocShell.h"
|
|
|
|
#include "nsIPresShell.h"
|
|
|
|
#include "nsPresContext.h"
|
|
|
|
#include "gfxContext.h"
|
2013-12-13 21:32:02 +04:00
|
|
|
#include "gfx2DGlue.h"
|
2013-10-18 00:48:30 +04:00
|
|
|
#include "ImageContainer.h"
|
|
|
|
#include "Layers.h"
|
|
|
|
#include "nsIInterfaceRequestorUtils.h"
|
|
|
|
#include "nsIDOMDocument.h"
|
|
|
|
#include "nsITabSource.h"
|
|
|
|
#include "VideoUtils.h"
|
|
|
|
#include "nsServiceManagerUtils.h"
|
|
|
|
#include "nsIPrefService.h"
|
2014-07-11 19:55:23 +04:00
|
|
|
#include "MediaTrackConstraints.h"
|
2014-03-26 08:59:01 +04:00
|
|
|
|
2013-10-18 00:48:30 +04:00
|
|
|
namespace mozilla {
|
|
|
|
|
2013-12-31 13:06:12 +04:00
|
|
|
using namespace mozilla::gfx;
|
|
|
|
|
2014-04-27 11:06:00 +04:00
|
|
|
NS_IMPL_ISUPPORTS(MediaEngineTabVideoSource, nsIDOMEventListener, nsITimerCallback)
|
2013-10-18 00:48:30 +04:00
|
|
|
|
|
|
|
MediaEngineTabVideoSource::MediaEngineTabVideoSource()
|
2015-01-21 05:52:34 +03:00
|
|
|
: mData(NULL), mDataSize(0), mMonitor("MediaEngineTabVideoSource"), mTabSource(nullptr)
|
2014-03-18 23:05:46 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-10-18 00:48:30 +04:00
|
|
|
nsresult
|
|
|
|
MediaEngineTabVideoSource::StartRunnable::Run()
|
|
|
|
{
|
|
|
|
mVideoSource->Draw();
|
2014-07-12 01:03:47 +04:00
|
|
|
mVideoSource->mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
|
|
|
mVideoSource->mTimer->InitWithCallback(mVideoSource, mVideoSource->mTimePerFrame, nsITimer:: TYPE_REPEATING_SLACK);
|
2014-07-21 22:57:28 +04:00
|
|
|
if (mVideoSource->mTabSource) {
|
|
|
|
mVideoSource->mTabSource->NotifyStreamStart(mVideoSource->mWindow);
|
|
|
|
}
|
2013-10-18 00:48:30 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
MediaEngineTabVideoSource::StopRunnable::Run()
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsPIDOMWindow> privateDOMWindow = do_QueryInterface(mVideoSource->mWindow);
|
|
|
|
|
|
|
|
if (mVideoSource->mTimer) {
|
|
|
|
mVideoSource->mTimer->Cancel();
|
2013-10-24 00:34:10 +04:00
|
|
|
mVideoSource->mTimer = nullptr;
|
2013-10-18 00:48:30 +04:00
|
|
|
}
|
2014-07-21 22:57:28 +04:00
|
|
|
if (mVideoSource->mTabSource) {
|
|
|
|
mVideoSource->mTabSource->NotifyStreamStop(mVideoSource->mWindow);
|
|
|
|
}
|
2013-10-18 00:48:30 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
MediaEngineTabVideoSource::HandleEvent(nsIDOMEvent *event) {
|
|
|
|
Draw();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
MediaEngineTabVideoSource::Notify(nsITimer*) {
|
|
|
|
Draw();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2014-07-11 19:55:23 +04:00
|
|
|
#define LOGTAG "TabVideo"
|
2013-10-18 00:48:30 +04:00
|
|
|
|
|
|
|
nsresult
|
|
|
|
MediaEngineTabVideoSource::InitRunnable::Run()
|
|
|
|
{
|
2014-07-21 22:57:28 +04:00
|
|
|
if (mVideoSource->mWindowId != -1) {
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window = nsGlobalWindow::GetOuterWindowWithId(mVideoSource->mWindowId);
|
|
|
|
if (window) {
|
|
|
|
mVideoSource->mWindow = window;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!mVideoSource->mWindow) {
|
|
|
|
nsresult rv;
|
|
|
|
mVideoSource->mTabSource = do_GetService(NS_TABSOURCESERVICE_CONTRACTID, &rv);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMWindow> win;
|
|
|
|
rv = mVideoSource->mTabSource->GetTabToStream(getter_AddRefs(win));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (!win)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
mVideoSource->mWindow = win;
|
|
|
|
}
|
2013-10-18 00:48:30 +04:00
|
|
|
nsCOMPtr<nsIRunnable> start(new StartRunnable(mVideoSource));
|
|
|
|
start->Run();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaEngineTabVideoSource::GetName(nsAString_internal& aName)
|
|
|
|
{
|
2014-04-18 23:01:56 +04:00
|
|
|
aName.AssignLiteral(MOZ_UTF16("&getUserMedia.videoSource.tabShare;"));
|
2013-10-18 00:48:30 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaEngineTabVideoSource::GetUUID(nsAString_internal& aUuid)
|
|
|
|
{
|
2014-05-26 22:54:10 +04:00
|
|
|
aUuid.AssignLiteral(MOZ_UTF16("uuid"));
|
2013-10-18 00:48:30 +04:00
|
|
|
}
|
|
|
|
|
2015-03-13 19:21:20 +03:00
|
|
|
#define DEFAULT_TABSHARE_VIDEO_MAX_WIDTH 4096
|
|
|
|
#define DEFAULT_TABSHARE_VIDEO_MAX_HEIGHT 4096
|
|
|
|
#define DEFAULT_TABSHARE_VIDEO_FRAMERATE 30
|
|
|
|
|
2013-10-18 00:48:30 +04:00
|
|
|
nsresult
|
2015-02-21 01:06:26 +03:00
|
|
|
MediaEngineTabVideoSource::Allocate(const dom::MediaTrackConstraints& aConstraints,
|
2014-07-11 19:55:23 +04:00
|
|
|
const MediaEnginePrefs& aPrefs)
|
2013-10-18 00:48:30 +04:00
|
|
|
{
|
2015-02-21 01:06:26 +03:00
|
|
|
// windowId and scrollWithPage are not proper constraints, so just read them.
|
|
|
|
// They have no well-defined behavior in advanced, so ignore them there.
|
|
|
|
|
|
|
|
mWindowId = aConstraints.mBrowserWindow.WasPassed() ?
|
|
|
|
aConstraints.mBrowserWindow.Value() : -1;
|
|
|
|
mScrollWithPage = aConstraints.mScrollWithPage.WasPassed() ?
|
|
|
|
aConstraints.mScrollWithPage.Value() : true;
|
|
|
|
|
|
|
|
FlattenedConstraints c(aConstraints);
|
|
|
|
|
|
|
|
mBufWidthMax = c.mWidth.Clamp(c.mWidth.mIdeal.WasPassed() ?
|
2015-03-13 19:21:20 +03:00
|
|
|
c.mWidth.mIdeal.Value() : DEFAULT_TABSHARE_VIDEO_MAX_WIDTH);
|
2015-02-21 01:06:26 +03:00
|
|
|
mBufHeightMax = c.mHeight.Clamp(c.mHeight.mIdeal.WasPassed() ?
|
2015-03-13 19:21:20 +03:00
|
|
|
c.mHeight.mIdeal.Value() : DEFAULT_TABSHARE_VIDEO_MAX_HEIGHT);
|
2015-02-21 01:06:26 +03:00
|
|
|
double frameRate = c.mFrameRate.Clamp(c.mFrameRate.mIdeal.WasPassed() ?
|
2015-03-13 19:21:20 +03:00
|
|
|
c.mFrameRate.mIdeal.Value() : DEFAULT_TABSHARE_VIDEO_FRAMERATE);
|
2015-02-21 01:06:26 +03:00
|
|
|
mTimePerFrame = std::max(10, int(1000.0 / (frameRate > 0? frameRate : 1)));
|
2013-10-18 00:48:30 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
MediaEngineTabVideoSource::Deallocate()
|
|
|
|
{
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
2014-09-18 03:50:01 +04:00
|
|
|
MediaEngineTabVideoSource::Start(SourceMediaStream* aStream, TrackID aID)
|
2013-10-18 00:48:30 +04:00
|
|
|
{
|
|
|
|
nsCOMPtr<nsIRunnable> runnable;
|
|
|
|
if (!mWindow)
|
|
|
|
runnable = new InitRunnable(this);
|
|
|
|
else
|
|
|
|
runnable = new StartRunnable(this);
|
|
|
|
NS_DispatchToMainThread(runnable);
|
2014-09-18 03:50:02 +04:00
|
|
|
aStream->AddTrack(aID, 0, new VideoSegment());
|
2013-10-18 00:48:30 +04:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2014-09-18 03:50:01 +04:00
|
|
|
MediaEngineTabVideoSource::NotifyPull(MediaStreamGraph*,
|
|
|
|
SourceMediaStream* aSource,
|
2014-12-30 04:54:03 +03:00
|
|
|
TrackID aID, StreamTime aDesiredTime)
|
2013-10-18 00:48:30 +04:00
|
|
|
{
|
|
|
|
VideoSegment segment;
|
|
|
|
MonitorAutoLock mon(mMonitor);
|
|
|
|
|
|
|
|
// Note: we're not giving up mImage here
|
|
|
|
nsRefPtr<layers::CairoImage> image = mImage;
|
2014-12-30 04:54:02 +03:00
|
|
|
StreamTime delta = aDesiredTime - aSource->GetEndOfAppendedData(aID);
|
2013-10-18 00:48:30 +04:00
|
|
|
if (delta > 0) {
|
2013-10-24 00:34:10 +04:00
|
|
|
// nullptr images are allowed
|
2014-03-15 23:00:17 +04:00
|
|
|
gfx::IntSize size = image ? image->GetSize() : IntSize(0, 0);
|
|
|
|
segment.AppendFrame(image.forget().downcast<layers::Image>(), delta, size);
|
2013-10-18 00:48:30 +04:00
|
|
|
// This can fail if either a) we haven't added the track yet, or b)
|
|
|
|
// we've removed or finished the track.
|
2014-12-30 04:54:03 +03:00
|
|
|
aSource->AppendToTrack(aID, &(segment));
|
2013-10-18 00:48:30 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaEngineTabVideoSource::Draw() {
|
|
|
|
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mWindow);
|
|
|
|
|
|
|
|
if (!win) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-01-20 22:54:19 +03:00
|
|
|
int32_t innerWidth, innerHeight;
|
|
|
|
win->GetInnerWidth(&innerWidth);
|
|
|
|
win->GetInnerHeight(&innerHeight);
|
2013-10-18 00:48:30 +04:00
|
|
|
|
2015-01-20 22:54:19 +03:00
|
|
|
if (innerWidth == 0 || innerHeight == 0) {
|
2013-10-18 00:48:30 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-01-20 22:54:19 +03:00
|
|
|
IntSize size;
|
|
|
|
// maintain source aspect ratio
|
|
|
|
if (mBufWidthMax/innerWidth < mBufHeightMax/innerHeight) {
|
2015-03-13 19:21:20 +03:00
|
|
|
// mBufWidthMax is quite large by default, so use innerWidth if less.
|
|
|
|
int32_t width = std::min(innerWidth, mBufWidthMax);
|
2015-01-23 19:53:15 +03:00
|
|
|
// adjust width to be divisible by 4 to work around bug 1125393
|
2015-03-13 19:21:20 +03:00
|
|
|
width = width - (width % 4);
|
2015-01-23 19:53:15 +03:00
|
|
|
size = IntSize(width, (width * ((float) innerHeight/innerWidth)));
|
2013-10-18 00:48:30 +04:00
|
|
|
} else {
|
2015-03-13 19:21:20 +03:00
|
|
|
int32_t width = std::min(innerHeight, mBufHeightMax) *
|
|
|
|
((float) innerWidth/innerHeight);
|
|
|
|
width = width - (width % 4);
|
2015-01-23 19:53:15 +03:00
|
|
|
size = IntSize(width, (width * ((float) innerHeight/innerWidth)));
|
2015-01-20 22:54:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
gfxImageFormat format = gfxImageFormat::RGB24;
|
|
|
|
uint32_t stride = gfxASurface::FormatStrideForWidth(format, size.width);
|
|
|
|
|
|
|
|
if (mDataSize < static_cast<size_t>(stride * size.height)) {
|
|
|
|
mDataSize = stride * size.height;
|
|
|
|
mData = static_cast<unsigned char*>(malloc(mDataSize));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mData) {
|
|
|
|
return;
|
2013-10-18 00:48:30 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
nsRefPtr<nsPresContext> presContext;
|
|
|
|
nsIDocShell* docshell = win->GetDocShell();
|
|
|
|
if (docshell) {
|
|
|
|
docshell->GetPresContext(getter_AddRefs(presContext));
|
|
|
|
}
|
|
|
|
if (!presContext) {
|
|
|
|
return;
|
|
|
|
}
|
2014-12-17 01:43:23 +03:00
|
|
|
|
2013-10-18 00:48:30 +04:00
|
|
|
nscolor bgColor = NS_RGB(255, 255, 255);
|
|
|
|
nsCOMPtr<nsIPresShell> presShell = presContext->PresShell();
|
2014-12-31 22:31:05 +03:00
|
|
|
uint32_t renderDocFlags = 0;
|
2014-12-17 01:43:23 +03:00
|
|
|
if (!mScrollWithPage) {
|
|
|
|
renderDocFlags |= nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
|
|
|
|
}
|
2015-01-20 22:54:19 +03:00
|
|
|
nsRect r(0, 0, nsPresContext::CSSPixelsToAppUnits((float)innerWidth),
|
|
|
|
nsPresContext::CSSPixelsToAppUnits((float)innerHeight));
|
2013-10-18 00:48:30 +04:00
|
|
|
|
|
|
|
nsRefPtr<layers::ImageContainer> container = layers::LayerManager::CreateImageContainer();
|
2014-04-10 12:49:53 +04:00
|
|
|
RefPtr<DrawTarget> dt =
|
|
|
|
Factory::CreateDrawTargetForData(BackendType::CAIRO,
|
|
|
|
mData.rwget(),
|
|
|
|
size,
|
|
|
|
stride,
|
|
|
|
SurfaceFormat::B8G8R8X8);
|
|
|
|
if (!dt) {
|
2013-10-18 00:48:30 +04:00
|
|
|
return;
|
|
|
|
}
|
2014-04-10 12:49:53 +04:00
|
|
|
nsRefPtr<gfxContext> context = new gfxContext(dt);
|
2015-01-20 22:54:19 +03:00
|
|
|
context->SetMatrix(context->CurrentMatrix().Scale((((float) size.width)/innerWidth),
|
|
|
|
(((float) size.height)/innerHeight)));
|
2013-10-18 00:48:30 +04:00
|
|
|
|
2015-01-20 22:54:19 +03:00
|
|
|
NS_ENSURE_SUCCESS_VOID(presShell->RenderDocument(r, renderDocFlags, bgColor, context));
|
2013-10-18 00:48:30 +04:00
|
|
|
|
2014-04-10 12:49:53 +04:00
|
|
|
RefPtr<SourceSurface> surface = dt->Snapshot();
|
|
|
|
if (!surface) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-18 00:48:30 +04:00
|
|
|
layers::CairoImage::Data cairoData;
|
|
|
|
cairoData.mSize = size;
|
2014-04-10 12:49:53 +04:00
|
|
|
cairoData.mSourceSurface = surface;
|
2013-10-18 00:48:30 +04:00
|
|
|
|
|
|
|
nsRefPtr<layers::CairoImage> image = new layers::CairoImage();
|
|
|
|
|
|
|
|
image->SetData(cairoData);
|
|
|
|
|
|
|
|
MonitorAutoLock mon(mMonitor);
|
|
|
|
mImage = image;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
MediaEngineTabVideoSource::Stop(mozilla::SourceMediaStream*, mozilla::TrackID)
|
|
|
|
{
|
2014-07-01 17:38:20 +04:00
|
|
|
if (!mWindow)
|
|
|
|
return NS_OK;
|
|
|
|
|
2013-10-18 00:48:30 +04:00
|
|
|
NS_DispatchToMainThread(new StopRunnable(this));
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
2014-04-02 21:58:19 +04:00
|
|
|
MediaEngineTabVideoSource::Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t)
|
2013-10-18 00:48:30 +04:00
|
|
|
{
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaEngineTabVideoSource::IsFake()
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|