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,无意中发现,这里已经提供了完善的对外接口
notion image
还很贴心的说明了这个接口只用于截取单张图片,如果获取视频流,要使用CreateVideoCapturer接口。
新版本的Cef提供了AlloyChrome两种运行时,统一从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层增加对外接口。

Cef层

cef_browser.h文件中的CefBrowserHost接口增加截图接口
/// // Capture snapshot. /// /*--cef()--*/ virtual void CaptureSnapshot(const CefRect& capture_rect, CefRefPtr<CefSnapshotCallback> callback) = 0;
定义回调接口
/// // Callback interface for CefBrowserHost::CaptureSnapshot. The methods of this class // will be called on the browser process UI thread. /// /*--cef(source=client)--*/ class CefSnapshotCallback : public virtual CefBaseRefCounted { public: /// // Method that will be executed when the capture operation has completed. |image| // is the output path. /// /*--cef()--*/ virtual void OnCaptured(CefRefPtr<CefImage> image) = 0; };
💡
方法或者类名上面的/*--cef()--*/不能缺少,否则会导致自动生成的代码中不包含该接口。
类名上的source种类,可以有libraryclient两种,library表示此类是cef已实现的代码,并对外公开接口;client表示此类是一个回调接口类。该参数会影响cpptoc以及ctocpp的代码生成。其他更详细信息可以参考cef\tools\translator.README.txt文件。
修改完成后,执行cef\tools\translator.bat即可(需要保证depot_tools在环境变量中)。
💡
在Windows系统下执行translator.bat脚本,生成的源代码文件是以\r\n结尾的,而一般git仓库都会要求以\n结尾,可以修改cef\tools\file_util.py中的write_file函数,强制以\n结尾。
notion image

结尾

至此,为Cef提供截图能力的需求已经完成。上层只需要继承并实现CefSnapshotCallback类即可在OnCaptured函数中接收到图像数据。
如果需要获取视频流,可以参考video_consumer_osr.cc文件的实现,通过文件名即可知道osr模式对于Content来说只是个视频数据的消费者。
badge