michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "MediaEngineTabVideoSource.h" michael@0: michael@0: #include "mozilla/gfx/2D.h" michael@0: #include "mozilla/RefPtr.h" michael@0: #include "nsGlobalWindow.h" michael@0: #include "nsDOMWindowUtils.h" michael@0: #include "nsIDOMClientRect.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsPresContext.h" michael@0: #include "gfxContext.h" michael@0: #include "gfx2DGlue.h" michael@0: #include "ImageContainer.h" michael@0: #include "Layers.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsITabSource.h" michael@0: #include "VideoUtils.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsIPrefService.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: using namespace mozilla::gfx; michael@0: michael@0: NS_IMPL_ISUPPORTS(MediaEngineTabVideoSource, nsIDOMEventListener, nsITimerCallback) michael@0: michael@0: MediaEngineTabVideoSource::MediaEngineTabVideoSource() michael@0: : mMonitor("MediaEngineTabVideoSource") michael@0: { michael@0: } michael@0: michael@0: nsresult michael@0: MediaEngineTabVideoSource::StartRunnable::Run() michael@0: { michael@0: mVideoSource->Draw(); michael@0: nsCOMPtr privateDOMWindow = do_QueryInterface(mVideoSource->mWindow); michael@0: if (privateDOMWindow) { michael@0: privateDOMWindow->GetChromeEventHandler()->AddEventListener(NS_LITERAL_STRING("MozAfterPaint"), mVideoSource, false); michael@0: } else { michael@0: mVideoSource->mTimer = do_CreateInstance(NS_TIMER_CONTRACTID); michael@0: mVideoSource->mTimer->InitWithCallback(mVideoSource, mVideoSource->mTimePerFrame, nsITimer:: TYPE_REPEATING_SLACK); michael@0: } michael@0: mVideoSource->mTabSource->NotifyStreamStart(mVideoSource->mWindow); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: MediaEngineTabVideoSource::StopRunnable::Run() michael@0: { michael@0: nsCOMPtr privateDOMWindow = do_QueryInterface(mVideoSource->mWindow); michael@0: if (privateDOMWindow && mVideoSource && privateDOMWindow->GetChromeEventHandler()) { michael@0: privateDOMWindow->GetChromeEventHandler()->RemoveEventListener(NS_LITERAL_STRING("MozAfterPaint"), mVideoSource, false); michael@0: } michael@0: michael@0: if (mVideoSource->mTimer) { michael@0: mVideoSource->mTimer->Cancel(); michael@0: mVideoSource->mTimer = nullptr; michael@0: } michael@0: mVideoSource->mTabSource->NotifyStreamStop(mVideoSource->mWindow); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: MediaEngineTabVideoSource::HandleEvent(nsIDOMEvent *event) { michael@0: Draw(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: MediaEngineTabVideoSource::Notify(nsITimer*) { michael@0: Draw(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: MediaEngineTabVideoSource::InitRunnable::Run() michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr prefs = do_GetService("@mozilla.org/preferences-service;1", &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: nsCOMPtr branch = do_QueryInterface(prefs); michael@0: if (!branch) michael@0: return NS_OK; michael@0: branch->GetIntPref("media.tabstreaming.width", &mVideoSource->mBufW); michael@0: branch->GetIntPref("media.tabstreaming.height", &mVideoSource->mBufH); michael@0: branch->GetIntPref("media.tabstreaming.time_per_frame", &mVideoSource->mTimePerFrame); michael@0: mVideoSource->mData = (unsigned char*)malloc(mVideoSource->mBufW * mVideoSource->mBufH * 4); michael@0: michael@0: mVideoSource->mTabSource = do_GetService(NS_TABSOURCESERVICE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr win; michael@0: rv = mVideoSource->mTabSource->GetTabToStream(getter_AddRefs(win)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (!win) michael@0: return NS_OK; michael@0: michael@0: mVideoSource->mWindow = win; michael@0: nsCOMPtr start(new StartRunnable(mVideoSource)); michael@0: start->Run(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: MediaEngineTabVideoSource::GetName(nsAString_internal& aName) michael@0: { michael@0: aName.Assign(NS_LITERAL_STRING("&getUserMedia.videoDevice.tabShare;")); michael@0: } michael@0: michael@0: void michael@0: MediaEngineTabVideoSource::GetUUID(nsAString_internal& aUuid) michael@0: { michael@0: aUuid.Assign(NS_LITERAL_STRING("uuid")); michael@0: } michael@0: michael@0: nsresult michael@0: MediaEngineTabVideoSource::Allocate(const VideoTrackConstraintsN&, michael@0: const MediaEnginePrefs&) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: MediaEngineTabVideoSource::Deallocate() michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: MediaEngineTabVideoSource::Start(mozilla::SourceMediaStream* aStream, mozilla::TrackID aID) michael@0: { michael@0: nsCOMPtr runnable; michael@0: if (!mWindow) michael@0: runnable = new InitRunnable(this); michael@0: else michael@0: runnable = new StartRunnable(this); michael@0: NS_DispatchToMainThread(runnable); michael@0: aStream->AddTrack(aID, USECS_PER_S, 0, new VideoSegment()); michael@0: aStream->AdvanceKnownTracksTime(STREAM_TIME_MAX); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: MediaEngineTabVideoSource::Snapshot(uint32_t, nsIDOMFile**) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: MediaEngineTabVideoSource:: michael@0: NotifyPull(MediaStreamGraph*, SourceMediaStream* aSource, mozilla::TrackID aID, mozilla::StreamTime aDesiredTime, mozilla::TrackTicks& aLastEndTime) michael@0: { michael@0: VideoSegment segment; michael@0: MonitorAutoLock mon(mMonitor); michael@0: michael@0: // Note: we're not giving up mImage here michael@0: nsRefPtr image = mImage; michael@0: TrackTicks target = TimeToTicksRoundUp(USECS_PER_S, aDesiredTime); michael@0: TrackTicks delta = target - aLastEndTime; michael@0: if (delta > 0) { michael@0: // nullptr images are allowed michael@0: gfx::IntSize size = image ? image->GetSize() : IntSize(0, 0); michael@0: segment.AppendFrame(image.forget().downcast(), delta, size); michael@0: // This can fail if either a) we haven't added the track yet, or b) michael@0: // we've removed or finished the track. michael@0: if (aSource->AppendToTrack(aID, &(segment))) { michael@0: aLastEndTime = target; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: MediaEngineTabVideoSource::Draw() { michael@0: michael@0: IntSize size(mBufW, mBufH); michael@0: michael@0: nsresult rv; michael@0: float scale = 1.0; michael@0: michael@0: nsCOMPtr win = do_QueryInterface(mWindow); michael@0: michael@0: if (!win) { michael@0: return; michael@0: } michael@0: michael@0: // take a screenshot, as wide as possible, proportional to the destination size michael@0: nsCOMPtr utils = do_GetInterface(win); michael@0: if (!utils) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr rect; michael@0: rv = utils->GetRootBounds(getter_AddRefs(rect)); michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: if (!rect) { michael@0: return; michael@0: } michael@0: michael@0: float left, top, width, height; michael@0: rect->GetLeft(&left); michael@0: rect->GetTop(&top); michael@0: rect->GetWidth(&width); michael@0: rect->GetHeight(&height); michael@0: michael@0: if (width == 0 || height == 0) { michael@0: return; michael@0: } michael@0: michael@0: int32_t srcX = left; michael@0: int32_t srcY = top; michael@0: int32_t srcW; michael@0: int32_t srcH; michael@0: michael@0: float aspectRatio = ((float) size.width) / size.height; michael@0: if (width / aspectRatio < height) { michael@0: srcW = width; michael@0: srcH = width / aspectRatio; michael@0: } else { michael@0: srcW = height * aspectRatio; michael@0: srcH = height; michael@0: } michael@0: michael@0: nsRefPtr presContext; michael@0: nsIDocShell* docshell = win->GetDocShell(); michael@0: if (docshell) { michael@0: docshell->GetPresContext(getter_AddRefs(presContext)); michael@0: } michael@0: if (!presContext) { michael@0: return; michael@0: } michael@0: nscolor bgColor = NS_RGB(255, 255, 255); michael@0: nsCOMPtr presShell = presContext->PresShell(); michael@0: uint32_t renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING | michael@0: nsIPresShell::RENDER_DOCUMENT_RELATIVE); michael@0: nsRect r(nsPresContext::CSSPixelsToAppUnits(srcX / scale), michael@0: nsPresContext::CSSPixelsToAppUnits(srcY / scale), michael@0: nsPresContext::CSSPixelsToAppUnits(srcW / scale), michael@0: nsPresContext::CSSPixelsToAppUnits(srcH / scale)); michael@0: michael@0: gfxImageFormat format = gfxImageFormat::RGB24; michael@0: uint32_t stride = gfxASurface::FormatStrideForWidth(format, size.width); michael@0: michael@0: nsRefPtr container = layers::LayerManager::CreateImageContainer(); michael@0: RefPtr dt = michael@0: Factory::CreateDrawTargetForData(BackendType::CAIRO, michael@0: mData.rwget(), michael@0: size, michael@0: stride, michael@0: SurfaceFormat::B8G8R8X8); michael@0: if (!dt) { michael@0: return; michael@0: } michael@0: nsRefPtr context = new gfxContext(dt); michael@0: gfxPoint pt(0, 0); michael@0: context->Translate(pt); michael@0: context->Scale(scale * size.width / srcW, scale * size.height / srcH); michael@0: rv = presShell->RenderDocument(r, renderDocFlags, bgColor, context); michael@0: michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: michael@0: RefPtr surface = dt->Snapshot(); michael@0: if (!surface) { michael@0: return; michael@0: } michael@0: michael@0: layers::CairoImage::Data cairoData; michael@0: cairoData.mSize = size; michael@0: cairoData.mSourceSurface = surface; michael@0: michael@0: nsRefPtr image = new layers::CairoImage(); michael@0: michael@0: image->SetData(cairoData); michael@0: michael@0: MonitorAutoLock mon(mMonitor); michael@0: mImage = image; michael@0: } michael@0: michael@0: nsresult michael@0: MediaEngineTabVideoSource::Stop(mozilla::SourceMediaStream*, mozilla::TrackID) michael@0: { michael@0: NS_DispatchToMainThread(new StopRunnable(this)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: MediaEngineTabVideoSource::Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: MediaEngineTabVideoSource::IsFake() michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: }