Fri, 16 Jan 2015 04:50:19 +0100
Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "MediaEngineTabVideoSource.h"
7 #include "mozilla/gfx/2D.h"
8 #include "mozilla/RefPtr.h"
9 #include "nsGlobalWindow.h"
10 #include "nsDOMWindowUtils.h"
11 #include "nsIDOMClientRect.h"
12 #include "nsIDocShell.h"
13 #include "nsIPresShell.h"
14 #include "nsPresContext.h"
15 #include "gfxContext.h"
16 #include "gfx2DGlue.h"
17 #include "ImageContainer.h"
18 #include "Layers.h"
19 #include "nsIInterfaceRequestorUtils.h"
20 #include "nsIDOMDocument.h"
21 #include "nsITabSource.h"
22 #include "VideoUtils.h"
23 #include "nsServiceManagerUtils.h"
24 #include "nsIPrefService.h"
26 namespace mozilla {
28 using namespace mozilla::gfx;
30 NS_IMPL_ISUPPORTS(MediaEngineTabVideoSource, nsIDOMEventListener, nsITimerCallback)
32 MediaEngineTabVideoSource::MediaEngineTabVideoSource()
33 : mMonitor("MediaEngineTabVideoSource")
34 {
35 }
37 nsresult
38 MediaEngineTabVideoSource::StartRunnable::Run()
39 {
40 mVideoSource->Draw();
41 nsCOMPtr<nsPIDOMWindow> privateDOMWindow = do_QueryInterface(mVideoSource->mWindow);
42 if (privateDOMWindow) {
43 privateDOMWindow->GetChromeEventHandler()->AddEventListener(NS_LITERAL_STRING("MozAfterPaint"), mVideoSource, false);
44 } else {
45 mVideoSource->mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
46 mVideoSource->mTimer->InitWithCallback(mVideoSource, mVideoSource->mTimePerFrame, nsITimer:: TYPE_REPEATING_SLACK);
47 }
48 mVideoSource->mTabSource->NotifyStreamStart(mVideoSource->mWindow);
49 return NS_OK;
50 }
52 nsresult
53 MediaEngineTabVideoSource::StopRunnable::Run()
54 {
55 nsCOMPtr<nsPIDOMWindow> privateDOMWindow = do_QueryInterface(mVideoSource->mWindow);
56 if (privateDOMWindow && mVideoSource && privateDOMWindow->GetChromeEventHandler()) {
57 privateDOMWindow->GetChromeEventHandler()->RemoveEventListener(NS_LITERAL_STRING("MozAfterPaint"), mVideoSource, false);
58 }
60 if (mVideoSource->mTimer) {
61 mVideoSource->mTimer->Cancel();
62 mVideoSource->mTimer = nullptr;
63 }
64 mVideoSource->mTabSource->NotifyStreamStop(mVideoSource->mWindow);
65 return NS_OK;
66 }
68 NS_IMETHODIMP
69 MediaEngineTabVideoSource::HandleEvent(nsIDOMEvent *event) {
70 Draw();
71 return NS_OK;
72 }
74 NS_IMETHODIMP
75 MediaEngineTabVideoSource::Notify(nsITimer*) {
76 Draw();
77 return NS_OK;
78 }
80 nsresult
81 MediaEngineTabVideoSource::InitRunnable::Run()
82 {
83 nsresult rv;
84 nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
85 NS_ENSURE_SUCCESS(rv, rv);
86 nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
87 if (!branch)
88 return NS_OK;
89 branch->GetIntPref("media.tabstreaming.width", &mVideoSource->mBufW);
90 branch->GetIntPref("media.tabstreaming.height", &mVideoSource->mBufH);
91 branch->GetIntPref("media.tabstreaming.time_per_frame", &mVideoSource->mTimePerFrame);
92 mVideoSource->mData = (unsigned char*)malloc(mVideoSource->mBufW * mVideoSource->mBufH * 4);
94 mVideoSource->mTabSource = do_GetService(NS_TABSOURCESERVICE_CONTRACTID, &rv);
95 NS_ENSURE_SUCCESS(rv, rv);
97 nsCOMPtr<nsIDOMWindow> win;
98 rv = mVideoSource->mTabSource->GetTabToStream(getter_AddRefs(win));
99 NS_ENSURE_SUCCESS(rv, rv);
100 if (!win)
101 return NS_OK;
103 mVideoSource->mWindow = win;
104 nsCOMPtr<nsIRunnable> start(new StartRunnable(mVideoSource));
105 start->Run();
106 return NS_OK;
107 }
109 void
110 MediaEngineTabVideoSource::GetName(nsAString_internal& aName)
111 {
112 aName.Assign(NS_LITERAL_STRING("&getUserMedia.videoDevice.tabShare;"));
113 }
115 void
116 MediaEngineTabVideoSource::GetUUID(nsAString_internal& aUuid)
117 {
118 aUuid.Assign(NS_LITERAL_STRING("uuid"));
119 }
121 nsresult
122 MediaEngineTabVideoSource::Allocate(const VideoTrackConstraintsN&,
123 const MediaEnginePrefs&)
124 {
125 return NS_OK;
126 }
128 nsresult
129 MediaEngineTabVideoSource::Deallocate()
130 {
131 return NS_OK;
132 }
134 nsresult
135 MediaEngineTabVideoSource::Start(mozilla::SourceMediaStream* aStream, mozilla::TrackID aID)
136 {
137 nsCOMPtr<nsIRunnable> runnable;
138 if (!mWindow)
139 runnable = new InitRunnable(this);
140 else
141 runnable = new StartRunnable(this);
142 NS_DispatchToMainThread(runnable);
143 aStream->AddTrack(aID, USECS_PER_S, 0, new VideoSegment());
144 aStream->AdvanceKnownTracksTime(STREAM_TIME_MAX);
146 return NS_OK;
147 }
149 nsresult
150 MediaEngineTabVideoSource::Snapshot(uint32_t, nsIDOMFile**)
151 {
152 return NS_OK;
153 }
155 void
156 MediaEngineTabVideoSource::
157 NotifyPull(MediaStreamGraph*, SourceMediaStream* aSource, mozilla::TrackID aID, mozilla::StreamTime aDesiredTime, mozilla::TrackTicks& aLastEndTime)
158 {
159 VideoSegment segment;
160 MonitorAutoLock mon(mMonitor);
162 // Note: we're not giving up mImage here
163 nsRefPtr<layers::CairoImage> image = mImage;
164 TrackTicks target = TimeToTicksRoundUp(USECS_PER_S, aDesiredTime);
165 TrackTicks delta = target - aLastEndTime;
166 if (delta > 0) {
167 // nullptr images are allowed
168 gfx::IntSize size = image ? image->GetSize() : IntSize(0, 0);
169 segment.AppendFrame(image.forget().downcast<layers::Image>(), delta, size);
170 // This can fail if either a) we haven't added the track yet, or b)
171 // we've removed or finished the track.
172 if (aSource->AppendToTrack(aID, &(segment))) {
173 aLastEndTime = target;
174 }
175 }
176 }
178 void
179 MediaEngineTabVideoSource::Draw() {
181 IntSize size(mBufW, mBufH);
183 nsresult rv;
184 float scale = 1.0;
186 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mWindow);
188 if (!win) {
189 return;
190 }
192 // take a screenshot, as wide as possible, proportional to the destination size
193 nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(win);
194 if (!utils) {
195 return;
196 }
198 nsCOMPtr<nsIDOMClientRect> rect;
199 rv = utils->GetRootBounds(getter_AddRefs(rect));
200 NS_ENSURE_SUCCESS_VOID(rv);
201 if (!rect) {
202 return;
203 }
205 float left, top, width, height;
206 rect->GetLeft(&left);
207 rect->GetTop(&top);
208 rect->GetWidth(&width);
209 rect->GetHeight(&height);
211 if (width == 0 || height == 0) {
212 return;
213 }
215 int32_t srcX = left;
216 int32_t srcY = top;
217 int32_t srcW;
218 int32_t srcH;
220 float aspectRatio = ((float) size.width) / size.height;
221 if (width / aspectRatio < height) {
222 srcW = width;
223 srcH = width / aspectRatio;
224 } else {
225 srcW = height * aspectRatio;
226 srcH = height;
227 }
229 nsRefPtr<nsPresContext> presContext;
230 nsIDocShell* docshell = win->GetDocShell();
231 if (docshell) {
232 docshell->GetPresContext(getter_AddRefs(presContext));
233 }
234 if (!presContext) {
235 return;
236 }
237 nscolor bgColor = NS_RGB(255, 255, 255);
238 nsCOMPtr<nsIPresShell> presShell = presContext->PresShell();
239 uint32_t renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
240 nsIPresShell::RENDER_DOCUMENT_RELATIVE);
241 nsRect r(nsPresContext::CSSPixelsToAppUnits(srcX / scale),
242 nsPresContext::CSSPixelsToAppUnits(srcY / scale),
243 nsPresContext::CSSPixelsToAppUnits(srcW / scale),
244 nsPresContext::CSSPixelsToAppUnits(srcH / scale));
246 gfxImageFormat format = gfxImageFormat::RGB24;
247 uint32_t stride = gfxASurface::FormatStrideForWidth(format, size.width);
249 nsRefPtr<layers::ImageContainer> container = layers::LayerManager::CreateImageContainer();
250 RefPtr<DrawTarget> dt =
251 Factory::CreateDrawTargetForData(BackendType::CAIRO,
252 mData.rwget(),
253 size,
254 stride,
255 SurfaceFormat::B8G8R8X8);
256 if (!dt) {
257 return;
258 }
259 nsRefPtr<gfxContext> context = new gfxContext(dt);
260 gfxPoint pt(0, 0);
261 context->Translate(pt);
262 context->Scale(scale * size.width / srcW, scale * size.height / srcH);
263 rv = presShell->RenderDocument(r, renderDocFlags, bgColor, context);
265 NS_ENSURE_SUCCESS_VOID(rv);
267 RefPtr<SourceSurface> surface = dt->Snapshot();
268 if (!surface) {
269 return;
270 }
272 layers::CairoImage::Data cairoData;
273 cairoData.mSize = size;
274 cairoData.mSourceSurface = surface;
276 nsRefPtr<layers::CairoImage> image = new layers::CairoImage();
278 image->SetData(cairoData);
280 MonitorAutoLock mon(mMonitor);
281 mImage = image;
282 }
284 nsresult
285 MediaEngineTabVideoSource::Stop(mozilla::SourceMediaStream*, mozilla::TrackID)
286 {
287 NS_DispatchToMainThread(new StopRunnable(this));
288 return NS_OK;
289 }
291 nsresult
292 MediaEngineTabVideoSource::Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t)
293 {
294 return NS_OK;
295 }
297 bool
298 MediaEngineTabVideoSource::IsFake()
299 {
300 return false;
301 }
303 }