Cef是没有对外提供截图接口的,但是Content层已经提供了,所以需要为Cef增加接口把截图能力暴露出去。
Content层
最开始没发现
RenderWidgetHostView
已经有暴露接口了,所以寻思着直接给Content增加接口,然后在cef中调用,由于实际没有采用此方法,所以只列出方法的大致实现,WebContents
接口的修改就不列出了。void WebContentsImpl::CaptureSnapshot( const gfx::Rect& snap_rect, CaptureSnapshotCallback callback) { if (GetMainFrame() == nullptr || GetMainFrame()->GetRenderWidgetHost() == nullptr) { std::move(callback).Run(SkBitmap()); return; } GetMainFrame()->GetRenderWidgetHost()->GetSnapshotFromBrowser( base::BindOnce([](const gfx::Rect& snap_rect, CaptureSnapshotCallback callback, const gfx::Image& image) mutable { SkBitmap cropped = SkBitmapOperations::CreateTiledBitmap( image.AsBitmap(), snap_rect.x(), snap_rect.y(), snap_rect.width(), snap_rect.height()); std::move(callback).Run(cropped); }, snap_rect, callback), true); }
💢 上面代码列出了基本思路,实际并未采用该方案。
在实现过程中,翻看了下
RenderWidgetHostView
,无意中发现,这里已经提供了完善的对外接口还很贴心的说明了这个接口只用于截取单张图片,如果获取视频流,要使用
CreateVideoCapturer
接口。新版本的Cef提供了
Alloy
和Chrome
两种运行时,统一从CefBrowserHostBase
类继承,所以我们可以直接把接口加到该基类,这样两种运行时就都有截图功能了。void CefBrowserHostBase::CaptureSnapshot(const CefRect& capture_rect, CefRefPtr<CefSnapshotCallback> callback) { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostBase::CaptureSnapshot, this, capture_rect, callback)); return; } auto web_contents = GetWebContents(); if (!web_contents) { LOG(ERROR) << "web_contents is null"; return; } auto view = web_contents->GetRenderWidgetHostView(); if (view) { gfx::Rect src_rect(capture_rect.x, capture_rect.y, capture_rect.width, capture_rect.height); view->CopyFromSurface(src_rect, gfx::Size(), base::BindOnce( [](CefRefPtr<CefSnapshotCallback> callback, const SkBitmap& bitmap) { CefRefPtr<CefImageImpl> img_impl = new CefImageImpl( gfx::ImageSkia::CreateFrom1xBitmap(bitmap)); callback->OnCaptured(img_impl.get()); }, callback)); } else { callback->OnCaptured(CefRefPtr<CefImage>(nullptr)); LOG(ERROR) << "no GetRenderWidgetHostView"; } }
这样Content层功能算是完成了,接着在Cef层增加对外接口。