diff -r 000000000000 -r 6474c204b198 content/media/webrtc/MediaEngineTabVideoSource.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/content/media/webrtc/MediaEngineTabVideoSource.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,303 @@ +/* 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 "nsGlobalWindow.h" +#include "nsDOMWindowUtils.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" + +namespace mozilla { + +using namespace mozilla::gfx; + +NS_IMPL_ISUPPORTS(MediaEngineTabVideoSource, nsIDOMEventListener, nsITimerCallback) + +MediaEngineTabVideoSource::MediaEngineTabVideoSource() +: mMonitor("MediaEngineTabVideoSource") +{ +} + +nsresult +MediaEngineTabVideoSource::StartRunnable::Run() +{ + mVideoSource->Draw(); + nsCOMPtr privateDOMWindow = do_QueryInterface(mVideoSource->mWindow); + if (privateDOMWindow) { + privateDOMWindow->GetChromeEventHandler()->AddEventListener(NS_LITERAL_STRING("MozAfterPaint"), mVideoSource, false); + } else { + mVideoSource->mTimer = do_CreateInstance(NS_TIMER_CONTRACTID); + mVideoSource->mTimer->InitWithCallback(mVideoSource, mVideoSource->mTimePerFrame, nsITimer:: TYPE_REPEATING_SLACK); + } + mVideoSource->mTabSource->NotifyStreamStart(mVideoSource->mWindow); + return NS_OK; +} + +nsresult +MediaEngineTabVideoSource::StopRunnable::Run() +{ + nsCOMPtr privateDOMWindow = do_QueryInterface(mVideoSource->mWindow); + if (privateDOMWindow && mVideoSource && privateDOMWindow->GetChromeEventHandler()) { + privateDOMWindow->GetChromeEventHandler()->RemoveEventListener(NS_LITERAL_STRING("MozAfterPaint"), mVideoSource, false); + } + + if (mVideoSource->mTimer) { + mVideoSource->mTimer->Cancel(); + mVideoSource->mTimer = nullptr; + } + 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() +{ + nsresult rv; + nsCOMPtr prefs = do_GetService("@mozilla.org/preferences-service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr branch = do_QueryInterface(prefs); + if (!branch) + return NS_OK; + branch->GetIntPref("media.tabstreaming.width", &mVideoSource->mBufW); + branch->GetIntPref("media.tabstreaming.height", &mVideoSource->mBufH); + branch->GetIntPref("media.tabstreaming.time_per_frame", &mVideoSource->mTimePerFrame); + mVideoSource->mData = (unsigned char*)malloc(mVideoSource->mBufW * mVideoSource->mBufH * 4); + + mVideoSource->mTabSource = do_GetService(NS_TABSOURCESERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr win; + rv = mVideoSource->mTabSource->GetTabToStream(getter_AddRefs(win)); + NS_ENSURE_SUCCESS(rv, rv); + if (!win) + return NS_OK; + + mVideoSource->mWindow = win; + nsCOMPtr start(new StartRunnable(mVideoSource)); + start->Run(); + return NS_OK; +} + +void +MediaEngineTabVideoSource::GetName(nsAString_internal& aName) +{ + aName.Assign(NS_LITERAL_STRING("&getUserMedia.videoDevice.tabShare;")); +} + +void +MediaEngineTabVideoSource::GetUUID(nsAString_internal& aUuid) +{ + aUuid.Assign(NS_LITERAL_STRING("uuid")); +} + +nsresult +MediaEngineTabVideoSource::Allocate(const VideoTrackConstraintsN&, + const MediaEnginePrefs&) +{ + return NS_OK; +} + +nsresult +MediaEngineTabVideoSource::Deallocate() +{ + return NS_OK; +} + +nsresult +MediaEngineTabVideoSource::Start(mozilla::SourceMediaStream* aStream, mozilla::TrackID aID) +{ + nsCOMPtr runnable; + if (!mWindow) + runnable = new InitRunnable(this); + else + runnable = new StartRunnable(this); + NS_DispatchToMainThread(runnable); + aStream->AddTrack(aID, USECS_PER_S, 0, new VideoSegment()); + aStream->AdvanceKnownTracksTime(STREAM_TIME_MAX); + + return NS_OK; +} + +nsresult +MediaEngineTabVideoSource::Snapshot(uint32_t, nsIDOMFile**) +{ + return NS_OK; +} + +void +MediaEngineTabVideoSource:: +NotifyPull(MediaStreamGraph*, SourceMediaStream* aSource, mozilla::TrackID aID, mozilla::StreamTime aDesiredTime, mozilla::TrackTicks& aLastEndTime) +{ + VideoSegment segment; + MonitorAutoLock mon(mMonitor); + + // Note: we're not giving up mImage here + nsRefPtr image = mImage; + TrackTicks target = TimeToTicksRoundUp(USECS_PER_S, aDesiredTime); + TrackTicks delta = target - aLastEndTime; + if (delta > 0) { + // nullptr images are allowed + gfx::IntSize size = image ? image->GetSize() : IntSize(0, 0); + segment.AppendFrame(image.forget().downcast(), delta, size); + // This can fail if either a) we haven't added the track yet, or b) + // we've removed or finished the track. + if (aSource->AppendToTrack(aID, &(segment))) { + aLastEndTime = target; + } + } +} + +void +MediaEngineTabVideoSource::Draw() { + + IntSize size(mBufW, mBufH); + + nsresult rv; + float scale = 1.0; + + nsCOMPtr win = do_QueryInterface(mWindow); + + if (!win) { + return; + } + + // take a screenshot, as wide as possible, proportional to the destination size + nsCOMPtr utils = do_GetInterface(win); + if (!utils) { + return; + } + + nsCOMPtr rect; + rv = utils->GetRootBounds(getter_AddRefs(rect)); + NS_ENSURE_SUCCESS_VOID(rv); + if (!rect) { + return; + } + + float left, top, width, height; + rect->GetLeft(&left); + rect->GetTop(&top); + rect->GetWidth(&width); + rect->GetHeight(&height); + + if (width == 0 || height == 0) { + return; + } + + int32_t srcX = left; + int32_t srcY = top; + int32_t srcW; + int32_t srcH; + + float aspectRatio = ((float) size.width) / size.height; + if (width / aspectRatio < height) { + srcW = width; + srcH = width / aspectRatio; + } else { + srcW = height * aspectRatio; + srcH = height; + } + + nsRefPtr presContext; + nsIDocShell* docshell = win->GetDocShell(); + if (docshell) { + docshell->GetPresContext(getter_AddRefs(presContext)); + } + if (!presContext) { + return; + } + nscolor bgColor = NS_RGB(255, 255, 255); + nsCOMPtr presShell = presContext->PresShell(); + uint32_t renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING | + nsIPresShell::RENDER_DOCUMENT_RELATIVE); + nsRect r(nsPresContext::CSSPixelsToAppUnits(srcX / scale), + nsPresContext::CSSPixelsToAppUnits(srcY / scale), + nsPresContext::CSSPixelsToAppUnits(srcW / scale), + nsPresContext::CSSPixelsToAppUnits(srcH / scale)); + + gfxImageFormat format = gfxImageFormat::RGB24; + uint32_t stride = gfxASurface::FormatStrideForWidth(format, size.width); + + nsRefPtr container = layers::LayerManager::CreateImageContainer(); + RefPtr dt = + Factory::CreateDrawTargetForData(BackendType::CAIRO, + mData.rwget(), + size, + stride, + SurfaceFormat::B8G8R8X8); + if (!dt) { + return; + } + nsRefPtr context = new gfxContext(dt); + gfxPoint pt(0, 0); + context->Translate(pt); + context->Scale(scale * size.width / srcW, scale * size.height / srcH); + rv = presShell->RenderDocument(r, renderDocFlags, bgColor, context); + + NS_ENSURE_SUCCESS_VOID(rv); + + RefPtr surface = dt->Snapshot(); + if (!surface) { + return; + } + + layers::CairoImage::Data cairoData; + cairoData.mSize = size; + cairoData.mSourceSurface = surface; + + nsRefPtr image = new layers::CairoImage(); + + image->SetData(cairoData); + + MonitorAutoLock mon(mMonitor); + mImage = image; +} + +nsresult +MediaEngineTabVideoSource::Stop(mozilla::SourceMediaStream*, mozilla::TrackID) +{ + NS_DispatchToMainThread(new StopRunnable(this)); + return NS_OK; +} + +nsresult +MediaEngineTabVideoSource::Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t) +{ + return NS_OK; +} + +bool +MediaEngineTabVideoSource::IsFake() +{ + return false; +} + +}