linux CEF 离屏渲染详解

Linux CEF 离屏渲染详解

1.下载linux版本的cef源码

下载地址:https://cef-builds.spotifycdn.com/index.html 下载对应的源码



   tar -xjvf 文件名.tar.bz2 // 解压到当前文件夹

2. 编译cef

    1、自行安装cmake  make gcc g++ gdb等 工具
    2、首先 cmake 编译 cef文件夹内的CMakeLists.txt 文件生成 MakeFile 文件
    3、再用make 编译 MakeFile 文件
    若出现某些lib找不到的问题 可以 通过 apt-get install xxx.dev 来进行安装或者yum install xxxx 安装解决, 可自行百度,然后重新继续编译
    编译 首先会编译libcef_dll_wrapper这个文件夹 主要生成libcef_dll_wrapper.a文件
         自动编译/tests/ceftests 文件夹
         自动编译     /tests/cefsimples/
         自动编译    /tests/cefclient/  我在编译这个文件夹时 就出现include<gtk/gtk.h>找不到  安装了gtk也不行 ,也不太清楚, 不过离屏渲染只需要cefsimple这个例子就能实现, 就没再深究。
    4.完成编译后,会在libcef_dll_wrapper 文件夹内生成 libcef_dll_wrapper.a 文件 , /tests/cefsimple/ReleaseorDebug 生成CefSimple 可执行文件, 可以尝试执行cefsimple 文件 进入当前文件夹内./cefsimple  这时会发现出错了,  经历痛苦的寻找发现需要加上--no-sandbox 也就是 ./cefsimple --no-sandbox  没有报错了可以出现一个窗体了但是加载不出来网页,是因为源码里的网页是google.com  没有科学上网是不行的,所以我们要改源码,将simple_app.cc 文件里的 url 改为 url = "http://www.baidu.com";再次执行就能显示出百度的简单网页窗口了。

3. 自己实现简单的离屏渲染

1.在linux 下编码 我开始准备用vscode 进行编码,但是当我将cef 头文件加载进来时发现,vscode 有个问题导致编译无法进行,网上也没找到方法解决, 简单来说就是,vscode 是以文件夹来作为代码的起始目录, 当我在文件夹内再创建一个文件夹include, 在include文件夹内创建两个 test1.cpp 和test2.cpp, 当test2.cpp需要引入test1.cpp时 需要加入头文件 此时如果

#include “include/test1.cpp” 就会出错, 必须变成 #include "test1.cpp" 就不会出错, 而最外层的main.cpp 引入 test1.cpp 就要加上 #include”include/test1.cpp" 才行, 我感觉 vscode 是每个文件 都会有一个对应路径 需要按照每个文件的路径做参考系 来包含所有的文件,导致我加载cef的include文件夹时, 里面需要大量改动文件头路径 太麻烦所以放弃了。



2、所以采用第二种方法QtCreater,直接下载QtCreater

自行搜索 Qt教程 有详细的Qt下载和linux安装教程

http://c.biancheng.net/qt/

新建一个Qt 控制台应用,我这里工程取名cef_code

我们要把之前 cef 工程文件下的Debug 文件夹 Release文件夹 include文件夹 Resources 文件夹复制到qt当前项目路径中

复制到



然后将cef文件夹 里的 libcef_dll_wrapper文件夹里的libcef_dll_wrapper.a 文件复制到qt文件夹里的Debug 和Release 文件夹内,此时资源文件和cef头文件和 动态库全部配置完毕。

打开工程, 在main.cpp中代码如下

#include <QCoreApplication>
#include "include/cef_app.h" //! cef 头文件
#include "include/cef_browser.h"
#include "include/cef_client.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"
// ! 保存成bmp 文件 需要的bmp  文件头信息
typedef unsigned char  BYTE;
typedef unsigned short	WORD;
typedef unsigned int  DWORD;

typedef struct tagBITMAPFILEHEADER{
     WORD     bfType;        //Linux此值为固定值,0x4d42
     DWORD    bfSize;        //BMP文件的大小,包含三部分
     WORD    bfReserved;    //置0
     WORD    bfReserved2; //保留,0
     DWORD    bfOffBits;     //文件起始位置到图像像素数据的字节偏移量

}__attribute__((packed)) BITMAPFILEHEADER;
 //__attribute__((packed))

typedef struct tagBITMAPINFOHEADER{
     DWORD    biSize;          //文件信息头的大小,40
     DWORD    biWidth;         //图像宽度
     DWORD    biHeight;        //图像高度
     WORD     biPlanes;        //BMP存储RGB数据,总为1
     WORD     biBitCount;      //图像像素位数,笔者RGB位数使用24
     DWORD    biCompression;   //压缩 0:不压缩  1:RLE8 2:RLE4
     DWORD    biSizeImage;     //4字节对齐的图像数据大小
     DWORD    biXPelsPerMeter; //水平分辨率  像素/米
     DWORD    biYPelsPerMeter;  //垂直分辨率  像素/米
     DWORD    biClrUsed;        //实际使用的调色板索引数,0:使用所有的调色板索引
     DWORD    biClrImportant;
}__attribute__((packed)) BITMAPINFOHEADER;

typedef struct tagRGBQUAD {
BYTE    rgbBlue;
BYTE    rgbGreen;
BYTE    rgbRed;
BYTE    rgbReserved;
} RGBQUAD;
//! 保存 bmp 函数
static void SaveImage(int width, int height, const void* buffer, const char* path)
{

      //Set BITMAPINFOHEADER
      BITMAPINFOHEADER bi;
      bi.biSize = sizeof(BITMAPINFOHEADER);
      bi.biWidth = width;
      bi.biHeight = -height;
      bi.biPlanes = 1;
      bi.biBitCount = 32;
      bi.biCompression = 0;
      bi.biSizeImage = ((width*height) <<2);
      bi.biXPelsPerMeter = 0;
      bi.biYPelsPerMeter = 0;
      bi.biClrUsed = 0;
      bi.biClrImportant = 0;
      
      
      //Set BITMAPFILEHEADER
      BITMAPFILEHEADER bf;
      bf.bfType = 0x4d42;
      bf.bfSize = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER) + bi.biSizeImage;
      bf.bfReserved = 0;
      bf.bfReserved2 = 0;
      bf.bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);
      
      FILE* fp;
      if((fp = fopen(path, "wb")) == NULL)
        return;
      
      fwrite(&bf,sizeof(BITMAPFILEHEADER),1,fp);                      //写入文件头
      fwrite(&bi,sizeof(BITMAPINFOHEADER),1,fp);                      //写入信息头
      fwrite(buffer,bi.biSizeImage,1,fp); //写入图像数据
      fclose(fp);
}
//提供一个CefClinet子类处理某个浏览进程的回调, 要实现离屏渲染 必须 继承 CefRenderHandler类 实现GetViewRect 和 OnPaint 方法
class MyClient :public CefClient, public CefLifeSpanHandler, public CefRenderHandler
{
public:
    MyClient():count(0){};
    virtual ~MyClient()override {};
    virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler()override {
        return this;
    }
    virtual CefRefPtr<CefRenderHandler> GetRenderHandler()override {
        return this;
    }
    virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser)override {
        CefQuitMessageLoop();
    }
    virtual void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect)override
    {
        // 设置渲染图片的宽高
        rect.x = rect.y = 0;
        rect.width = 1920; 
        rect.height = 1080;
    }
    virtual void OnPaint(CefRefPtr<CefBrowser> browser,
        PaintElementType type,
        const RectList& dirtyRects,
        const void* buffer,
        int width,
        int height)override
    {
        // width  图片宽  height 图片高     buffer 图片数据
         count++;
         char num[100];
         snprintf(num, 100, "/home/leizhang/Documents/picture/%d.bmp", count);
         SaveImage(width, height, buffer, num);
    }
private:
    int count;
    IMPLEMENT_REFCOUNTING(MyClient);
};
//提供一个CefApp子类处理某个进程的回调
class MyApp :public CefApp, public CefBrowserProcessHandler
{
public:
    virtual ~MyApp()override{};
    virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler()override {
        return this;
    }
    virtual void OnContextInitialized() override {
        CEF_REQUIRE_UI_THREAD();
        CefWindowInfo window_info;

        CefRefPtr<MyClient> client = new MyClient();
        // 设置 无窗口渲染模式
        window_info.SetAsWindowless(0);
        //window_info.SetAsPopup(NULL, "SIMPLE");
        //window_info.SetAsChild(m_hwnd, m_rect);
        CefBrowserSettings settings;
        // 设置离屏渲染帧率
        settings.windowless_frame_rate = 60;
        // 设置网址url
        CefString url = "http://www.baidu.com";
        // 调用CefBrowserHost::CreateBrowser()函数创建浏览进程实例并使用CefLifeSpanHandler来管理浏览生命周期
        CefBrowserHost::CreateBrowser(window_info, client, url, settings, nullptr, nullptr);
    }
private:
    IMPLEMENT_REFCOUNTING(MyApp);
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
//每个CEF3子进程运行时使用运行行来指定配置信息并通过CefMainArgs结构传递给CefExecuteProcess函数,CefMainArgs结构是跨平台的。
    CefMainArgs main_args(argc, argv);
//当单可执行体时行时,入口函数CefExecuteProcess在不同进程类型之间需要区分。单可执行体结构只支持window和linux,不支持macos
    int exit_code = CefExecuteProcess(main_args, nullptr, nullptr);
    if (exit_code >= 0)
    {
        return exit_code;
    }

    CefSettings settings;
    settings.no_sandbox = true;
    //settings.multi_threaded_message_loop = true; window有效 linux无效,linux 不能打开这个 否则可能会无法运行
    // 离屏渲染模式开启
    settings.windowless_rendering_enabled = true;
    settings.log_severity = LOGSEVERITY_DISABLE;//日志
    auto myApp = CefRefPtr<MyApp>(new MyApp());
 // 提供一个入口函数以初始化CEF和运行每个子进程逻辑和CEF消息处理
    CefInitialize(main_args, settings, myApp.get(), nullptr);
// 开启cef 消息循环
    CefRunMessageLoop();
    //a.exec();
    CefShutdown();
    return 0;
}

想要实现离屏渲染 提供一个CefClient 子类 要继承CefRenderHandler类主要提供方法有:

1.CefRenderHandler::GetViewRect()函数获取想要获取视图的矩阵。// 必须实现

2.CefRenderHandler::OnPaint()被调用,以提供一个无效区域和更新的像素buffer。CefClient应用程序使用OpenGL绘制缓冲。// 必须实现

3.调用CefBrowserHost::WasResized()重置浏览器大小。这将导致调用GetViewRect()来检索新尺寸随后调用OnPaint()。

4.CefBrowserHost::SendXXX()函数通知浏览进程的鼠标、键盘和焦点事件

5.CefBrowserHost::CloseBrowser()销毁浏览器

 

4. 编译代码

完成简单的代码编写后,需要加入动态链接库

打开cef_code.pro 文件添加

LIBS += $$PWD/Debug/libcef.so
LIBS += $$PWD/Debug/libcef_dll_wrapper.a

必须添加否则会报错, 编译完成后, 调试发现会提示libcef.so 文件找不到

这里也是比较头疼,于是我把qt项目当前目录下的Debug里的所有文件 都包含的有libcef.so 文件放到 可执行文件目录下 也提示libcef.so无法找到, 于是我将libcef.so 文件放到/usr/lib/ 中 终于不提示报错了,然后我想 通过vim ~/.bashrc 在里面加入

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/leizhang/Documents/qt_code/cef_code/Debug

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/leizhang/Documents/qt_code/cef_code/Release

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/leizhang/Documents/qt_code/cef_code/Resources

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/leizhang/Documents/qt_code/cef_code/Debug/locales

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/leizhang/Documents/qt_code/cef_code/Debug/swiftshader

把各种加载资源文件和动态库目录都导入带bashrc 文件中, 并把/usr/lib/ 得libcef.so 删除 再次运行 还是报libcef.so 文件找不到,到这里我已经不知道怎么办了,于是我将libcef.so 还是拷贝到/usr/lib/ 系统目录下,能找到文件了,但是调试到CefInitialize 这一步又崩溃了,应该是资源文件没有添加,于是我把qt项目目录下的Resource文件夹里所有资源全部拷贝到可执行文件当前目录下,调试还是崩溃,应该还是调试的时候资源文件没有加载到导致的,但是我又不知道资源文件应该加载到哪里。。 网上也基本没有这种解决办法,暂时无解。

然后我把/usr/lib/ 里的libcef.so 文件删除,

 

准备直接在生成的可执行文件的目录下 用命令行执行 试试看



报这个什么libva error 于是又在网上找 好像没装libva 库 然后安装后还是不行,但是我发现当我点退出的时候说我还有进程未退出,代表着可能还在运行, 我进入生成图片的文件夹内部发现竟然生成了 网页图片,我猜测 直接执行的话 它能找到当前目录的可执行文件、资源文件和动态链接库,于是我再次把bashrc 里 之前导入的文件夹路径删除,再次实验无法执行报错,发现动态链接库还是需要在bashrc文件里指定路径



总结:1. linux Qt调试时 将libcef.so 放到 /usr/lib 下才能运行,网上说在bashrc 文件指定动态库目录路径也可以,但是我这不行不知道为啥

指定路径为 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库文件夹路径

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/leizhang/Documents/qt_code/cef_code/Debug

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/leizhang/Documents/qt_code/cef_code/Release

2. linux Qt 可以调试之后 在CefInitialize 初始化失败 可能是调试时 Resource文件.pak等文件 没有加载进来, 暂时也不太清楚这个文件在哪里加载

3. linux Qt 编译成功后 可以直接在可执行文件下 用终端进行 执行, 但是要将资源的所有文件和动态链接库也就是Qt工程目录下Debug 或者Release 文件夹里libcef.so libcef_dll_wrapper.a等所有内容放到可执行文件目录下,在bashrc文件添加动态库文件夹路径,才能执行!

参考文献:

https://blog.csdn.net/cqclark/article/details/49121053

https://blog.csdn.net/CAir2/article/details/84969813

https://blog.csdn.net/hp_cpp/article/details/109864269

https://bitbucket.org/chromiumembedded/cef/wiki/BranchesAndBuilding.md

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值