打印
[资料干货]

CH9338应用:开源项目FFMPEG实现镜像屏及拓展屏

[复制链接]
369|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主

[i=s] 本帖最后由 lzy871238103 于 2025-7-25 16:13 编辑 [/i]<br /> <br />

项目背景

最近由于程序调试,需要在两台电脑间需要频繁拷贝文件,拿着U盘每天在两台电脑之间插拔几十次,实在是苦不堪言,于是某宝买了一个USB KMFU对拷线,文件传输只需拖拽鼠标,拷贝速度能有20多MB/s ,完全满足日常所需。 甚至可以在KMFU对拷线自身的HUB上插一个固态硬盘,超大文件传输至固态硬盘后,通过KMFU配置界面将固态硬盘切换到对端电脑上,极为方便!

研究了下该设备,发现可以搭配FFMPEG项目做一些有趣的事情,于是开始了尝试的第一步。

CH9338简介

CH9338是一款用于实现高速USB对拷功能的专用芯片,由CH9338打造的USB KMFU (Keyboard/Mouse/File/USB HUB)对拷共享HUB可用于多台计算机键盘/鼠标/触摸板的跨屏共享,支持Windows、macOS、Android操作系统。

PC间跨屏使用时等同单台PC内操作,省去鼠标键盘、节省空间,同时两台PC间的剪贴板、文件支持共享。

USB KMFU 还提供USB HUB和外设共享功能,无需用户插拔USB设备,实现在两台PC设备间的3个HUB端口自由切换,便捷的同时延长了USB接口的使用寿命。

KMFUHelp.jpg

与传统的USB对拷线相比,KMFU有很多创新,例如: 边界防误穿屏:鼠标穿屏灵敏度6挡可调。 灵敏度高----->鼠标切屏丝滑无阻力 灵敏度低----->鼠标切屏有阻力,减少误操作

9af525e4c0cb48bfab11978817c49c92.gif

外设共享:KMFU自带HUB上USB设备,可随时切换至任意一端。

2.gif

数据透传通道:提供一条USB数据透传通道。 本次博客主要是利用CH9338的USB数据透传通道,借助FFMPEG项目传输屏幕数据,实现镜像屏及拓展屏功能。 CH9338详细介绍可参考:

用于双机跨屏共享文件和数据的USB对连芯片-CH9338(https://www.wch.cn/news/726.html.html)

项目最终效果

镜像屏:

3.gif

拓展屏:

4.gif

FFMPEG简介

FFmpeg是一款开源的跨平台音视频处理工具集,提供录制、转换、流传输等完整解决方案。其核心库包括libavcodec(编解码)、libavformat(封装格式处理)、libavfilter(滤镜处理)等等。 用户可通过项目内置的ffmpeg、ffplay程序快速实现基础功能,也可通过API实现复杂自定义功能。

11.png

FFMPEG官网:https://ffmpeg.org/ FFMPEG源码库:https://github.com/FFmpeg/FFmpeg

添加自定义传输协议

FFMPEG本身实现了一些常见的传输协议,如网络的TCP/UDP、HTTP/HTTPS、FTP/FTPS等等。这些协议的驱动位于项目中的libavformat库中,我们将参照原有的实现,添加我们自定义的传输协议。

FFMPEG源码修改

FFMPEG工程中主要是依靠URLProtocol以及URLContext两个结构体实现传输协议的创建、使用和销毁。 URLProtocol 是协议的操作接口定义,其中定义了打开关闭、读写接口等,需要我们自行实现。 URLContext则是协议的运行时实例,管理协议的实际状态。其中成员 priv_data指向自定义的私有化结构体。 CH9338定义的私有化结构体如下:

typedef struct CH375Context {
    const AVClass *class;  // 固定值,保证能为上层提供基地址
    int dev_index;   //设备的index号
    int w_endpoint;  //设备的写端点号
    int r_endpoint;  //设备的读端点号
    int rw_timeout;  //读写超时时间
    char* dev_name;  //设备标识符
} CH375Context;

除此外,还需设定默认值并为用户态提供修改接口:

#define OFFSET(x) offsetof(CH375Context, x)
static const AVOption ch375_options[] = {
    { "device_index", "CH375 device serial number", OFFSET(dev_index), AV_OPT_TYPE_INT, { .i64 = 0 }, -1, 12, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_DECODING_PARAM },
    { "w_endpoint", "CH375 write endpoint", OFFSET(w_endpoint), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 8, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_DECODING_PARAM },
    { "r_endpoint", "CH375 read endpoint", OFFSET(r_endpoint), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 8, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_DECODING_PARAM },
    { "rw_timeout", "CH375 read/write timeout in milliseconds", OFFSET(rw_timeout), AV_OPT_TYPE_INT, { .i64 = 30 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_DECODING_PARAM },
    { "dev_name", "CH75 device identifier", OFFSET(dev_name), AV_OPT_TYPE_STRING, {.str = "VID_1A86&PID_8022&MI_03"}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_DECODING_PARAM },
    { NULL }
};

接下来则是实现读写等基本功能,详见代码。

以初始化输入设备为例,用户在程序中使用 avformat_open_input函数,函数会根据提供的URL地址去匹配协议,按照 priv_data_size去申请内存,创建我们声明的私有结构体,并将URLContext结构体中的 void *priv_data指针指向CH375Context 。接着则是正常的打开、读取。。。 调用的大致流程如下:

22.png

修改后的FFMPEG编译(以Windows系统为例)

Windows平台下FFMPEG的编译可使用MSYS2或Cygwin,这里以MSYS2为例。

前期工作

安装MinGW-w64

pacman -S mingw-w64-x86_64-toolchain

安装git、cmake等:

pacman -S git
pacman -S make
pacman -S automake
pacman -S autoconf
pacman -S perl
pacman -S libtool
pacman -S mingw-w64-i686-cmake
pacman -S pkg-config
pacman -S mingw-w64-x86_64-SDL2

安装SDL2,FFMPEG工程中ffplay默认使用SDL2进行渲染播放:

pacman -S mingw-w64-x86_64-SDL2

源码编译

  1. 自行git x264、fdk-aac、mp3lame、libvpx源码,并 make install在自定义目录中。
  2. git FFMPEG源码,配置编译选项:
./configure \
    --prefix=/home/xxx/ffmpeg/build/ffmpeg-7.7.1 \
    --arch=x86_64 \             #指定编译架构
    --enable-shared \
    --enable-protocol=ch375 \   #使能自定义的协议
    --enable-ffplay \ 
    --enable-sdl2 \ 
    --enable-gpl \
    --enable-libfdk-aac \
    --enable-nonfree \
    --enable-libvpx \
    --enable-libx264 \
    --enable-libmp3lame \
    --extra-cflags="-I/mingw64/include/SDL2" \
    --extra-ldflags="-L/mingw64/lib" \
    --extra-cflags="-I/home/xxx/ffmpeg/build/libfdk-aac/include" \
    --extra-ldflags="-L/home/xxx/ffmpeg/build/libfdk-aac/lib" \
    --extra-cflags="-I/home/xxx/ffmpeg/build/libvpx/include" \
    --extra-ldflags="-L/home/xxx/ffmpeg/build/libvpx/lib" \
    --extra-cflags="-I/home/xxx/ffmpeg/build/libx264/include" \
    --extra-ldflags="-L/home/xxx/ffmpeg/build/libx264/lib" \
    --extra-cflags="-I/home/xxx/ffmpeg/build/libmp3lame/include" \
    --extra-ldflags="-L/home/xxx/ffmpeg/build/libmp3lame/lib"

常见错误: 如遇 X264无法找到环境目录,例如:

33.png

可按以下方式手动声明,并重新 configure

 export PKG_CONFIG_PATH=/home/xxx/ffmpeg/build/libx264/lib/pkgconfig:$PKG_CONFIG_PATH
  1. make install configure如无错误,正常 make install即可。 如遇到其他问题,可自行百度FFMPEG编译教程。

  2. 验证 编译完成后,运行ffmpeg.exe、ffplay.exe、ffprobe.exe任意程序,提示缺少的DLL可在源码目录及MSYS2安装目录下找到。 我们命令行调用FFMPEG,使用 -protocols命令列出当前支持的传输协议,查询当前是否支持CH9338。

    44.png

应用场景

可以利用CH9338提供的透传通道,搭配FFMPEG去实现屏幕的镜像与拓展。

屏幕镜像

屏幕镜像的本质是通过gdigrab去抓取屏幕、通过CH9338发送H264数据、另一台PC中使用ffplay.exe解码并播放画面。 ffmpeg.exe抓屏并传输:

ffmpeg \
-probesize 32 \
-fflags flush_packets \
-fflags discardcorrupt \
-flags low_delay \
-analyzeduration 0 \
-f gdigrab \
-framerate 30 \    #指定30帧录制传输
-i desktop \
-preset ultrafast \  #指定ultrafast编码预设
-c:v libx264 \    #指定libx264编码
-crf 28 \         #H264恒定速率因子,数值越小,画质越高
-g 1 \
-tune zerolatency \
-pix_fmt yuv420p \
-max_delay 0 \
-bf 0 \          #取消B帧,减小延迟
-f h264 \   
"ch375://download"  #声明传输协议

ffplay.exe接收并实时显示:

ffplay \
-fflags nobuffer \
-analyzeduration 0 \
-probesize 32 \
-flags low_delay \
-flags2 fast \
-fflags discardcorrupt \
"ch375://upload"

屏幕拓展

  1. 创建虚拟显示屏

首先需要一个虚拟显示驱动来创建副屏。 这里推荐借助parsec-vdd项目来完成。 Parsec VDD 可在 Windows 10+ 系统上启用虚拟显示,能够很方便的创建各种自定义尺寸的屏幕。 项目仓库:

https://github.com/nomi-san/parsec-vdd/tree/main

55.png

  1. ffmpeg.exe抓屏并传输
ffmpeg \
-probesize 32 \
-fflags flush_packets \
-fflags discardcorrupt \
-flags low_delay \
-analyzeduration 0 \
-f gdigrab \
-framerate 30 \
-offset_x 1920 \    #主屏幕尺寸为1920*1080,因此抓取x坐标偏移1920
-offset_y 0 \
-video_size 1920x1080 \    #创建的副屏尺寸为1920*1080
-i desktop \
-preset ultrafast \
-c:v libx264 \
-crf 28 \
-g 1 \
-tune zerolatency \
-pix_fmt yuv420p \
-max_delay 0 \
-bf 0 \
-f h264 \
"ch375://download"

ffplay.exe接收并实时显示命令不变,仍是:

ffplay \
-fflags nobuffer \
-analyzeduration 0 \
-probesize 32 \
-flags low_delay \
-flags2 fast \
-fflags discardcorrupt \
"ch375://upload"

总结

项目所使用的WCHKMFU相关库文件可联系官方技术支持索取:

技术邮箱:tech@wch.cn

本文利用CH9338芯片提供的USB透传通道完成屏幕数据的传输。修改的FFMPEG的源码、编译后的ffmpeg.exe、ffplay.exe,以及通过FFMPEG API调用CH9338抓屏推流及拉流DEMO均已上传至Github,详见:

https://github.com/ZhiyuanYuanNJ/CH9338_FFMPEG_Screen

使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

6

主题

11

帖子

0

粉丝