惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
雷峰网
雷峰网
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Spread Privacy
Spread Privacy
H
Hacker News: Front Page
PCI Perspectives
PCI Perspectives
Webroot Blog
Webroot Blog
罗磊的独立博客
H
Heimdal Security Blog
TaoSecurity Blog
TaoSecurity Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
大猫的无限游戏
大猫的无限游戏
月光博客
月光博客
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Google Online Security Blog
Google Online Security Blog
Last Week in AI
Last Week in AI
美团技术团队
Help Net Security
Help Net Security
The Hacker News
The Hacker News
C
Cisco Blogs
T
The Blog of Author Tim Ferriss
J
Java Code Geeks
The Register - Security
The Register - Security
IT之家
IT之家
WordPress大学
WordPress大学
Jina AI
Jina AI
Recent Commits to openclaw:main
Recent Commits to openclaw:main
H
Help Net Security
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
T
Threat Research - Cisco Blogs
P
Proofpoint News Feed
NISL@THU
NISL@THU
爱范儿
爱范儿
The GitHub Blog
The GitHub Blog
Scott Helme
Scott Helme
V
Vulnerabilities – Threatpost
B
Blog
T
Tenable Blog
博客园 - 三生石上(FineUI控件)
T
The Exploit Database - CXSecurity.com
S
Security Affairs
小众软件
小众软件
Hacker News: Ask HN
Hacker News: Ask HN
Security Latest
Security Latest
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
W
WeLiveSecurity
A
Arctic Wolf
L
LINUX DO - 热门话题
Google DeepMind News
Google DeepMind News
M
MIT News - Artificial intelligence

Yesterday17's Blog

2026 新年解密红包 / Melody Flag | Yesterday17's Blog 谈谈 Iori 的设计思路(二):如何实现一个 Showroom 录制工具? | Yesterday17's Blog 谈谈 Iori 的设计思路(一):从 Nico Timeshift 说起 | Yesterday17's Blog Iori Minyami 0.1.0 发布 | Yesterday17's Blog 2025 新年解密红包 / Melody Flag | Yesterday17's Blog 使用 Cloudflare Warp 解决罗森票务的海外登录问题 | Yesterday17's Blog How To Blog 04: The Astro v5 Era | Yesterday17's Blog 谈谈 tokio::select! 的公平性 | Yesterday17's Blog Learning Pingora 05 - Connect with TLS | Yesterday17's Blog Leaving Bytedance | Yesterday17's Blog 大橋彩香 AsiaTour「Reflection」上海公演 个人向记录 & Repo | Yesterday17's Blog Recoving from burnout - What happened? | Yesterday17 Yubikey 重建手册 | Yesterday17's Blog How To Blog 03: Heimus | Yesterday17's Blog 🪧 Blog Migration Accouncement | Yesterday17's Blog Learn Your IDE - VSCode 是如何仅重启插件的? | Yesterday17's Blog How To Blog 02: Astro❤️Password | Yesterday17's Blog How To Blog 01: Why, How, and the Future | Yesterday17's Blog Learning Pingora 04 - Establish L4 Connection | Yesterday17's Blog Learning Pingora 03 - Upstreams and Peers | Yesterday17's Blog Learning Pingora 02 - A Simple HTTP Server | Yesterday17's Blog Learning Pingora 01 - Getting Started | Yesterday17's Blog 2024 新年解密红包 / Melody Flag | Yesterday17's Blog 向新的一年飞驰——记录 2023 | Yesterday17's Blog 「サクラノ刻」对话选摘(2) | Yesterday17's Blog PGP Key Revocation 注销声明 | Yesterday17's Blog 「サクラノ刻」对话选摘(1) | Yesterday17's Blog 2023 新年解密红包 / Melody Flag | Yesterday17's Blog 『蒼の彼方のフォーリズム』通关感想 | Yesterday17's Blog 单显卡直通教程 | Yesterday17's Blog 对博客与笔记的思考 | Yesterday17's Blog Project Anni 之旅(3)自动化 Flutter 应用 CI/CD 上架流程 | Yesterday17's Blog AsobiStage 直接播放链接 | Yesterday17 如何在后分P时代进行投稿——sswa使用详解 | Yesterday17's Blog JSON RPC 与 LSP 协议基础 | Yesterday17's Blog Grajapa Shueisha / BookEnd 加密方式调查 | Yesterday17's Blog 【2022篇+WriteUp】如何再收一个新年红包? | Yesterday17's Blog 如何将良心云的良心功能清理干净 | Yesterday17's Blog 【油猴脚本】bilibili 投稿页面返回旧版+旧版页面强制允许分P上传 | Yesterday17's Blog Cloudr1v1 授权方式分析 | Yesterday17 Typora 1.0.2 逆向实录 | Yesterday17's Blog Project Anni 之旅(2)ValueAfterTable——toml-rs的实现与限制 | Yesterday17's Blog IPv4透明代理+IPv6 Passthrough——树莓派单臂软路由折腾记 | Yesterday17's Blog Chaos; Child 汉化补丁 神秘编码探索 | Yesterday17's Blog 镣铐与舞蹈——个性与共性之迷思 | Yesterday17's Blog Go 学习笔记 02 - 找准 io 之道 | Yesterday17's Blog NAT Slipstreaming v1 原理浅析 | Yesterday17's Blog 绕过「9-nine-」的 CDKEY 验证——KrkrPlugin 正(?)向实录 | Yesterday17's Blog 静流的青春纪念册——「サクラノ刻 -櫻の森の下を歩む-」体验版感言 | Yesterday17's Blog Project Anni 之旅 01 - 从 clap-builder 到 derive | Yesterday17's Blog [Google CTF 2021] CPP WriteUp | Yesterday17's Blog 获取 アソビステージ 的实际播放链接 | Yesterday17's Blog 90 行 Rust 代码实现 AsyncTeeReader | Yesterday17's Blog 或许还算有价值一读的文章列表 | Yesterday17's Blog 从零开始的 Seedbox 之旅 | Yesterday17's Blog [随笔]技术型博客行文迷思(1) | Yesterday17's Blog 浅谈 git fetch 的工作方式 | Yesterday17's Blog 『ソーサレス*アライヴ! ~the World's End Fallen Star~』通关感想" | Yesterday17's Blog Rust std::fmt 格式语法简述 | Yesterday17's Blog 日亚修改居住国的解决方案 | Yesterday17's Blog 【翻译】Subtyping and Variance / 子类型与变型 | Yesterday17's Blog Berd's Red Envelope 2021 WriteUp | Yesterday17's Blog 【中英对照】ALSA 音频 API 使用教程/A Tutorial on Using the ALSA Audio API | Yesterday17's Blog 从 cue_scanner.l 看 CUE Sheet 的词法单元 | Yesterday17's Blog Postman 历史记录导出的解决方案 | Yesterday17's Blog 《恋爱绮谭 不存在的夏天》通关感想 | Yesterday17's Blog [微机实验/TD-PITE] 微机接口综合实验 | Yesterday17's Blog [微机实验/TD-PITE] 键盘扫描及数码管显示实验 | Yesterday17's Blog [微机实验/TD-PITE] 数码管显示实验 | Yesterday17's Blog Airsonic Advanced+Google Drive+Caddy 部署纪实 | Yesterday17's Blog X-NUCA 2020 - hellowasm 题解 | Yesterday17's Blog [微机实验/TD-PITE] 8251 串行接口实验 | Yesterday17's Blog Node.js child_process.fork 与 env 污染 RCE | Yesterday17's Blog EP.01 「夜の向日葵」 | Yesterday17's Blog [微机实验/TD-PITE] 8254 定时/计数器实验+选做实验 | Yesterday17's Blog [JLU CTF/2020] babywasm WriteUp | Yesterday17's Blog PHP 反序列化与经典利用 | Yesterday17's Blog WebAssembly 逆向简述 | Yesterday17's Blog 『彼女、お借りします』一期完结点评 | Yesterday17's Blog [微机实验/TD-PITE] D/A 转换实验+选做实验 | Yesterday17's Blog [微机实验/TD-PITE] A/D 转换实验+选做实验 | Yesterday17's Blog 开源项目申请 JetBrains Open Source License 简单流程 | Yesterday17's Blog 微软拼音与 JetBrains 搜索快捷键冲突的解决方案 | Yesterday17's Blog [微机实验/TD-PITE] 8259 中断优先级实验+选做实验 | Yesterday17's Blog IFTTT 测试(续) | Yesterday17 IFTTT 测试 | Yesterday17's Blog [微机实验/TD-PITE] 存储器扩展实验+选做实验 | Yesterday17's Blog 新版 GCC 针对 -fdump-translation-unit 的替代方案 | Yesterday17's Blog 一次 HSTS 策略配置的排错之旅 | Yesterday17's Blog YukiNative 踩坑记——Windows 的消息队列 | Yesterday17's Blog 我是我自己——论获取 HTTPS 证书时的验证步骤 | Yesterday17's Blog 【设计文档】对 PUG 的大规模设计修订(1.1) | Yesterday17's Blog GS65 折腾记(2)加装固态,分区,Grub2 引导 Manjaro LiveCD | Yesterday17's Blog 「さくら、もゆ。」的空白字体列表——一次逆向问题定位过程实录 | Yesterday17's Blog GSuite 探索篇(1)使用 Service Account 向 Google Drive 传输文件 | Yesterday17's Blog 『サクラノ詩 -櫻の森の上を舞う-』通关感想 | Yesterday17's Blog 《ATRI -My Dear Moments-》通关感想 | Yesterday17's Blog [工具][VSCode 扩展] AegiKit——方便 Aegisub 使用的工具箱 | Yesterday17's Blog 贝塞尔曲线、字体矢量化与曲线运算 | Yesterday17's Blog NAT 类型初探 | Yesterday17's Blog
[Windows/Linux] GC553 的 Switch 完美采集之路 | Yesterday17's Blog
Yesterday17 · 2021-01-27 · via Yesterday17's Blog

ToC

  • 前言
  • Windows
  • Linux
    • 视频
      • 尝试:VLC
      • 尝试:OBS-Studio
      • 尝试:mpv
    • 那么,音频……
      • 音频在哪里?
      • mpv 播放视频+音频
      • mpv 播放音频
      • OBS 播放音频
      • PulseAudio 音频 loopback
    • Bash Script
  • 参考

前言

趁着假期摸了一块采集卡,准备在笔记本上愉快地莱莎2。本来基本已经放弃在 Linux 上玩耍了[1],但 Reddit 上的一篇帖子[2]重新勾起了我的兴趣。

正面照
正面照

帖子中的描述非常简单,好像楼主也并没有做太多的操作。但正是由于过于简单,导致后续的研究过程耗费了比较多的时间。

虽说是勾起的 Linux 下的兴趣,这篇文章中同样包含 Windows 下的部分配置内容。虽然 Windows 用得少,但不代表完全不用。Windows 下的采集非常简单,基本上可以说是完全无坑;Linux 下则是完全靠着摸索得到的方案,也算是收获颇丰吧(笑)

什么?你说 mac?你给我买一台我就写(逃

Windows

Windows 下有着官方的技术支持,过程非常简单,但我使用的并不是官方方案。一来官方方案需要额外下载软件,二是官方方案主要是为直播而生,对于直接在笔记本上使用并没有特别优化过。因此我最终选择了 PotPlayer

虽然它被钉在了耻辱柱上,但谁让它是真的香呢(悲)

PotPlayer 的主菜单中,选择「打开设备」,然后选中设备就可以了。视频输入一般是开箱即用,音频输入可能需要在下面的设置里调整一下,或者直接在 Windows 设置里把采集卡的音频输入设置成默认(?

总之就是这种不需要怎么说就能简单实现的平台 有空把这部分补补齐吧(懒

Linux

Linux 下的问题就相对复杂些了。由于官方没有提供任何 Linux 相关的技术支持,我们只能自己摸索。为了方便观察,下面的截图里使用了很多 grep 来产生高亮。

视频

首先需要解决的问题是视频源。我们插入 USB,看看 lsusb

设备成功显示。然后使用 v4l2-ctl 查看设备列表:

可以看到有两个 /dev/video,我暂且蒙在鼓里(

尝试:VLC

尝试使用 VLC 打开:

第一次打开发现提示了神秘的错误:

第二次出现了神秘的影像:

看起来要手动调整一下分辨率:

然而修改之后再尝试连接什么反应都没有了,非常神秘。重新开启 VLC,连之前的神秘影像都显示不出来了。拔掉 USB 重来,看来是不能指望 VLC 了。

尝试:OBS-Studio

OBSWindows 上的表现还是值得称道的,但在 Linux 下好像就不大行了……

不知道发生了什么事情,总之就是绿了。不大行(

尝试:mpv

mpv 在它的 Wiki 中有专门的一篇文章叙述 v4l2[3]。我们直接使用 Wiki 上提供的第一行命令:

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

然后就没有然后了,hmm……并且延迟非常可以,个人感觉比 Windows 下还低。完全没有问题了。

顺带一提,在 mpv 成功播放之后,OBS 它也可以了(

这合理吗
这合理吗

嘛,不管专门说,视频问题都解决了(

那么,音频……

解决完视频问题,问题就解决了一大半了。毕竟你可以直接把耳机接 NS 上(逃

音频在哪里?

我们先通过 pactl 来观察一下情况:

可以发现,音频源是作为一个输入设备接入的。通过 arecord 也可以看到:

同样是通过 arecord,获得设备的 ID

现在我们来尝试录制一下:

耳机里出现了熟悉的 BGM

mpv 播放视频+音频

同样是在那篇 Wiki 中,mpv 提供了和 v4l2 一起播放音频的方案:通过 --audio-file 指定音频文件为 pulseaudio 设备。

但这个方案在测试中发现并不能让 mpv 发出声音。文章最后的 untested suggestion 倒是可以播放音频,但发出声音的代价是视频的延迟激增到了 2 秒以上,并且伴随着极低的 FPS。这条路可以算是彻底不可行了。

mpv 播放音频

和上面一条路有点像,但又有所区别。既然 mpv 一起放不行,那分开放呢?

事实证明,分开播放确实可行。由于视频采集和音频采集各自的延时都很低,因此分开播放并不会产生太大的 desync 问题。但是在播放过程中,时常会有扰人的 Audio device underrun 出现,伴随着由此产生的爆音:

日志输出
日志输出

于是,这条路也只好放弃了。

OBS 播放音频

既然是 PulseAudio 源,那么使用 OBS 的音频输入捕获怎么样呢?我们来尝试一下。首先是音频源,直接选中我们的设备:

可以看到,底部已经有声音的标识了:

但这时候我们还不能听到声音,需要在高级音频属性里打开音频设备的监听:

此时桌面音频就和音频输入捕获保持同步了,我们也可以听到音乐了:

经过测试,OBS 的音频输入捕获工作得非常完美,不存在 mpv 的爆音问题。但为了播放音频而多打开一个软件实在是太不优雅,于是这条路作为备用方案,暂时搁置。

PulseAudio 音频 loopback

到了这里,我们不妨想一想,绕了这么多圈,用了这么多软件,我们这个需求的本质是什么?

在我之前翻译的 ALSA 音频 API 使用教程 中较为详细地描述了 ALSA 工作的一些基本概念,而 PulseAudio 则是在 ALSA 和应用程序之间充当着代理的角色。

回到正题,我们希望能够将采集卡采集到的音频在计算机上播放,此时采集卡相当于一个输入设备。对于我们而言,平时最常用的音频输入设备麦克风,此时的采集卡也充当着这样的角色。

回过头来,在很多情况下,我们都能听到麦克风录制的自己的声音,这通常用在麦克风测试的过程中,通过播放麦克风录制的音频,确保麦克风配置正确。这样的行为就是 loopback

PulseAudio 中,通过 module-loopback 模块开启音频源的 loopback,我们可以通过 pactl 来启用它:

启用过程中要指定 source 的 id
启用过程中要指定 source 的 id

随着 BGM 的响起,这场和采集卡斗智斗勇的旅程也就这样告下帷幕了。

Bash Script

最后就是一键脚本时间了。保存脚本为 ccp.sh,你就可以通过 ccp 直接开始摸鱼了。

幸福往往是摸得透彻,而敬业的心却常常隐藏

简单来说,功能有:

  • 双击全屏
  • ESC 退出全屏
  • Ctrl+C 退出
  • F10 截屏到 pwd 或指定目录

具体实现看源码就是了(笑

实际使用中有一个问题,就是在 USB 刚刚接入的时候就运行脚本,有大概率音频回放没有声音。我个人的猜测是采集卡还没有进入工作状态,所以建议等待一段时间(也不用太长)再运行就没有问题了。

#!/bin/bash

PROGNAME="$(basename $0)"

# mpv settings

MPV_FULLSCREEN=no

MPV_VIDEO_SIZE=1920x1080

MPV_TITLE="Live Gamer Ultra-Video"

MPV_SCREENSHOT_DIR="$(pwd)"

GETOPT_ARGS=$(getopt -o hc:w:g:de:nm: -l "help","fullscreen","fs" -n "$PROGNAME" -- "$@")

[[ $? -ne 0 ]] && exit 1

eval set -- "$GETOPT_ARGS"

while :; do

case "$1" in

-h|--help)

usage

exit 0

;;

--video-size)

shift

VIDEO_SIZE="$1"

shift

;;

--video-device)

shift

MPV_VIDEO_SIZE="$1"

shift

;;

--fullscreen|--fs)

shift

MPV_FULLSCREEN=yes

;;

--screenshot-directory)

shift

MPV_SCREENSHOT_DIR="$1"

shift

;;

--title)

shift

MPV_TITLE="$1"

SHIFT

;;

--)

shift

break

;;

esac

done

function requirement_check() {

which lsusb > /dev/null 2>&1 &&

which pactl > /dev/null 2>&1 &&

which v4l2-ctl > /dev/null 2>&1 &&

which mpv > /dev/null 2>&1 ||

(echo "Please make sure lsusb, pactl, v4l2-ctl and mpv exist in your system!"; exit 1)

}

function device_check() {

lsusb | grep -q AVerMedia

[[ $? -ne 0 ]] && echo "No capture card found!" && exit 1

}

function get_source() {

PACTL_SOURCE=$(LC_ALL=C pactl list sources | grep AVerMedia -B 3 | grep Source | cut -d '#' -f 2)

}

function audio_on() {

# get audio source id

get_source

# retry once

# may fail if audio source has not been initialized

[[ -z "$PACTL_SOURCE" ]] && sleep 1 && get_source

[[ -z "$PACTL_SOURCE" ]] && echo "Failed to get audio source!" && exit 1

# enable audio loopback

PACTL_MODULE_ID=$(pactl load-module module-loopback source="$PACTL_SOURCE")

[[ $? -ne 0 ]] && echo "Failed to initialize audio playback!" && exit 1

}

function audio_off() {

if [ ! -z "$PACTL_MODULE_ID" ]; then

pactl unload-module $PACTL_MODULE_ID

fi

}

function video_config() {

# create tmp config dir

CONFIG_DIR=$(mktemp -d -t ccplay-XXXXXXXX)

# create tmp config file

CONFIG_FILE=$(cat << EOF

osc=no

input-default-bindings=no

osd-font-size=24

profile=low-latency

untimed=yes

# screenshot

screenshot-format=png

screenshot-template=screenshot-%tY-%tm-%td-%tH:%tM:%tS

screenshot-directory=$MPV_SCREENSHOT_DIR

title=$MPV_TITLE

fullscreen=$MPV_FULLSCREEN

demuxer-lavf-o=video_size=$MPV_VIDEO_SIZE

EOF

)

echo "$CONFIG_FILE" > "$CONFIG_DIR/mpv.conf"

# create tmp input config

CONFIG_FILE=$(cat << EOF

MBTN_LEFT_DBL cycle fullscreen

ESC set fullscreen no

ctrl+c quit 0

F10 screenshot

EOF

)

echo "$CONFIG_FILE" > "$CONFIG_DIR/input.conf"

}

function video_on() {

# default video device

if [ -z "$VIDEO_DEVICE" ]; then

VIDEO_DEVICE=$(v4l2-ctl --list-devices | grep 'Live Gamer Ultra-Video' -A 5 | grep '/dev' | head -n 1 | xargs)

fi

# launch mpv

mpv "av://v4l2:$VIDEO_DEVICE" --config-dir="$CONFIG_DIR" &>/dev/null &

}

function start() {

requirement_check

device_check

audio_on

video_config

video_on

wait $!

# https://github.com/mpv-player/mpv/blob/master/DOCS/man/mpv.rst#exit-codes

# file could not be played, retry once

if [ $? -eq 2 ]; then

video_on

wait

fi

}

function clean() {

audio_off

yes | rm "$CONFIG_DIR"/*.conf &>/dev/null

yes | rmdir "$CONFIG_DIR" &>/dev/null

}

trap clean EXIT

start

参考

  1. https://forum.manjaro.org/t/trying-to-connect-avermedia-capture-card/38613
  2. https://www.reddit.com/r/linux/comments/c0vy1c/recording_4k_linux_including_gaming_footage_using/
  3. https://github.com/mpv-player/mpv/wiki/Video4Linux2-Input