实现浏览器可以使用的媒体设备

Implementing a media device the browser can consume

本文关键字:媒体 浏览器 可以使 实现      更新时间:2023-10-16

我想创建一个供浏览器使用的媒体设备。也就是说,我想发布一个浏览器可以通过navigator.mediaDevices获取的视频流,通过WebRTC发送,放入<video>标签。

实际上,我正在做的是消耗视频流(我从C++中吐出图像的循环中收集到它(,读取它并分析它,我希望能够将流发送到浏览器。理想情况下,我希望能够从 Docker 容器执行此操作。C++过程也将作为 Node 绑定共存,但我不确定这是否相关。在这种情况下,我要说的是,如果通过绑定的 API 发送图像/视频流然后从 Node 发布是最容易的,我对此没有问题。

任何人都可以提供有关如何注册人造设备的文档或阅读材料,无论浏览器从何处获取设备?我对司机或任何东西都不是很熟悉。

虽然我认为任何好的解决方案都会与其他系统相当交叉兼容,但我只需要它与 Ubuntu 16.04 和 Chrome 兼容。

我最终得到的架构如下:

使用FFmpeg (libavdevice/libavcodec/libavformat 等(C++馈送到我使用 v4l2loopback 创建的设备中。然后Chrome可以检测到这个伪设备。(只要您使用如下所示exclusive_caps=1选项(

所以我做的第一件事是设置 v4l2loopback 设备。这是一个人造设备,可以像普通相机一样输出,但它也会像捕获设备或类似设备一样进行输入。

git clone https://github.com/umlaeute/v4l2loopback
cd v4l2loopback
git reset --hard b2b33ee31d521da5069cd0683b3c0982251517b6 # ensure v4l2loopback functionality changes don't affect this script
make
sudo insmod v4l2loopback.ko exclusive_caps=1 video_nr=$video_nr card_label="My_Fake_Camera"

浏览器将在你发布到设备时看到navigator.mediaDevices.enumerateDevices()设备。要在通过C++馈送给它之前测试它是否正常工作,您可以使用ffmpeg -re -i test.avi -f v4l2 /dev/video$video_nr.对于我的需求,我使用的是Puppeteer,因此测试相对容易,但请记住,持久的浏览器会话将缓存设备并很少刷新它们,因此请确保test.avi(或任何视频文件(很长(1分钟+(,以便您可以尝试完全重置环境。我从来没有弄清楚缓存策略到底是什么,所以Puppeteer在这里非常有用,但我已经在使用它,所以我不必设置它。扬子晚报.

现在(对我来说(困难的部分是让 FFmpeg(libav-* 版本 2.8(输出到这个设备。我不能/不会分享我的所有代码,但这里有一些部分和一些指导智慧:

建立:

  • 使用avformat_alloc_output_context2(&formatContext->pb, NULL, "v4l2", "/dev/video5")创建AVFormatContext
  • 使用avcodec_find_encoder设置AVCodec并使用avformat_new_stream创建AVStream
  • 您应该设置一堆小标志,但我不会在这个答案中介绍所有这些标志。这个片段以及其他一些片段包括很多这样的工作,但它们都是针对写入磁盘而不是设备。您需要更改的最大内容是使用设备而不是文件创建AVFormatContext(请参阅第一步(。

对于每一帧:

  • 使用OpenCV的cvtColor将您的图像转换为适当的色彩空间(我的是BGR,这是OpenCV的默认颜色空间(
  • 将 OpenCV 矩阵转换为 libav AVFrame(使用sws_scale(
  • 使用avcodec_encode_video2将 AVFrame 编码为 AVPacket
  • 使用av_write_frame将数据包写入AVFormatContext

只要您正确执行所有这些操作,它就应该将其馈送到设备中,并且您应该能够在浏览器(或任何检测摄像头的地方(中使用视频源。

我要补充的一件事是,对于

Docker 来说,这是特别必要的,你必须确保在主机和容器之间共享 v4l2 设备,假设你在容器之外使用设备(我是(。这意味着您将使用--device=/dev/video$video_nr运行docker run命令。