|
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/. */ |
|
4 |
|
5 #include "MediaEngineTabVideoSource.h" |
|
6 |
|
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" |
|
25 |
|
26 namespace mozilla { |
|
27 |
|
28 using namespace mozilla::gfx; |
|
29 |
|
30 NS_IMPL_ISUPPORTS(MediaEngineTabVideoSource, nsIDOMEventListener, nsITimerCallback) |
|
31 |
|
32 MediaEngineTabVideoSource::MediaEngineTabVideoSource() |
|
33 : mMonitor("MediaEngineTabVideoSource") |
|
34 { |
|
35 } |
|
36 |
|
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 } |
|
51 |
|
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 } |
|
59 |
|
60 if (mVideoSource->mTimer) { |
|
61 mVideoSource->mTimer->Cancel(); |
|
62 mVideoSource->mTimer = nullptr; |
|
63 } |
|
64 mVideoSource->mTabSource->NotifyStreamStop(mVideoSource->mWindow); |
|
65 return NS_OK; |
|
66 } |
|
67 |
|
68 NS_IMETHODIMP |
|
69 MediaEngineTabVideoSource::HandleEvent(nsIDOMEvent *event) { |
|
70 Draw(); |
|
71 return NS_OK; |
|
72 } |
|
73 |
|
74 NS_IMETHODIMP |
|
75 MediaEngineTabVideoSource::Notify(nsITimer*) { |
|
76 Draw(); |
|
77 return NS_OK; |
|
78 } |
|
79 |
|
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); |
|
93 |
|
94 mVideoSource->mTabSource = do_GetService(NS_TABSOURCESERVICE_CONTRACTID, &rv); |
|
95 NS_ENSURE_SUCCESS(rv, rv); |
|
96 |
|
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; |
|
102 |
|
103 mVideoSource->mWindow = win; |
|
104 nsCOMPtr<nsIRunnable> start(new StartRunnable(mVideoSource)); |
|
105 start->Run(); |
|
106 return NS_OK; |
|
107 } |
|
108 |
|
109 void |
|
110 MediaEngineTabVideoSource::GetName(nsAString_internal& aName) |
|
111 { |
|
112 aName.Assign(NS_LITERAL_STRING("&getUserMedia.videoDevice.tabShare;")); |
|
113 } |
|
114 |
|
115 void |
|
116 MediaEngineTabVideoSource::GetUUID(nsAString_internal& aUuid) |
|
117 { |
|
118 aUuid.Assign(NS_LITERAL_STRING("uuid")); |
|
119 } |
|
120 |
|
121 nsresult |
|
122 MediaEngineTabVideoSource::Allocate(const VideoTrackConstraintsN&, |
|
123 const MediaEnginePrefs&) |
|
124 { |
|
125 return NS_OK; |
|
126 } |
|
127 |
|
128 nsresult |
|
129 MediaEngineTabVideoSource::Deallocate() |
|
130 { |
|
131 return NS_OK; |
|
132 } |
|
133 |
|
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); |
|
145 |
|
146 return NS_OK; |
|
147 } |
|
148 |
|
149 nsresult |
|
150 MediaEngineTabVideoSource::Snapshot(uint32_t, nsIDOMFile**) |
|
151 { |
|
152 return NS_OK; |
|
153 } |
|
154 |
|
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); |
|
161 |
|
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 } |
|
177 |
|
178 void |
|
179 MediaEngineTabVideoSource::Draw() { |
|
180 |
|
181 IntSize size(mBufW, mBufH); |
|
182 |
|
183 nsresult rv; |
|
184 float scale = 1.0; |
|
185 |
|
186 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mWindow); |
|
187 |
|
188 if (!win) { |
|
189 return; |
|
190 } |
|
191 |
|
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 } |
|
197 |
|
198 nsCOMPtr<nsIDOMClientRect> rect; |
|
199 rv = utils->GetRootBounds(getter_AddRefs(rect)); |
|
200 NS_ENSURE_SUCCESS_VOID(rv); |
|
201 if (!rect) { |
|
202 return; |
|
203 } |
|
204 |
|
205 float left, top, width, height; |
|
206 rect->GetLeft(&left); |
|
207 rect->GetTop(&top); |
|
208 rect->GetWidth(&width); |
|
209 rect->GetHeight(&height); |
|
210 |
|
211 if (width == 0 || height == 0) { |
|
212 return; |
|
213 } |
|
214 |
|
215 int32_t srcX = left; |
|
216 int32_t srcY = top; |
|
217 int32_t srcW; |
|
218 int32_t srcH; |
|
219 |
|
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 } |
|
228 |
|
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)); |
|
245 |
|
246 gfxImageFormat format = gfxImageFormat::RGB24; |
|
247 uint32_t stride = gfxASurface::FormatStrideForWidth(format, size.width); |
|
248 |
|
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); |
|
264 |
|
265 NS_ENSURE_SUCCESS_VOID(rv); |
|
266 |
|
267 RefPtr<SourceSurface> surface = dt->Snapshot(); |
|
268 if (!surface) { |
|
269 return; |
|
270 } |
|
271 |
|
272 layers::CairoImage::Data cairoData; |
|
273 cairoData.mSize = size; |
|
274 cairoData.mSourceSurface = surface; |
|
275 |
|
276 nsRefPtr<layers::CairoImage> image = new layers::CairoImage(); |
|
277 |
|
278 image->SetData(cairoData); |
|
279 |
|
280 MonitorAutoLock mon(mMonitor); |
|
281 mImage = image; |
|
282 } |
|
283 |
|
284 nsresult |
|
285 MediaEngineTabVideoSource::Stop(mozilla::SourceMediaStream*, mozilla::TrackID) |
|
286 { |
|
287 NS_DispatchToMainThread(new StopRunnable(this)); |
|
288 return NS_OK; |
|
289 } |
|
290 |
|
291 nsresult |
|
292 MediaEngineTabVideoSource::Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t) |
|
293 { |
|
294 return NS_OK; |
|
295 } |
|
296 |
|
297 bool |
|
298 MediaEngineTabVideoSource::IsFake() |
|
299 { |
|
300 return false; |
|
301 } |
|
302 |
|
303 } |