summaryrefslogtreecommitdiffstats
path: root/dom/media/webrtc/MediaEngineTabVideoSource.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/webrtc/MediaEngineTabVideoSource.cpp')
-rw-r--r--dom/media/webrtc/MediaEngineTabVideoSource.cpp395
1 files changed, 395 insertions, 0 deletions
diff --git a/dom/media/webrtc/MediaEngineTabVideoSource.cpp b/dom/media/webrtc/MediaEngineTabVideoSource.cpp
new file mode 100644
index 000000000..d101bab1e
--- /dev/null
+++ b/dom/media/webrtc/MediaEngineTabVideoSource.cpp
@@ -0,0 +1,395 @@
+/* -*- 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
+ * 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/. */
+
+#include "MediaEngineTabVideoSource.h"
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "nsGlobalWindow.h"
+#include "nsIDOMClientRect.h"
+#include "nsIDocShell.h"
+#include "nsIPresShell.h"
+#include "nsPresContext.h"
+#include "gfxContext.h"
+#include "gfx2DGlue.h"
+#include "ImageContainer.h"
+#include "Layers.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIDOMDocument.h"
+#include "nsITabSource.h"
+#include "VideoUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIPrefService.h"
+#include "MediaTrackConstraints.h"
+
+namespace mozilla {
+
+using namespace mozilla::gfx;
+
+NS_IMPL_ISUPPORTS(MediaEngineTabVideoSource, nsIDOMEventListener, nsITimerCallback)
+
+MediaEngineTabVideoSource::MediaEngineTabVideoSource()
+ : mBufWidthMax(0)
+ , mBufHeightMax(0)
+ , mWindowId(0)
+ , mScrollWithPage(false)
+ , mViewportOffsetX(0)
+ , mViewportOffsetY(0)
+ , mViewportWidth(0)
+ , mViewportHeight(0)
+ , mTimePerFrame(0)
+ , mDataSize(0)
+ , mBlackedoutWindow(false)
+ , mMonitor("MediaEngineTabVideoSource") {}
+
+nsresult
+MediaEngineTabVideoSource::StartRunnable::Run()
+{
+ mVideoSource->Draw();
+ mVideoSource->mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+ mVideoSource->mTimer->InitWithCallback(mVideoSource, mVideoSource->mTimePerFrame, nsITimer:: TYPE_REPEATING_SLACK);
+ if (mVideoSource->mTabSource) {
+ mVideoSource->mTabSource->NotifyStreamStart(mVideoSource->mWindow);
+ }
+ return NS_OK;
+}
+
+nsresult
+MediaEngineTabVideoSource::StopRunnable::Run()
+{
+ if (mVideoSource->mTimer) {
+ mVideoSource->mTimer->Cancel();
+ mVideoSource->mTimer = nullptr;
+ }
+ if (mVideoSource->mTabSource) {
+ mVideoSource->mTabSource->NotifyStreamStop(mVideoSource->mWindow);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+MediaEngineTabVideoSource::HandleEvent(nsIDOMEvent *event) {
+ Draw();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+MediaEngineTabVideoSource::Notify(nsITimer*) {
+ Draw();
+ return NS_OK;
+}
+
+nsresult
+MediaEngineTabVideoSource::InitRunnable::Run()
+{
+ if (mVideoSource->mWindowId != -1) {
+ nsGlobalWindow* globalWindow =
+ nsGlobalWindow::GetOuterWindowWithId(mVideoSource->mWindowId);
+ if (!globalWindow) {
+ // We can't access the window, just send a blacked out screen.
+ mVideoSource->mWindow = nullptr;
+ mVideoSource->mBlackedoutWindow = true;
+ } else {
+ nsCOMPtr<nsPIDOMWindowOuter> window = globalWindow->AsOuter();
+ if (window) {
+ mVideoSource->mWindow = window;
+ mVideoSource->mBlackedoutWindow = false;
+ }
+ }
+ }
+ if (!mVideoSource->mWindow && !mVideoSource->mBlackedoutWindow) {
+ nsresult rv;
+ mVideoSource->mTabSource = do_GetService(NS_TABSOURCESERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<mozIDOMWindowProxy> win;
+ rv = mVideoSource->mTabSource->GetTabToStream(getter_AddRefs(win));
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!win)
+ return NS_OK;
+
+ mVideoSource->mWindow = nsPIDOMWindowOuter::From(win);
+ MOZ_ASSERT(mVideoSource->mWindow);
+ }
+ nsCOMPtr<nsIRunnable> start(new StartRunnable(mVideoSource));
+ start->Run();
+ return NS_OK;
+}
+
+nsresult
+MediaEngineTabVideoSource::DestroyRunnable::Run()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mVideoSource->mWindow = nullptr;
+ mVideoSource->mTabSource = nullptr;
+
+ return NS_OK;
+}
+
+void
+MediaEngineTabVideoSource::GetName(nsAString_internal& aName) const
+{
+ aName.AssignLiteral(u"&getUserMedia.videoSource.tabShare;");
+}
+
+void
+MediaEngineTabVideoSource::GetUUID(nsACString_internal& aUuid) const
+{
+ aUuid.AssignLiteral("tab");
+}
+
+#define DEFAULT_TABSHARE_VIDEO_MAX_WIDTH 4096
+#define DEFAULT_TABSHARE_VIDEO_MAX_HEIGHT 4096
+#define DEFAULT_TABSHARE_VIDEO_FRAMERATE 30
+
+nsresult
+MediaEngineTabVideoSource::Allocate(const dom::MediaTrackConstraints& aConstraints,
+ const MediaEnginePrefs& aPrefs,
+ const nsString& aDeviceId,
+ const nsACString& aOrigin,
+ AllocationHandle** aOutHandle,
+ const char** aOutBadConstraint)
+{
+ // windowId is not a proper constraint, so just read it.
+ // It has no well-defined behavior in advanced, so ignore it there.
+
+ mWindowId = aConstraints.mBrowserWindow.WasPassed() ?
+ aConstraints.mBrowserWindow.Value() : -1;
+ *aOutHandle = nullptr;
+
+ {
+ MonitorAutoLock mon(mMonitor);
+ mState = kAllocated;
+ }
+
+ return Restart(nullptr, aConstraints, aPrefs, aDeviceId, aOutBadConstraint);
+}
+
+nsresult
+MediaEngineTabVideoSource::Restart(AllocationHandle* aHandle,
+ const dom::MediaTrackConstraints& aConstraints,
+ const mozilla::MediaEnginePrefs& aPrefs,
+ const nsString& aDeviceId,
+ const char** aOutBadConstraint)
+{
+ MOZ_ASSERT(!aHandle);
+
+ // scrollWithPage is not proper a constraint, so just read it.
+ // It has no well-defined behavior in advanced, so ignore it there.
+
+ mScrollWithPage = aConstraints.mScrollWithPage.WasPassed() ?
+ aConstraints.mScrollWithPage.Value() : false;
+
+ FlattenedConstraints c(aConstraints);
+
+ mBufWidthMax = c.mWidth.Get(DEFAULT_TABSHARE_VIDEO_MAX_WIDTH);
+ mBufHeightMax = c.mHeight.Get(DEFAULT_TABSHARE_VIDEO_MAX_HEIGHT);
+ double frameRate = c.mFrameRate.Get(DEFAULT_TABSHARE_VIDEO_FRAMERATE);
+ mTimePerFrame = std::max(10, int(1000.0 / (frameRate > 0? frameRate : 1)));
+
+ if (!mScrollWithPage) {
+ mViewportOffsetX = c.mViewportOffsetX.Get(0);
+ mViewportOffsetY = c.mViewportOffsetY.Get(0);
+ mViewportWidth = c.mViewportWidth.Get(INT32_MAX);
+ mViewportHeight = c.mViewportHeight.Get(INT32_MAX);
+ }
+ return NS_OK;
+}
+
+nsresult
+MediaEngineTabVideoSource::Deallocate(AllocationHandle* aHandle)
+{
+ MOZ_ASSERT(!aHandle);
+ NS_DispatchToMainThread(do_AddRef(new DestroyRunnable(this)));
+
+ {
+ MonitorAutoLock mon(mMonitor);
+ mState = kReleased;
+ }
+ return NS_OK;
+}
+
+nsresult
+MediaEngineTabVideoSource::Start(SourceMediaStream* aStream, TrackID aID,
+ const PrincipalHandle& aPrincipalHandle)
+{
+ nsCOMPtr<nsIRunnable> runnable;
+ if (!mWindow)
+ runnable = new InitRunnable(this);
+ else
+ runnable = new StartRunnable(this);
+ NS_DispatchToMainThread(runnable);
+ aStream->AddTrack(aID, 0, new VideoSegment());
+
+ {
+ MonitorAutoLock mon(mMonitor);
+ mState = kStarted;
+ }
+
+ return NS_OK;
+}
+
+void
+MediaEngineTabVideoSource::NotifyPull(MediaStreamGraph*,
+ SourceMediaStream* aSource,
+ TrackID aID, StreamTime aDesiredTime,
+ const PrincipalHandle& aPrincipalHandle)
+{
+ VideoSegment segment;
+ MonitorAutoLock mon(mMonitor);
+ if (mState != kStarted) {
+ return;
+ }
+
+ // Note: we're not giving up mImage here
+ RefPtr<layers::SourceSurfaceImage> image = mImage;
+ StreamTime delta = aDesiredTime - aSource->GetEndOfAppendedData(aID);
+ if (delta > 0) {
+ // nullptr images are allowed
+ gfx::IntSize size = image ? image->GetSize() : IntSize(0, 0);
+ segment.AppendFrame(image.forget().downcast<layers::Image>(), delta, size,
+ aPrincipalHandle);
+ // This can fail if either a) we haven't added the track yet, or b)
+ // we've removed or finished the track.
+ aSource->AppendToTrack(aID, &(segment));
+ }
+}
+
+void
+MediaEngineTabVideoSource::Draw() {
+ if (!mWindow && !mBlackedoutWindow) {
+ return;
+ }
+
+ if (mWindow) {
+ if (mScrollWithPage || mViewportWidth == INT32_MAX) {
+ mWindow->GetInnerWidth(&mViewportWidth);
+ }
+ if (mScrollWithPage || mViewportHeight == INT32_MAX) {
+ mWindow->GetInnerHeight(&mViewportHeight);
+ }
+ if (!mViewportWidth || !mViewportHeight) {
+ return;
+ }
+ } else {
+ mViewportWidth = 640;
+ mViewportHeight = 480;
+ }
+
+ IntSize size;
+ {
+ float pixelRatio;
+ if (mWindow) {
+ pixelRatio = mWindow->GetDevicePixelRatio(CallerType::System);
+ } else {
+ pixelRatio = 1.0f;
+ }
+ const int32_t deviceWidth = (int32_t)(pixelRatio * mViewportWidth);
+ const int32_t deviceHeight = (int32_t)(pixelRatio * mViewportHeight);
+
+ if ((deviceWidth <= mBufWidthMax) && (deviceHeight <= mBufHeightMax)) {
+ size = IntSize(deviceWidth, deviceHeight);
+ } else {
+ const float scaleWidth = (float)mBufWidthMax / (float)deviceWidth;
+ const float scaleHeight = (float)mBufHeightMax / (float)deviceHeight;
+ const float scale = scaleWidth < scaleHeight ? scaleWidth : scaleHeight;
+
+ size = IntSize((int)(scale * deviceWidth), (int)(scale * deviceHeight));
+ }
+ }
+
+ gfxImageFormat format = SurfaceFormat::X8R8G8B8_UINT32;
+ uint32_t stride = gfxASurface::FormatStrideForWidth(format, size.width);
+
+ if (mDataSize < static_cast<size_t>(stride * size.height)) {
+ mDataSize = stride * size.height;
+ mData = MakeUniqueFallible<unsigned char[]>(mDataSize);
+ }
+ if (!mData) {
+ return;
+ }
+
+ nsCOMPtr<nsIPresShell> presShell;
+ if (mWindow) {
+ RefPtr<nsPresContext> presContext;
+ nsIDocShell* docshell = mWindow->GetDocShell();
+ if (docshell) {
+ docshell->GetPresContext(getter_AddRefs(presContext));
+ }
+ if (!presContext) {
+ return;
+ }
+ presShell = presContext->PresShell();
+ }
+
+ RefPtr<layers::ImageContainer> container =
+ layers::LayerManager::CreateImageContainer(layers::ImageContainer::ASYNCHRONOUS);
+ RefPtr<DrawTarget> dt =
+ Factory::CreateDrawTargetForData(BackendType::CAIRO,
+ mData.get(),
+ size,
+ stride,
+ SurfaceFormat::B8G8R8X8);
+ if (!dt || !dt->IsValid()) {
+ return;
+ }
+ RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
+ MOZ_ASSERT(context); // already checked the draw target above
+ context->SetMatrix(context->CurrentMatrix().Scale((((float) size.width)/mViewportWidth),
+ (((float) size.height)/mViewportHeight)));
+
+ if (mWindow) {
+ nscolor bgColor = NS_RGB(255, 255, 255);
+ uint32_t renderDocFlags = mScrollWithPage? 0 :
+ (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
+ nsIPresShell::RENDER_DOCUMENT_RELATIVE);
+ nsRect r(nsPresContext::CSSPixelsToAppUnits((float)mViewportOffsetX),
+ nsPresContext::CSSPixelsToAppUnits((float)mViewportOffsetY),
+ nsPresContext::CSSPixelsToAppUnits((float)mViewportWidth),
+ nsPresContext::CSSPixelsToAppUnits((float)mViewportHeight));
+ NS_ENSURE_SUCCESS_VOID(presShell->RenderDocument(r, renderDocFlags, bgColor, context));
+ }
+
+ RefPtr<SourceSurface> surface = dt->Snapshot();
+ if (!surface) {
+ return;
+ }
+
+ RefPtr<layers::SourceSurfaceImage> image = new layers::SourceSurfaceImage(size, surface);
+
+ MonitorAutoLock mon(mMonitor);
+ mImage = image;
+}
+
+nsresult
+MediaEngineTabVideoSource::Stop(mozilla::SourceMediaStream* aSource,
+ mozilla::TrackID aID)
+{
+ // If mBlackedoutWindow is true, we may be running
+ // despite mWindow == nullptr.
+ if (!mWindow && !mBlackedoutWindow) {
+ return NS_OK;
+ }
+
+ NS_DispatchToMainThread(new StopRunnable(this));
+
+ {
+ MonitorAutoLock mon(mMonitor);
+ mState = kStopped;
+ aSource->EndTrack(aID);
+ }
+ return NS_OK;
+}
+
+bool
+MediaEngineTabVideoSource::IsFake()
+{
+ return false;
+}
+
+}