Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(132)

Side by Side Diff: media/blink/webmediaplayer_cast_android.cc

Issue 1567123002: Support CAST+WMPI on android (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: cast tests are now passing locally Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/blink/webmediaplayer_cast_android.h"
6
7 #include "gpu/GLES2/gl2extchromium.h"
8 #include "gpu/blink/webgraphicscontext3d_impl.h"
9 #include "gpu/command_buffer/client/gles2_interface.h"
10 #include "gpu/command_buffer/common/sync_token.h"
11 #include "media/base/android/media_common_android.h"
12 #include "media/base/bind_to_current_loop.h"
13 #include "media/blink/webmediaplayer_impl.h"
14 #include "media/blink/webmediaplayer_params.h"
15 #include "third_party/WebKit/public/platform/WebMediaPlayerClient.h"
16 #include "third_party/WebKit/public/web/WebDocument.h"
17 #include "third_party/WebKit/public/web/WebLocalFrame.h"
18 #include "third_party/skia/include/core/SkCanvas.h"
19 #include "third_party/skia/include/core/SkPaint.h"
20 #include "third_party/skia/include/core/SkTypeface.h"
21 #include "third_party/skia/include/gpu/GrContext.h"
22 #include "third_party/skia/include/gpu/SkGrPixelRef.h"
23
24 #if defined(OS_ANDROID)
DaleCurtis 2016/01/15 20:07:45 Delete after build file fixes.
hubbe 2016/01/15 20:35:47 Done.
25
26 using gpu::gles2::GLES2Interface;
27
28 namespace media {
29
30 namespace {
31 // File-static function is to allow it to run even after WMPI is deleted.
32 void OnReleaseTexture(const WebMediaPlayerCast::GLContextCB& context_3d_cb,
33 GLuint texture_id,
34 const gpu::SyncToken& sync_token) {
35 GLES2Interface* gl = context_3d_cb.Run();
36 if (!gl)
37 return;
38
39 gl->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
40 gl->DeleteTextures(1, &texture_id);
41 // Flush to ensure that the texture gets deleted in a timely fashion.
42 gl->ShallowFlushCHROMIUM();
43 }
44
45 GLES2Interface* GLCBShim(
46 const WebMediaPlayerParams::Context3DCB& context_3d_cb) {
47 return context_3d_cb.Run().gl;
48 }
49
50 } // namespace
51
52 scoped_refptr<VideoFrame> WebMediaPlayerCast::MakeTextFrameForCast(
53 const std::string& remote_playback_message,
54 gfx::Size canvas_size,
55 gfx::Size natural_size,
56 const WebMediaPlayerCast::GLContextCB& context_3d_cb) {
57 SkBitmap bitmap;
58 bitmap.allocN32Pixels(canvas_size.width(), canvas_size.height());
59
60 // Create the canvas and draw the "Casting to <Chromecast>" text on it.
61 SkCanvas canvas(bitmap);
62 canvas.drawColor(SK_ColorBLACK);
63
64 const SkScalar kTextSize(40);
65 const SkScalar kMinPadding(40);
66
67 SkPaint paint;
68 paint.setAntiAlias(true);
69 paint.setFilterQuality(kHigh_SkFilterQuality);
70 paint.setColor(SK_ColorWHITE);
71 paint.setTypeface(SkTypeface::CreateFromName("sans", SkTypeface::kBold));
72 paint.setTextSize(kTextSize);
73
74 // Calculate the vertical margin from the top
75 SkPaint::FontMetrics font_metrics;
76 paint.getFontMetrics(&font_metrics);
77 SkScalar sk_vertical_margin = kMinPadding - font_metrics.fAscent;
78
79 // Measure the width of the entire text to display
80 size_t display_text_width = paint.measureText(remote_playback_message.c_str(),
81 remote_playback_message.size());
82 std::string display_text(remote_playback_message);
83
84 if (display_text_width + (kMinPadding * 2) > canvas_size.width()) {
85 // The text is too long to fit in one line, truncate it and append ellipsis
86 // to the end.
87
88 // First, figure out how much of the canvas the '...' will take up.
89 const std::string kTruncationEllipsis("\xE2\x80\xA6");
90 SkScalar sk_ellipse_width = paint.measureText(kTruncationEllipsis.c_str(),
91 kTruncationEllipsis.size());
92
93 // Then calculate how much of the text can be drawn with the '...' appended
94 // to the end of the string.
95 SkScalar sk_max_original_text_width(canvas_size.width() -
96 (kMinPadding * 2) - sk_ellipse_width);
97 size_t sk_max_original_text_length = paint.breakText(
98 remote_playback_message.c_str(), remote_playback_message.size(),
99 sk_max_original_text_width);
100
101 // Remove the part of the string that doesn't fit and append '...'.
102 display_text.erase(
103 sk_max_original_text_length,
104 remote_playback_message.size() - sk_max_original_text_length);
105 display_text.append(kTruncationEllipsis);
106 display_text_width =
107 paint.measureText(display_text.c_str(), display_text.size());
108 }
109
110 // Center the text horizontally.
111 SkScalar sk_horizontal_margin =
112 (canvas_size.width() - display_text_width) / 2.0;
113 canvas.drawText(display_text.c_str(), display_text.size(),
114 sk_horizontal_margin, sk_vertical_margin, paint);
115
116 GLES2Interface* gl = context_3d_cb.Run();
117
118 // GPU Process crashed.
119 if (!gl)
120 return nullptr;
121 GLuint remote_playback_texture_id = 0;
122 gl->GenTextures(1, &remote_playback_texture_id);
123 GLuint texture_target = GL_TEXTURE_2D;
124 gl->BindTexture(texture_target, remote_playback_texture_id);
125 gl->TexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
126 gl->TexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
127 gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
128 gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
129
130 {
131 SkAutoLockPixels lock(bitmap);
132 gl->TexImage2D(texture_target, 0 /* level */, GL_RGBA /* internalformat */,
133 bitmap.width(), bitmap.height(), 0 /* border */,
134 GL_RGBA /* format */, GL_UNSIGNED_BYTE /* type */,
135 bitmap.getPixels());
136 }
137
138 gpu::Mailbox texture_mailbox;
139 gl->GenMailboxCHROMIUM(texture_mailbox.name);
140 gl->ProduceTextureCHROMIUM(texture_target, texture_mailbox.name);
141 gl->Flush();
142 gpu::SyncToken texture_mailbox_sync_token(gl->InsertSyncPointCHROMIUM());
143
144 return VideoFrame::WrapNativeTexture(
145 media::PIXEL_FORMAT_ARGB,
146 gpu::MailboxHolder(texture_mailbox, texture_mailbox_sync_token,
147 texture_target),
148 media::BindToCurrentLoop(base::Bind(&OnReleaseTexture, context_3d_cb,
149 remote_playback_texture_id)),
150 canvas_size /* coded_size */, gfx::Rect(canvas_size) /* visible_rect */,
151 natural_size /* natural_size */, base::TimeDelta() /* timestamp */);
152 }
153
154 WebMediaPlayerCast::WebMediaPlayerCast(
155 WebMediaPlayerImpl* impl,
156 blink::WebMediaPlayerClient* client,
157 const WebMediaPlayerParams::Context3DCB& context_3d_cb,
158 base::WeakPtr<WebMediaPlayerDelegate> delegate)
159 : webmediaplayer_(impl),
160 client_(client),
161 context_3d_cb_(context_3d_cb),
162 delegate_(delegate) {}
163
164 WebMediaPlayerCast::~WebMediaPlayerCast() {
165 if (player_manager_) {
166 if (is_player_initialized_)
167 player_manager_->DestroyPlayer(player_id_);
168
169 player_manager_->UnregisterMediaPlayer(player_id_);
170 }
171 }
172
173 void WebMediaPlayerCast::Initialize(const GURL& url,
174 blink::WebLocalFrame* frame) {
175 player_manager_->Initialize(MEDIA_PLAYER_TYPE_REMOTE_ONLY, player_id_, url,
176 frame->document().firstPartyForCookies(), 0,
177 frame->document().url(), true);
178 is_player_initialized_ = true;
179 }
180
181 void WebMediaPlayerCast::SetMediaPlayerManager(
182 RendererMediaPlayerManagerInterface* media_player_manager) {
183 player_manager_ = media_player_manager;
184 player_id_ = player_manager_->RegisterMediaPlayer(this);
185 }
186
187 void WebMediaPlayerCast::requestRemotePlayback() {
188 player_manager_->Seek(player_id_, base::TimeDelta::FromSecondsD(
189 webmediaplayer_->currentTime()));
190 player_manager_->RequestRemotePlayback(player_id_);
191 }
192
193 void WebMediaPlayerCast::requestRemotePlaybackControl() {
194 player_manager_->RequestRemotePlaybackControl(player_id_);
195 }
196
197 void WebMediaPlayerCast::OnMediaMetadataChanged(base::TimeDelta duration,
198 int width,
199 int height,
200 bool success) {}
201
202 void WebMediaPlayerCast::OnPlaybackComplete() {
203 DVLOG(1) << __FUNCTION__;
204 webmediaplayer_->OnRemotePlaybackEnded();
205 }
206
207 void WebMediaPlayerCast::OnBufferingUpdate(int percentage) {
208 DVLOG(1) << __FUNCTION__;
209 }
210
211 void WebMediaPlayerCast::OnSeekRequest(const base::TimeDelta& time_to_seek) {
212 DVLOG(1) << __FUNCTION__;
213 client_->requestSeek(time_to_seek.InSecondsF());
214 }
215
216 void WebMediaPlayerCast::OnSeekComplete(const base::TimeDelta& current_time) {
217 DVLOG(1) << __FUNCTION__;
218 remote_time_at_ = base::TimeTicks::Now();
219 remote_time_ = current_time;
220 webmediaplayer_->OnPipelineSeeked(true, PIPELINE_OK);
221 }
222
223 void WebMediaPlayerCast::OnMediaError(int error_type) {
224 DVLOG(1) << __FUNCTION__;
225 }
226
227 void WebMediaPlayerCast::OnVideoSizeChanged(int width, int height) {
228 DVLOG(1) << __FUNCTION__;
229 }
230
231 void WebMediaPlayerCast::OnTimeUpdate(base::TimeDelta current_timestamp,
232 base::TimeTicks current_time_ticks) {
233 DVLOG(1) << __FUNCTION__ << " " << current_timestamp.InSecondsF();
234 remote_time_at_ = current_time_ticks;
235 remote_time_ = current_timestamp;
236 }
237
238 void WebMediaPlayerCast::OnPlayerReleased() {
239 DVLOG(1) << __FUNCTION__;
240 }
241
242 void WebMediaPlayerCast::OnConnectedToRemoteDevice(
243 const std::string& remote_playback_message) {
244 DVLOG(1) << __FUNCTION__;
245 remote_time_ = base::TimeDelta::FromSecondsD(webmediaplayer_->currentTime());
246 is_remote_ = true;
247 initializing_ = true;
248 paused_ = false;
249 if (delegate_)
250 delegate_->DidPlay(webmediaplayer_);
DaleCurtis 2016/01/15 20:07:45 All the delegate calls in this file happen with is
hubbe 2016/01/15 20:35:47 Yes, but you wanted the delegate to make that dete
251 client_->playbackStateChanged();
252
253 remote_playback_message_ = remote_playback_message;
254 webmediaplayer_->SuspendForRemote();
255 client_->connectedToRemoteDevice();
256 }
257
258 double WebMediaPlayerCast::currentTime() const {
259 base::TimeDelta ret = remote_time_;
260 if (!paused_ && !initializing_) {
261 ret += base::TimeTicks::Now() - remote_time_at_;
262 }
263 return ret.InSecondsF();
264 }
265
266 void WebMediaPlayerCast::play() {
267 if (!paused_)
268 return;
269
270 player_manager_->Start(player_id_);
271 remote_time_at_ = base::TimeTicks::Now();
272 paused_ = false;
273 if (delegate_)
274 delegate_->DidPlay(webmediaplayer_);
275 }
276
277 void WebMediaPlayerCast::pause() {
278 player_manager_->Pause(player_id_, true);
279 }
280
281 void WebMediaPlayerCast::seek(base::TimeDelta t) {
282 should_notify_time_changed_ = true;
283 player_manager_->Seek(player_id_, t);
284 }
285
286 void WebMediaPlayerCast::OnDisconnectedFromRemoteDevice() {
287 DVLOG(1) << __FUNCTION__;
288 if (!paused_) {
289 paused_ = true;
290 if (delegate_)
291 delegate_->DidPause(webmediaplayer_);
292 }
293 is_remote_ = false;
294 double t = currentTime();
295 if (t + media::kTimeUpdateInterval * 2 / 1000 > webmediaplayer_->duration()) {
296 t = webmediaplayer_->duration();
297 }
298 webmediaplayer_->OnDisconnectedFromRemoteDevice(t);
299 }
300
301 void WebMediaPlayerCast::OnDidExitFullscreen() {
302 DVLOG(1) << __FUNCTION__;
303 }
304
305 void WebMediaPlayerCast::OnMediaPlayerPlay() {
306 DVLOG(1) << __FUNCTION__ << " is_remote_ = " << is_remote_;
307 initializing_ = false;
308 if (is_remote_ && paused_) {
309 paused_ = false;
310 if (paused_)
311 delegate_->DidPlay(webmediaplayer_);
312 remote_time_at_ = base::TimeTicks::Now();
313 client_->playbackStateChanged();
314 }
315 // Blink expects a timeChanged() in response to a seek().
316 if (should_notify_time_changed_)
317 client_->timeChanged();
318 }
319
320 void WebMediaPlayerCast::OnMediaPlayerPause() {
321 DVLOG(1) << __FUNCTION__ << " is_remote_ = " << is_remote_;
322 if (is_remote_ && !paused_) {
323 paused_ = true;
324 if (delegate_)
325 delegate_->DidPause(webmediaplayer_);
326 client_->playbackStateChanged();
327 }
328 }
329
330 void WebMediaPlayerCast::OnRemoteRouteAvailabilityChanged(
331 bool routes_available) {
332 DVLOG(1) << __FUNCTION__;
333 client_->remoteRouteAvailabilityChanged(routes_available);
334 }
335
336 void WebMediaPlayerCast::SuspendAndReleaseResources() {}
337 void WebMediaPlayerCast::OnWaitingForDecryptionKey() {}
338
339 bool WebMediaPlayerCast::hasVideo() const {
340 return true;
341 }
342
343 bool WebMediaPlayerCast::paused() const {
344 return paused_;
345 }
346
347 #if defined(VIDEO_HOLE)
348 bool WebMediaPlayerCast::UpdateBoundaryRectangle() {
349 return false;
350 }
351
352 const gfx::RectF WebMediaPlayerCast::GetBoundaryRectangle() {
353 return gfx::RectF();
354 }
355 #endif // defined(VIDEO_HOLE)
356
357 scoped_refptr<VideoFrame> WebMediaPlayerCast::GetCastingBanner() {
358 DVLOG(1) << __FUNCTION__;
359
360 // TODO(johnme): Should redraw this frame if the layer bounds change; but
361 // there seems no easy way to listen for the layer resizing (as opposed to
362 // OnVideoSizeChanged, which is when the frame sizes of the video file
363 // change). Perhaps have to poll (on main thread of course)?
364 gfx::Size canvas_size = webmediaplayer_->GetCanvasSize();
365 if (!canvas_size.width())
366 return nullptr;
367
368 return MakeTextFrameForCast(remote_playback_message_, canvas_size,
369 webmediaplayer_->naturalSize(),
370 base::Bind(&GLCBShim, context_3d_cb_));
371 }
372
373 } // namespace media
374
375 #endif // OS_ANDROID
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698