cef降低视频渲染时cpu使用率的方法

6 篇文章 2 订阅

windows客户端中使用了基于webrtc的音视频sdk,同时界面使用的是cef。
当webrtc解码出大尺寸视频时,需要通过某些技术将数据投递给javascript进行绘制。
##一、使用canvas绘制bmp数据
webrtc解码图像后,将数据转换为BMP图像,保存在内存中,
javascript通过自定义scheme进行图像的获取,比如请求zhangpengtest://image001.bmp
拿到数据后通过canvas绘制bmp。
这种方法cpu占用率很高,因为canvas使用cpu绘制,并且需要进行图像的缩放。
c++通过以下代码通知js有图像准备好:

    std::stringstream ss;
	ss << "zhangpengtest://" << sid << pic_id++ << ".bmp";
	std::string url = ss.str();		
	CefRefPtr<CefFrame> frame = browser->GetMainFrame();
	char buffer[256];
	sprintf(buffer, "onVideoData('%s');", url.c_str());
	frame->ExecuteJavaScript(buffer, frame->GetURL(), 0);

javascript可以通过设置img标签的src来加载图像
##二、通过webgl绘制
通过webgl绘制可以降低cpu,如何获取javascript的arraybuffer数据?
可以通过自定义scheme,结合XMLHttpRequest的Get请求,向browser进程的webrtc请求数据。
可以参考文章 cef中javascript和c++交换二进制数据(arraybuffer)的方法
我为什么选择使用方案三,是因为当时还没有发现能拿到arraybuffer数据的方法。
目前感觉可以使用此方案替换方案三。
##三、修改cef源码,添加虚拟摄像头,使用video标签预览摄像头
###基础知识
cef中本身也继承了一个webrtc,在javascript中使用getUserMedia()可以获取视频流stream。
通过window.navigator.mediaDevices.enumerateDevices()可以获取摄像头列表。
通过window.navigator.mediaDevices.getUserMedia可以获取对应于某个videoId的stream。
当使用video.srcObject = stream时,即可在video标签中预览摄像头。
###添加虚拟的摄像头
通过为设备列表添加一个虚拟的id,通过这个id的stream,使video标签打开我们自己实现的摄像头。
在该摄像头的实现代码中,为摄像头获取webrtc sdk解码的YUV格式的视频图像。
(1)修改media\capture\video\win\video_capture_device_factory_win.cc

void VideoCaptureDeviceFactoryWin::GetDeviceDescriptors(
    VideoCaptureDeviceDescriptors* device_descriptors) {
  DCHECK(thread_checker_.CalledOnValidThread());
  VideoCaptureApi api = VideoCaptureApi::WIN_DIRECT_SHOW;
  if (use_media_foundation_) {
	  GetDeviceDescriptorsMediaFoundation(device_descriptors);
	  api = VideoCaptureApi::WIN_MEDIA_FOUNDATION;
  }
  else {
	  GetDeviceDescriptorsDirectShow(device_descriptors);
  }
  device_descriptors->emplace_back("zhangpengtest", "085db6c9-3c95-43a9-96de-2f015f2ac17c", "zhangpengtest:0", api);
}

在获取摄像头列表后,添加一个虚拟摄像头zhangpengtest,字符串的字段都可以任意填写,不要和已存在的设备重名即可。

std::unique_ptr<VideoCaptureDevice> VideoCaptureDeviceFactoryWin::CreateDevice(
    const Descriptor& device_descriptor) {
  DCHECK(thread_checker_.CalledOnValidThread());

  std::unique_ptr<VideoCaptureDevice> device;
  if (device_descriptor.display_name.find("zhangpengtest") != std::string::npos) {
	  device.reset(new VideoCaptureDeviceZhangpeng(device_descriptor));
	  DVLOG(1) << " DirectShow Device: " << device_descriptor.display_name;
	  if (!static_cast<VideoCaptureDeviceFenbi*>(device.get())->Init())
		  device.reset();

	  return device;
  }
  //省略了部分代码
  return device;
}

以上代码,在打开摄像头时,发现是虚拟摄像头,就返回了VideoCaptureDeviceZhangpeng对象。
(2)实现摄像头类VideoCaptureDeviceZhangpeng

//继承于VideoCaptureDevice
class VideoCaptureDeviceZhangpeng : public VideoCaptureDevice {
    bool Init();
}

具体的实现细节,参考内置摄像头的实现方法即可。
重点细节在于在Init中启动一个线程,该线程负责定时获取图像数据。
获取图像数据后,回调给上层逻辑。

client_->OnIncomingCapturedData(data);

这样video标签就拿到了视频图像,会自动绘制出来。
(3)如何获取图像数据呢?
在windows系统中,可以通过共享内存来解决。
webrtc sdk解码视频后,写入共享内存。
虚拟摄像头中的线程,定时读取共享内存即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值