如何在 Linux 中使用旧相机作为网络摄像头
相机 摄像头
![](/data/attachment/album/202303/21/233633z1qxdoq1shrx1xmc.jpg)



> 
> 我用 gphoto2 给我的旧单反相机带来了新的生命，把它变成了 Linux 电脑的网络摄像头。
> 
> 
> 


今年，在我基本上放弃了 MacBook，转而使用 NixOS 机器之后，我开始在与人进行视频通话时被要求“打开摄像头”。这是一个问题，因为我没有网络摄像头。我考虑购买一个，但后来我意识到我有一台完好无损的 2008 年产的佳能 EOS Rebel XS 数码单反相机放在书架上。这台相机有一个 mini-USB 接口，所以我自然而然地思考：一台数码单反相机、一个 mini-USB 接口和一台台式电脑，是否意味着我能拥有一个网络摄像头？


只有一个问题。我的佳能 EOS Rebel XS 不能录制视频。它可以拍摄一些漂亮的照片，仅此而已。所以这结束了？


还是有别的办法？


恰好有一个叫做 [gphoto2](http://gphoto.org/) 的神奇的开源软件。一旦安装，它允许你从计算机控制各种支持的相机，并拍摄照片和视频。


### 支持的相机


首先，了解你的设备是否得到支持：



```
$ gphoto2 --list-cameras

```

### 拍摄图像


你可以用它拍照：



```
$ gphoto2 --capture-image-and-download

```

快门触发，图像会保存到你当前的工作目录中。


### 录制视频


我意识到了这里的潜力，所以尽管我的相机没有视频功能，我还是决定尝试 `gphoto2 --capture-movie` 命令。不知怎么，尽管我的相机不支持视频功能，`gphoto2` 仍然能够生成一个 MJPEG 文件！


在我的相机上，我需要将其置于“实时预览”模式下，然后 `gphoto2` 才能录制视频。这包括将相机设置为纵向模式，然后按下 “<ruby> 设置 <rt>  Set </rt></ruby>” 按钮，使取景器关闭，相机屏幕显示图像。不幸的是，这还不足以将其用作网络摄像头。它仍然需要分配一个视频设备，例如 `/dev/video0`。


### 安装 ffmpeg 和 v4l2loopback


毫不奇怪，有一个开源的解决方案来解决这个问题。首先，使用你的包管理器安装 `gphoto2`、`ffmpeg` 和 `mpv`。例如，在 Fedora 、CentOS 、Mageia 和类似的 Linux 发行版上：



```
$ sudo dnf install gphoto2 ffmpeg mpv

```

在 Debian、Linux Mint 及其类似发行版:



```
$ sudo apt install gphoto2 ffmpeg mpv

```

我使用的是 NixOS，这是我的配置文件：



```
# configuration.nix
...
environment.systemPackages = with pkgs; [
  ffmpeg
  gphoto2
  mpv
...  
]
```

创建虚拟视频设备需要使用 `v4l2loopback` Linux 内核模块。在撰写本文时，该功能未包含在主线内核中，因此你需要自己下载和编译它：



```
$ git clone https://github.com/umlaeute/v4l2loopback
$ cd v4l2loopback
$ make
$ sudo make install
$ sudo depmod -a

```

如果你像我一样使用 NixOS ，你可以在 `configuration.nix` 中添加额外的模块包：



```
[...]
boot.extraModulePackages = with config.boot.kernelPackages;
[ v4l2loopback.out ];
boot.kernelModules = [
  "v4l2loopback"
];
boot.extraModprobeConfig = ''
  options v4l2loopback exclusive_caps=1 card_label="Virtual Camera"
'';
[...]

```

在 NixOS 上, 运行 `sudo nixos-rebuild switch`，然后重启。


### 创建一个视频设备


假设你的计算机当前没有 `/dev/video` 设备，你可以借助 `v4l2loopback` 在需要时创建一个。


运行以下命令，将 `gphoto2` 中的数据发送到 `ffmpeg`，使用设备如 `/dev/video0` 设备：



```
$ gphoto2 --stdout --capture-movie |
 ffmpeg -i - -vcodec rawvideo -pix_fmt yuv420p -f v4l2 /dev/video0

```

你得到的输出是这样的：



```
ffmpeg version 4.4.1 Copyright (c) 2000-2021 the FFmpeg developers
  built with gcc 11.3.0 (GCC)
  configuration: --disable-static ...
  libavutil      56. 70.100 / 56. 70.100
  libavcodec     58.134.100 / 58.134.100
  libavformat    58. 76.100 / 58. 76.100
  libavdevice    58. 13.100 / 58. 13.100
  libavfilter     7.110.100 /  7.110.100
  libavresample   4.  0.  0 /  4.  0.  0
  libswscale      5.  9.100 /  5.  9.100
  libswresample   3.  9.100 /  3.  9.100
  libpostproc    55.  9.100 / 55.  9.100
Capturing preview frames as movie to 'stdout'. Press Ctrl-C to abort.[mjpeg @ 0x1dd0380] Format mjpeg detected only with low score of 25, misdetection possible!
Input #0, mjpeg, from 'pipe:':
  Duration: N/A, bitrate: N/A
  Stream #0:0: Video: mjpeg (Baseline), yuvj422p(pc, bt470bg/unknown/unknown), 768x512 ...
Stream mapping:
  Stream #0:0 -> #0:0 (mjpeg (native) -> rawvideo (native))[swscaler @ 0x1e27340] deprecated pixel format used, make sure you did set range correctly
Output #0, video4linux2,v4l2, to '/dev/video0':
  Metadata:
    encoder         : Lavf58.76.100
  Stream #0:0: Video: rawvideo (I420 / 0x30323449) ...
    Metadata:
      encoder         : Lavc58.134.100 rawvideoframe=  289 fps= 23 q=-0.0 size=N/A time=00:00:11.56 bitrate=N/A speed=0.907x

```

要查看来自网络摄像头的视频，请使用 `mpv` 命令：



```
$ mpv av://v4l2:/dev/video0 --profile=low-latency --untimed

```

![Streaming a live feed from the webcam](/data/attachment/album/202303/21/233414pjm6que6y6zj6mlu.jpg)


### 自动启动你的网络摄像头


每次想使用网络摄像头时都需要执行一次命令有点麻烦。幸运的是，你可以在启动时自动运行此命令。我将其实现为一个 `systemd` 服务：



```
# configuration.nix
...
  systemd.services.webcam = {
    enable = true;
    script = ''
      ${pkgs.gphoto2}/bin/gphoto2 --stdout --capture-movie |
        ${pkgs.ffmpeg}/bin/ffmpeg -i - \
            -vcodec rawvideo -pix_fmt yuv420p -f v4l2  /dev/video0
    '';
wantedBy = [ "multi-user.target" ];
  };
...

```

在 NixOS 上，运行 `sudo nixos-rebuild switch`，然后重新启动你的计算机。你的网络摄像头已经开启并处于活动状态。


要检查是否存在任何问题，可以使用 `systemctl status webcam` 命令。它会告诉你服务最后一次运行的时间，并提供其以前输出的日志。这对于调试非常有用。


### 迭代以使其变得更好


止步于此也许很诱人。但是，考虑到当前的全球危机，我们可能需要思考是否有必要一直开着网络摄像头。这让我感到不太理想，原因如下：


* 这浪费电。
* 这类事情涉及隐私问题。


我的摄像头有一个镜头盖，所以说实话，第二个原因并不真的让我感到困扰。当我不使用网络摄像头时，我总是可以把镜头盖上。然而，让一个耗电量大的单反相机整天开着（更不用说需要解码视频所需的 CPU 开销），对我的电费并没有任何好处。


理想情况是：


* 我一直把相机插在电脑上，但是关闭的。
* 当我想使用网络摄像头时，我按下相机的电源按钮将其打开。
* 我的计算机会检测到相机并启动 systemd 服务。
* 使用网络摄像头完成后，我再次将其关闭。


为了实现这一点，你需要使用一个自定义的 udev 规则。


udev 规则可以告诉你的计算机，当它发现某个设备已经可用时执行某个任务。这可以是外部硬盘甚至是非 USB 设备。在这种情况下，你需要通过其 USB 连接识别相机。


首先，指定 udev 规则被触发时要运行的命令。你可以用一个 shell 脚本来完成（`systemctl restart webcam` 应该可以工作）。我运行的是 NixOS，所以我只需要创建一个派生包（一个 Nix 包），它会重新启动 systemd 服务：



```
# start-webcam.nix
with import <nixpkgs> { };
writeShellScriptBin "start-webcam" ''
  systemctl restart webcam
  # debugging example
  # echo "hello" &> /home/tom/myfile.txt
  # If myfile.txt gets created then we know the udev rule has triggered properly''

```

接下来，实际定义 udev 规则。查找摄像头的设备和厂商 ID。使用 `lsusb` 命令可以完成此操作。该命令可能已经安装在你的发行版上，但我不经常使用它，因此我只需要根据需要使用 `nix-shell` 安装它：



```
$ nix-shell -p usbutils

```

无论你的计算机上已经安装了它，还是刚刚安装，请运行 `lsusb` ：



```
$ lsusb
Bus 002 Device 008: ID 04a9:317b Canon, Inc. Canon Digital Camera[...]

```

在此输出中，厂商 ID 为 `04a9`，设备 ID 为 `317b`。这已足以创建 udev 规则：



```
ACTION=="add", SUBSYSTEM=="usb",
ATTR{idVendor}=="04a9",
ATTR{idProduct}=="317b",
RUN+="/usr/local/bin/start-webcam.sh"

```

或者，如果你使用的是 NixOS：



```
# configuration.nix[...]let
  startWebcam = import ./start-webcam.nix;[...]
services.udev.extraRules = ''
  ACTION=="add",  \
  SUBSYSTEM=="usb", \
  ATTR{idVendor}=="04a9", \
  ATTR{idProduct}=="317b",  \
  RUN+="${startWebcam}/bin/start-webcam"'';[...]

```

最后，在你的 `start-webcam` systemd 服务中删除 `wantedBy = ["multi-user.target"];` 这一行。（如果保留它，则无论相机是否开启，该服务都会在下次重启时自动启动。）


### 重复使用旧技术


我希望这篇文章能让你在放弃一些旧技术之前三思而后行。Linux 可以为技术注入活力，无论是你的电脑还是数码相机或其他外围设备等简单的东西。




---


via: <https://opensource.com/article/22/12/old-camera-webcam-linux>


作者：[Tom Oliver](https://opensource.com/users/tomoliver) 选题：[lkxed](https://github.com/lkxed) 译者：[Pabloxllwe](https://github.com/Pabloxllwe) 校对：[wxy](https://github.com/wxy)


本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


OK
This year after largely abandoning my MacBook in favor of a NixOS machine, I started getting requests to "turn my camera on" when video calling people. This was a problem because I didn't have a webcam. I thought about buying one, but then I realized I had a perfectly good Canon EOS Rebel XS DSLR from 2008 lying around on my shelf. This camera has a mini-USB port, so naturally, I pondered: Did a DSLR, mini-USB port, and a desktop PC mean I could have a webcam?

There's just one problem. My Canon EOS Rebel XS isn't capable of recording video. It can take some nice pictures, but that's about it. So that's the end of that.

Or is it?

There happens to be some amazing open source software called [gphoto2](https://opensource.com/article/20/7/gphoto2-linux). Once installed, it allows you to control various supported cameras from your computer and it takes photos and videos.

## Supported cameras

First, find out whether yours is supported:

```
````$ gphoto2 --list-cameras`


## Capture an image

You can take a picture with it:

```
````$ gphoto2 --capture-image-and-download`


The shutter activates, and the image is saved to your current working directory.

## Capture video

I sensed the potential here, so despite the aforementioned lack of video functionality on my camera, I decided to try `gphoto2 --capture-movie`

. Somehow, although my camera does not support video natively, gphoto2 still manages to spit out an MJPEG file!

On my camera, I need to put it in "live-view" mode before gphoto2 records video. This consists of setting the camera to portrait mode and then pressing the **Set** button so that the viewfinder is off and the camera screen displays an image. Unfortunately, though, this isn't enough to be able to use it as a webcam. It still needs to get assigned a video device, such as `/dev/video0`

.

## Install ffmpeg and v4l2loopback

Not surprisingly, there's an open source solution to this problem. First, use your package manager to install `gphoto2`

, `ffmpeg`

, and `mpv`

. For example, on Fedora, CentOS, Mageia, and similar:

```
````$ sudo dnf install gphoto2 ffmpeg mpv`


On Debian, Linux Mint, and similar:

```
````$ sudo apt install gphoto2 ffmpeg mpv`


I use NixOS, so here's my configuration:

```
``````
# configuration.nix
...
environment.systemPackages = with pkgs; [
ffmpeg
gphoto2
mpv
...
```


Creating a virtual video device requires the `v4l2loopback`

Linux kernel module. At the time of this writing, that capability is not included in the mainline kernel, so you must download and compile it yourself:

```
``````
$ git clone https://github.com/umlaeute/v4l2loopback
$ cd v4l2loopback
$ make
$ sudo make install
$ sudo depmod -a
```


If you're using NixOS like me, you can just add the extra module package in `configuration.nix`

:

```
``````
[...]
boot.extraModulePackages = with config.boot.kernelPackages;
[ v4l2loopback.out ];
boot.kernelModules = [
"v4l2loopback"
];
boot.extraModprobeConfig = ''
options v4l2loopback exclusive_caps=1 card_label="Virtual Camera"
'';
[...]
```


On NixOS, run `sudo nixos-rebuild switch`

and then reboot.

## Create a video device

Assuming your computer currently has no `/dev/video`

device, you can create one on demand thanks to the `v4l2loopback`

.

Run this command to send data from `gphoto2`

to `ffmpeg`

, using a device such as `/dev/video0`

device:

```
``````
$ gphoto2 --stdout --capture-movie |
ffmpeg -i - -vcodec rawvideo -pix_fmt yuv420p -f v4l2 /dev/video0
```


You get output like this:

```
``````
ffmpeg version 4.4.1 Copyright (c) 2000-2021 the FFmpeg developers
built with gcc 11.3.0 (GCC)
configuration: --disable-static ...
libavutil 56. 70.100 / 56. 70.100
libavcodec 58.134.100 / 58.134.100
libavformat 58. 76.100 / 58. 76.100
libavdevice 58. 13.100 / 58. 13.100
libavfilter 7.110.100 / 7.110.100
libavresample 4. 0. 0 / 4. 0. 0
libswscale 5. 9.100 / 5. 9.100
libswresample 3. 9.100 / 3. 9.100
libpostproc 55. 9.100 / 55. 9.100
Capturing preview frames as movie to 'stdout'. Press Ctrl-C to abort.
[mjpeg @ 0x1dd0380] Format mjpeg detected only with low score of 25, misdetection possible!
Input #0, mjpeg, from 'pipe:':
Duration: N/A, bitrate: N/A
Stream #0:0: Video: mjpeg (Baseline), yuvj422p(pc, bt470bg/unknown/unknown), 768x512 ...
Stream mapping:
Stream #0:0 -> #0:0 (mjpeg (native) -> rawvideo (native))
[swscaler @ 0x1e27340] deprecated pixel format used, make sure you did set range correctly
Output #0, video4linux2,v4l2, to '/dev/video0':
Metadata:
encoder : Lavf58.76.100
Stream #0:0: Video: rawvideo (I420 / 0x30323449) ...
Metadata:
encoder : Lavc58.134.100 rawvideo
frame= 289 fps= 23 q=-0.0 size=N/A time=00:00:11.56 bitrate=N/A speed=0.907x
```


To see the video feed from your webcam, use `mpv`

:

```
````$ mpv av://v4l2:/dev/video0 --profile=low-latency --untimed`


![Streaming a live feed from the webcam](https://opensource.com/sites/default/files/2022-12/streaming-webcam.png)

(Tom Oliver, CC BY-SA 4.0)

## Start your webcam automatically

It's a bit annoying to execute a command every time you want to use your webcam. Luckily, you can run this command automatically at startup. I implement it as a `systemd`

service:

```
``````
# configuration.nix
...
systemd.services.webcam = {
enable = true;
script = ''
${pkgs.gphoto2}/bin/gphoto2 --stdout --capture-movie |
${pkgs.ffmpeg}/bin/ffmpeg -i - \
-vcodec rawvideo -pix_fmt yuv420p -f v4l2 /dev/video0
'';
wantedBy = [ "multi-user.target" ];
};
...
```


On NixOS, run `sudo nixos-rebuild switch`

and then reboot your computer. Your webcam is on and active.

To check for any problems, you can use `systemctl status webcam`

. This tells you the last time the service was run and provides a log of its previous output. It's useful for debugging.


## Iterating to make it better

It's tempting to stop here. However, considering the current global crises, it may be pertinent to wonder whether it's necessary to have a webcam on all the time. It strikes me as sub-optimal for two reasons:

- It's a waste of electricity.
- There are privacy concerns associated with this kind of thing.

My camera has a lens cap, so to be honest, the second point doesn't really bother me. I can always put the lens cap on when I'm not using the webcam. However, leaving a big power-hungry DSLR camera on all day (not to mention the CPU overhead required for decoding the video) isn't doing anything for my electricity bill.

The ideal scenario:

- I leave my camera plugged in to my computer all the time but switched off.
- When I want to use the webcam, I switch on the camera with its power button.
- My computer detects the camera and starts the systemd service.
- After finishing with the webcam, I switch it off again.

To achieve this, you need to use a custom [udev rule](https://opensource.com/article/18/11/udev).

A udev rule tells your computer to perform a certain task when it discovers that a device has become available. This could be an external hard drive or even a non-USB device. In this case, you need it to [recognize the camera through its USB connection](https://opensource.com/article/22/1/cameras-usb-ports-obs).

First, specify what command to run when the udev rule is triggered. You can do that as a shell script (`systemctl restart webcam`

should work). I run NixOS, so I just create a derivation (a Nix package) that restarts the systemd service:

```
``````
# start-webcam.nix
with import <nixpkgs> { };
writeShellScriptBin "start-webcam" ''
systemctl restart webcam
# debugging example
# echo "hello" &> /home/tom/myfile.txt
# If myfile.txt gets created then we know the udev rule has triggered properly
''
```


Next, actually define the udev rule. Find the device and vendor ID of the camera. Do this by using the `lsusb`

command. That command is likely already installed on your distribution, but I don't use it often, so I just install it as needed using `nix-shell`

:

```
````$ nix-shell -p usbutils`


Whether you already have it on your computer or you've just installed it, run `lsusb`

:

```
``````
$ lsusb
Bus 002 Device 008: ID 04a9:317b Canon, Inc. Canon Digital Camera
[...]
```


In this output, the vendor ID is 04a9 and the device ID is 317b. That's enough to create the udev rule:

```
``````
ACTION=="add", SUBSYSTEM=="usb",
ATTR{idVendor}=="04a9",
ATTR{idProduct}=="317b",
RUN+="/usr/local/bin/start-webcam.sh"
```


Alternatively, if you're using NixOS:

```
``````
# configuration.nix
[...]
let
startWebcam = import ./start-webcam.nix;
[...]
services.udev.extraRules = ''
ACTION=="add", \
SUBSYSTEM=="usb", \
ATTR{idVendor}=="04a9", \
ATTR{idProduct}=="317b", \
RUN+="${startWebcam}/bin/start-webcam"
'';
[...]
```


Finally, remove the **wantedBy = ["multi-user.target"];** line in your `start-webcam`

systemd service. (If you leave it, then the service starts automatically when you next reboot, whether the camera is switched on or not.)

## Reuse old technology

I hope this article has made you think twice before chucking some of your old tech. Linux can breathe life back into technology, whether it's your [computer](https://opensource.com/article/22/4/how-linux-saves-earth) or something simple like a digital camera or some other peripheral.

## 1 Comment