Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 | |
| OLD | NEW |