1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/webrtc/MediaEngineTabVideoSource.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,303 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "MediaEngineTabVideoSource.h" 1.9 + 1.10 +#include "mozilla/gfx/2D.h" 1.11 +#include "mozilla/RefPtr.h" 1.12 +#include "nsGlobalWindow.h" 1.13 +#include "nsDOMWindowUtils.h" 1.14 +#include "nsIDOMClientRect.h" 1.15 +#include "nsIDocShell.h" 1.16 +#include "nsIPresShell.h" 1.17 +#include "nsPresContext.h" 1.18 +#include "gfxContext.h" 1.19 +#include "gfx2DGlue.h" 1.20 +#include "ImageContainer.h" 1.21 +#include "Layers.h" 1.22 +#include "nsIInterfaceRequestorUtils.h" 1.23 +#include "nsIDOMDocument.h" 1.24 +#include "nsITabSource.h" 1.25 +#include "VideoUtils.h" 1.26 +#include "nsServiceManagerUtils.h" 1.27 +#include "nsIPrefService.h" 1.28 + 1.29 +namespace mozilla { 1.30 + 1.31 +using namespace mozilla::gfx; 1.32 + 1.33 +NS_IMPL_ISUPPORTS(MediaEngineTabVideoSource, nsIDOMEventListener, nsITimerCallback) 1.34 + 1.35 +MediaEngineTabVideoSource::MediaEngineTabVideoSource() 1.36 +: mMonitor("MediaEngineTabVideoSource") 1.37 +{ 1.38 +} 1.39 + 1.40 +nsresult 1.41 +MediaEngineTabVideoSource::StartRunnable::Run() 1.42 +{ 1.43 + mVideoSource->Draw(); 1.44 + nsCOMPtr<nsPIDOMWindow> privateDOMWindow = do_QueryInterface(mVideoSource->mWindow); 1.45 + if (privateDOMWindow) { 1.46 + privateDOMWindow->GetChromeEventHandler()->AddEventListener(NS_LITERAL_STRING("MozAfterPaint"), mVideoSource, false); 1.47 + } else { 1.48 + mVideoSource->mTimer = do_CreateInstance(NS_TIMER_CONTRACTID); 1.49 + mVideoSource->mTimer->InitWithCallback(mVideoSource, mVideoSource->mTimePerFrame, nsITimer:: TYPE_REPEATING_SLACK); 1.50 + } 1.51 + mVideoSource->mTabSource->NotifyStreamStart(mVideoSource->mWindow); 1.52 + return NS_OK; 1.53 +} 1.54 + 1.55 +nsresult 1.56 +MediaEngineTabVideoSource::StopRunnable::Run() 1.57 +{ 1.58 + nsCOMPtr<nsPIDOMWindow> privateDOMWindow = do_QueryInterface(mVideoSource->mWindow); 1.59 + if (privateDOMWindow && mVideoSource && privateDOMWindow->GetChromeEventHandler()) { 1.60 + privateDOMWindow->GetChromeEventHandler()->RemoveEventListener(NS_LITERAL_STRING("MozAfterPaint"), mVideoSource, false); 1.61 + } 1.62 + 1.63 + if (mVideoSource->mTimer) { 1.64 + mVideoSource->mTimer->Cancel(); 1.65 + mVideoSource->mTimer = nullptr; 1.66 + } 1.67 + mVideoSource->mTabSource->NotifyStreamStop(mVideoSource->mWindow); 1.68 + return NS_OK; 1.69 +} 1.70 + 1.71 +NS_IMETHODIMP 1.72 +MediaEngineTabVideoSource::HandleEvent(nsIDOMEvent *event) { 1.73 + Draw(); 1.74 + return NS_OK; 1.75 +} 1.76 + 1.77 +NS_IMETHODIMP 1.78 +MediaEngineTabVideoSource::Notify(nsITimer*) { 1.79 + Draw(); 1.80 + return NS_OK; 1.81 +} 1.82 + 1.83 +nsresult 1.84 +MediaEngineTabVideoSource::InitRunnable::Run() 1.85 +{ 1.86 + nsresult rv; 1.87 + nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv); 1.88 + NS_ENSURE_SUCCESS(rv, rv); 1.89 + nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs); 1.90 + if (!branch) 1.91 + return NS_OK; 1.92 + branch->GetIntPref("media.tabstreaming.width", &mVideoSource->mBufW); 1.93 + branch->GetIntPref("media.tabstreaming.height", &mVideoSource->mBufH); 1.94 + branch->GetIntPref("media.tabstreaming.time_per_frame", &mVideoSource->mTimePerFrame); 1.95 + mVideoSource->mData = (unsigned char*)malloc(mVideoSource->mBufW * mVideoSource->mBufH * 4); 1.96 + 1.97 + mVideoSource->mTabSource = do_GetService(NS_TABSOURCESERVICE_CONTRACTID, &rv); 1.98 + NS_ENSURE_SUCCESS(rv, rv); 1.99 + 1.100 + nsCOMPtr<nsIDOMWindow> win; 1.101 + rv = mVideoSource->mTabSource->GetTabToStream(getter_AddRefs(win)); 1.102 + NS_ENSURE_SUCCESS(rv, rv); 1.103 + if (!win) 1.104 + return NS_OK; 1.105 + 1.106 + mVideoSource->mWindow = win; 1.107 + nsCOMPtr<nsIRunnable> start(new StartRunnable(mVideoSource)); 1.108 + start->Run(); 1.109 + return NS_OK; 1.110 +} 1.111 + 1.112 +void 1.113 +MediaEngineTabVideoSource::GetName(nsAString_internal& aName) 1.114 +{ 1.115 + aName.Assign(NS_LITERAL_STRING("&getUserMedia.videoDevice.tabShare;")); 1.116 +} 1.117 + 1.118 +void 1.119 +MediaEngineTabVideoSource::GetUUID(nsAString_internal& aUuid) 1.120 +{ 1.121 + aUuid.Assign(NS_LITERAL_STRING("uuid")); 1.122 +} 1.123 + 1.124 +nsresult 1.125 +MediaEngineTabVideoSource::Allocate(const VideoTrackConstraintsN&, 1.126 + const MediaEnginePrefs&) 1.127 +{ 1.128 + return NS_OK; 1.129 +} 1.130 + 1.131 +nsresult 1.132 +MediaEngineTabVideoSource::Deallocate() 1.133 +{ 1.134 + return NS_OK; 1.135 +} 1.136 + 1.137 +nsresult 1.138 +MediaEngineTabVideoSource::Start(mozilla::SourceMediaStream* aStream, mozilla::TrackID aID) 1.139 +{ 1.140 + nsCOMPtr<nsIRunnable> runnable; 1.141 + if (!mWindow) 1.142 + runnable = new InitRunnable(this); 1.143 + else 1.144 + runnable = new StartRunnable(this); 1.145 + NS_DispatchToMainThread(runnable); 1.146 + aStream->AddTrack(aID, USECS_PER_S, 0, new VideoSegment()); 1.147 + aStream->AdvanceKnownTracksTime(STREAM_TIME_MAX); 1.148 + 1.149 + return NS_OK; 1.150 +} 1.151 + 1.152 +nsresult 1.153 +MediaEngineTabVideoSource::Snapshot(uint32_t, nsIDOMFile**) 1.154 +{ 1.155 + return NS_OK; 1.156 +} 1.157 + 1.158 +void 1.159 +MediaEngineTabVideoSource:: 1.160 +NotifyPull(MediaStreamGraph*, SourceMediaStream* aSource, mozilla::TrackID aID, mozilla::StreamTime aDesiredTime, mozilla::TrackTicks& aLastEndTime) 1.161 +{ 1.162 + VideoSegment segment; 1.163 + MonitorAutoLock mon(mMonitor); 1.164 + 1.165 + // Note: we're not giving up mImage here 1.166 + nsRefPtr<layers::CairoImage> image = mImage; 1.167 + TrackTicks target = TimeToTicksRoundUp(USECS_PER_S, aDesiredTime); 1.168 + TrackTicks delta = target - aLastEndTime; 1.169 + if (delta > 0) { 1.170 + // nullptr images are allowed 1.171 + gfx::IntSize size = image ? image->GetSize() : IntSize(0, 0); 1.172 + segment.AppendFrame(image.forget().downcast<layers::Image>(), delta, size); 1.173 + // This can fail if either a) we haven't added the track yet, or b) 1.174 + // we've removed or finished the track. 1.175 + if (aSource->AppendToTrack(aID, &(segment))) { 1.176 + aLastEndTime = target; 1.177 + } 1.178 + } 1.179 +} 1.180 + 1.181 +void 1.182 +MediaEngineTabVideoSource::Draw() { 1.183 + 1.184 + IntSize size(mBufW, mBufH); 1.185 + 1.186 + nsresult rv; 1.187 + float scale = 1.0; 1.188 + 1.189 + nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mWindow); 1.190 + 1.191 + if (!win) { 1.192 + return; 1.193 + } 1.194 + 1.195 + // take a screenshot, as wide as possible, proportional to the destination size 1.196 + nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(win); 1.197 + if (!utils) { 1.198 + return; 1.199 + } 1.200 + 1.201 + nsCOMPtr<nsIDOMClientRect> rect; 1.202 + rv = utils->GetRootBounds(getter_AddRefs(rect)); 1.203 + NS_ENSURE_SUCCESS_VOID(rv); 1.204 + if (!rect) { 1.205 + return; 1.206 + } 1.207 + 1.208 + float left, top, width, height; 1.209 + rect->GetLeft(&left); 1.210 + rect->GetTop(&top); 1.211 + rect->GetWidth(&width); 1.212 + rect->GetHeight(&height); 1.213 + 1.214 + if (width == 0 || height == 0) { 1.215 + return; 1.216 + } 1.217 + 1.218 + int32_t srcX = left; 1.219 + int32_t srcY = top; 1.220 + int32_t srcW; 1.221 + int32_t srcH; 1.222 + 1.223 + float aspectRatio = ((float) size.width) / size.height; 1.224 + if (width / aspectRatio < height) { 1.225 + srcW = width; 1.226 + srcH = width / aspectRatio; 1.227 + } else { 1.228 + srcW = height * aspectRatio; 1.229 + srcH = height; 1.230 + } 1.231 + 1.232 + nsRefPtr<nsPresContext> presContext; 1.233 + nsIDocShell* docshell = win->GetDocShell(); 1.234 + if (docshell) { 1.235 + docshell->GetPresContext(getter_AddRefs(presContext)); 1.236 + } 1.237 + if (!presContext) { 1.238 + return; 1.239 + } 1.240 + nscolor bgColor = NS_RGB(255, 255, 255); 1.241 + nsCOMPtr<nsIPresShell> presShell = presContext->PresShell(); 1.242 + uint32_t renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING | 1.243 + nsIPresShell::RENDER_DOCUMENT_RELATIVE); 1.244 + nsRect r(nsPresContext::CSSPixelsToAppUnits(srcX / scale), 1.245 + nsPresContext::CSSPixelsToAppUnits(srcY / scale), 1.246 + nsPresContext::CSSPixelsToAppUnits(srcW / scale), 1.247 + nsPresContext::CSSPixelsToAppUnits(srcH / scale)); 1.248 + 1.249 + gfxImageFormat format = gfxImageFormat::RGB24; 1.250 + uint32_t stride = gfxASurface::FormatStrideForWidth(format, size.width); 1.251 + 1.252 + nsRefPtr<layers::ImageContainer> container = layers::LayerManager::CreateImageContainer(); 1.253 + RefPtr<DrawTarget> dt = 1.254 + Factory::CreateDrawTargetForData(BackendType::CAIRO, 1.255 + mData.rwget(), 1.256 + size, 1.257 + stride, 1.258 + SurfaceFormat::B8G8R8X8); 1.259 + if (!dt) { 1.260 + return; 1.261 + } 1.262 + nsRefPtr<gfxContext> context = new gfxContext(dt); 1.263 + gfxPoint pt(0, 0); 1.264 + context->Translate(pt); 1.265 + context->Scale(scale * size.width / srcW, scale * size.height / srcH); 1.266 + rv = presShell->RenderDocument(r, renderDocFlags, bgColor, context); 1.267 + 1.268 + NS_ENSURE_SUCCESS_VOID(rv); 1.269 + 1.270 + RefPtr<SourceSurface> surface = dt->Snapshot(); 1.271 + if (!surface) { 1.272 + return; 1.273 + } 1.274 + 1.275 + layers::CairoImage::Data cairoData; 1.276 + cairoData.mSize = size; 1.277 + cairoData.mSourceSurface = surface; 1.278 + 1.279 + nsRefPtr<layers::CairoImage> image = new layers::CairoImage(); 1.280 + 1.281 + image->SetData(cairoData); 1.282 + 1.283 + MonitorAutoLock mon(mMonitor); 1.284 + mImage = image; 1.285 +} 1.286 + 1.287 +nsresult 1.288 +MediaEngineTabVideoSource::Stop(mozilla::SourceMediaStream*, mozilla::TrackID) 1.289 +{ 1.290 + NS_DispatchToMainThread(new StopRunnable(this)); 1.291 + return NS_OK; 1.292 +} 1.293 + 1.294 +nsresult 1.295 +MediaEngineTabVideoSource::Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t) 1.296 +{ 1.297 + return NS_OK; 1.298 +} 1.299 + 1.300 +bool 1.301 +MediaEngineTabVideoSource::IsFake() 1.302 +{ 1.303 + return false; 1.304 +} 1.305 + 1.306 +}