• Linux 下编译和交叉编译FFmpeg、OpenCV(contrib )库


    目录

    一、Linux下FFmpeg库的编译

    1.1 yasm库

    1.2 安装X264

    1.3 安装FFmepg

    1.4 实验

    报错

    二、Linux 下OpenCV库的编译

    三、环境变量设置

    四、FFmpeg Linux交叉编译

    4.1 FFmpeg不依赖其他库编译

    4.2 FFmpeg编译依赖库(如X264)

    以上3部分验证过得,是正确的,第四步没验证

    五、OpenCV的交叉编译


    一、Linux下FFmpeg库的编译

    1.1 yasm库

    hkx@ubuntu:~$ wget http://www.nasm.us/pub/nasm/releasebuilds/2.13.01/nasm-2.13.01.tar.xz

    手动解压

    cd ~
    hkx@ubuntu:~$ cd nasm-2.13.01
    hkx@ubuntu:~/nasm-2.13.01$ ./configure
    hkx@ubuntu:~/nasm-2.13.01$ make -j8&& sudo make install

    上面的hkx@ubuntu:~$ ./configure这一步可以设置你的安装路径等,如:

    hkx@ubuntu:~/nasm-2.13.01$ ./configure --prefix=/home/hkx/nasm

    wc,点的太快忘记配置编译后库的安装路径了

    nasm是x264的依赖,旧版本X264依赖是yasm

    1.2 安装X264

    cd ~

    hkx@ubuntu:~$ git -C x264 pull 2> /dev/null || git clone --depth 1 \ https://gitee.com/mirrors_addons/x264.git

    hkx@ubuntu:~/x264$ cd x264
    hkx@ubuntu:~/x264$ ./configure --enable-shared --enable-static
    hkx@ubuntu:~/x264$ make -j8&& sudo make install

    sudo apt-get install libx11-dev
    sudo apt-get install xorg-dev

    git这一步\是换行符,注意一下,还有git需要注册账号,麻烦,换个方式吧,直接下载,下载完 cd x264开始安装

    x264, the best H.264/AVC encoder - VideoLAN

    1.3 安装FFmepg

    FFmpeg源码下载地址:Index of /releases

    hkx@ubuntu:~/ffmpeg-3.4.9$./configure --enable-static --enable-shared  --prefix=/home/hkx/ffmpeg_3_4_9 --enable-gpl --enable-nonfree --enable-libx264 --enable-ffplay

    make  -j8

    make install

    如果需要ffmpeg命令录音需要在配置编译选项前执行:

    sudo apt-get install libasound2-dev

    按照如上配置完成胡没有ffplay命令生成,ffplay依赖SDL库,需下载源码,

    SDL2-2.0.14 (linuxfromscratch.org)

    编译之前的依赖:

    1. apt-get install libasound2-dev
    2. apt-get install libpulse-dev
    3. apt-get install libx11-dev
    4. apt-get install xorg-dev
    5. #编译
    6. ./configure
    7. make && make install

    安装完成,配库和BIN的环境

    BIN:

    sudo gedit /etc/profile

    最后加入:

    export PATH=xxx:PATH   xxx表示你的bin文件路径

    source /etc/profile   立即生效

     出现error while loading shared libraries的原因:

    1-1. 不存在该共享库,如果是这个原因,需要下载或者编译该共享库先了。

    1-2. 存在该共享库,但是找不到或者共享库的不对

    若是1-2,则sudo gedit /etc/ld.so.conf,输入你库的路径,保存退出,然后执行sudo ldconfig 来重新装载/etc/ld.so.cache文件

    系统查看共享库的过程:首先查找 /etc/ld.so.cache文件,如果找不到就查找环境变量里的LD_LIBRARY_PATH的值,如果找到了就到对应的目录加载该共享库,如果找不到就报error while loading shared libraries错误了;

     而/etc/ld.so.cache文件的内容是根据,

    /lib目录、

    /usr/lib目录、

    /etc/ld.so.conf(/etc/ld.so.conf文件包含/etc/ld.so.conf.d下所有conf文件)文件、

     这三个的内容生成的。

    https://www.cnblogs.com/yongfengnice/p/6777930.html

    至此,FFmpeg的安装已经完成,但1.4实验的时候没有ffplay命令,查了一下,说是缺少SDL库

    ffplay编译主要依赖sdl2环境(针对ffmpeg 3.x版本),未安装sdl2的情况下,不会自动生成关于ffplay的编译选项。

    SDL(Simple DirectMedia Layer)是一个跨平台的多媒体和游戏开发包,提供2D,音频,事件驱动,多线程和定时器等服务,它使用C语言写成,提供了多种控制图像、声音、输出的函数,让开发者只要用相同或是相似的代码就可以开发出跨多个平台(Linux、Windows、Mac OS X等)的应用软件。

    可以通过下面两个命令安装sdl2组件,dev后缀表示安装路径下会包含头文件及动态库等在开发环境下需要的文件

    sudo apt-get install libsdl2-2.0

    sudo apt-get install libsdl2-dev

    如上若还有问题,那么看下面的:

    安装SDL依赖

    安装编译SDL

    重新编译FFmpeg

    1.4 实验

    采集压缩成H264

    ffmpeg -f video4linux2 -i "/dev/video0" -vcodec libx264 -pix_fmt yuv420p mycamera.h264

    报错

    原因是,虚拟机没连接摄像头,打开虚拟机,可移动设备那里连接

    使用上述命令,可以采集摄像头数据,并编码成h264文件。下面详细讲解一下这些参数。
    -f video4linux2,表示采用video4linux2驱动程序。
    -i “/dev/video0”,表示输入数据的地址是“/dev/video0”,这个设备地址,就是我们的摄像头设备地址。
    -vcodec libx264,表示采用libx264编解码器。
    -pix_fmt yuv420p,表示编码的数据采用yuv420p。
    mycamera.h264,这个就是我们最终的编码好的h264数据

    播放:

    ffplay mycamera.h264

    编解码

    1. 打开设备
    2. 打开编码器
    3. 采集摄像头数据,为yuyv422格式
    4. 转换摄像头数据,从yuyv422转成yuv420p
    5. 编码yuv420p数据为h264数据,并写成文件

    linux下使用ffmpeg采集摄像头数据并编码成h264文件_snail_hunan的博客-CSDN博客_-vcodec libx264

    以上问题待解决,有方法,未验证,不影响如下操作;

    采集yuyv422,640*480,30fps,编码有格式转换。

    编译命令:(尽管是C代码)

    g++ capture.cpp -I/home/hkx/ffmpeg_3_4_9/include -L/home/hkx/ffmpeg_3_4_9/lib -lavformat -lavutil -lavdevice  -lavcodec -lm -o capture

    代码编译错误参考链接

    引入ffmpeg编译错误taking address of temporary array_fantasy_arch的博客-CSDN博客_taking address of temporary array

    Linux下基于ffmpeg音视频解码_IT_阿水的博客-CSDN博客_linux 视频解码器

    1. #include <string.h>
    2. extern "C"
    3. {
    4. #include <libavdevice/avdevice.h>
    5. #include <libavformat/avformat.h>
    6. #include <libavutil/avutil.h>
    7. }
    8. #define V_WIDTH 640
    9. #define V_HEIGHT 480
    10. //@brief
    11. // return
    12. static AVFormatContext *open_dev() {
    13. int ret = 0;
    14. char errors[1024] = {
    15. 0,
    16. };
    17. // ctx
    18. AVFormatContext *fmt_ctx = NULL;
    19. AVDictionary *options = NULL;
    20. //摄像头的设备文件
    21. char *devicename = "/dev/video0";
    22. // register video device
    23. avdevice_register_all();
    24. // get format
    25. AVInputFormat *iformat = av_find_input_format("video4linux2");
    26. av_dict_set(&options, "video_size", "640x480", 0);
    27. av_dict_set(&options, "framerate", "30", 0);
    28. av_dict_set(&options, "pixel_format", "yuyv422", 0);
    29. // open device
    30. ret = avformat_open_input(&fmt_ctx, devicename, iformat, &options);
    31. if (ret < 0) {
    32. av_strerror(ret, errors, 1024);
    33. fprintf(stderr, "Failed to open video device, [%d]%s\n", ret, errors);
    34. return NULL;
    35. }
    36. return fmt_ctx;
    37. }
    38. static void open_encoder(int width, int height, AVCodecContext **enc_ctx) {
    39. int ret = 0;
    40. AVCodec *codec = NULL;
    41. avcodec_register_all();
    42. codec = avcodec_find_encoder_by_name("libx264");
    43. if (!codec) {
    44. printf("Codec libx264 not found\n");
    45. exit(1);
    46. }
    47. *enc_ctx = avcodec_alloc_context3(codec);
    48. if (!enc_ctx) {
    49. printf("Could not allocate video codec context!\n");
    50. exit(1);
    51. }
    52. // SPS/PPS
    53. (*enc_ctx)->profile = FF_PROFILE_H264_HIGH_444;
    54. (*enc_ctx)->level = 50; //表示LEVEL是5.0
    55. //设置分辫率
    56. (*enc_ctx)->width = width; // 640
    57. (*enc_ctx)->height = height; // 480
    58. // GOP
    59. (*enc_ctx)->gop_size = 250;
    60. (*enc_ctx)->keyint_min = 25; // option
    61. //设置B帧数据
    62. (*enc_ctx)->max_b_frames = 3; // option
    63. (*enc_ctx)->has_b_frames = 1; // option
    64. //参考帧的数量
    65. (*enc_ctx)->refs = 3; // option
    66. //设置输入YUV格式
    67. (*enc_ctx)->pix_fmt = AV_PIX_FMT_YUV420P;
    68. //设置码率
    69. (*enc_ctx)->bit_rate = 600000; // 600kbps
    70. //设置帧率
    71. (*enc_ctx)->time_base = (AVRational){1, 30}; //帧与帧之间的间隔是time_base
    72. (*enc_ctx)->framerate = (AVRational){30, 1}; //帧率,每秒 30
    73. ret = avcodec_open2((*enc_ctx), codec, NULL);
    74. if (ret < 0) {
    75. printf("Could not open codec: %s!\n", av_err2str(ret));
    76. exit(1);
    77. }
    78. }
    79. static AVFrame *create_frame(int width, int height) {
    80. int ret = 0;
    81. AVFrame *frame = NULL;
    82. frame = av_frame_alloc();
    83. if (!frame) {
    84. printf("Error, No Memory!\n");
    85. goto __ERROR;
    86. }
    87. //设置参数
    88. frame->width = width;
    89. frame->height = height;
    90. frame->format = AV_PIX_FMT_YUV420P;
    91. // alloc inner memory
    92. ret = av_frame_get_buffer(frame, 32); //32 位对齐
    93. if (ret < 0) {
    94. printf("Error, Failed to alloc buffer for frame!\n");
    95. goto __ERROR;
    96. }
    97. return frame;
    98. __ERROR:
    99. if (frame) {
    100. av_frame_free(&frame);
    101. }
    102. return NULL;
    103. }
    104. static void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *newpkt,
    105. FILE *outfile) {
    106. int ret = 0;
    107. if (frame) {
    108. printf("send frame to encoder, pts=%lld", frame->pts);
    109. }
    110. //送原始数据给编码器进行编码
    111. ret = avcodec_send_frame(enc_ctx, frame);
    112. if (ret < 0) {
    113. printf("Error, Failed to send a frame for enconding!\n");
    114. exit(1);
    115. }
    116. //从编码器获取编码好的数据
    117. while (ret >= 0) {
    118. ret = avcodec_receive_packet(enc_ctx, newpkt);
    119. //如果编码器数据不足时会返回 EAGAIN,或者到数据尾时会返回 AVERROR_EOF
    120. if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
    121. return;
    122. } else if (ret < 0) {
    123. printf("Error, Failed to encode!\n");
    124. exit(1);
    125. }
    126. fwrite(newpkt->data, 1, newpkt->size, outfile);
    127. fflush(outfile);
    128. av_packet_unref(newpkt);
    129. }
    130. }
    131. void yuyv422ToYuv420p(AVFrame *frame, AVPacket *pkt) {
    132. int i = 0;
    133. int yuv422_length = V_WIDTH * V_HEIGHT * 2;
    134. int y_index = 0;
    135. // copy all y
    136. for (i = 0; i < yuv422_length; i += 2) {
    137. frame->data[0][y_index] = pkt->data[i];
    138. y_index++;
    139. }
    140. // copy u and v
    141. int line_start = 0;
    142. int is_u = 1;
    143. int u_index = 0;
    144. int v_index = 0;
    145. // copy u, v per line. skip a line once
    146. for (i = 0; i < V_HEIGHT; i += 2) {
    147. // line i offset
    148. line_start = i * V_WIDTH * 2;
    149. for (int j = line_start + 1; j < line_start + V_WIDTH * 2; j += 4) {
    150. frame->data[1][u_index] = pkt->data[j];
    151. u_index++;
    152. frame->data[2][v_index] = pkt->data[j + 2];
    153. v_index++;
    154. }
    155. }
    156. }
    157. void rec_video() {
    158. int ret = 0;
    159. int base = 0;
    160. int count = 0;
    161. // pakcet
    162. AVPacket pkt;
    163. AVFormatContext *fmt_ctx = NULL;
    164. AVCodecContext *enc_ctx = NULL;
    165. // set log level
    166. av_log_set_level(AV_LOG_DEBUG);
    167. // create file
    168. char *yuvout = "./video.yuv";
    169. char *out = "./video.h264";
    170. FILE *yuvoutfile = fopen(yuvout, "wb+");
    171. FILE *outfile = fopen(out, "wb+");
    172. //打开设备
    173. fmt_ctx = open_dev();
    174. //打开编码器
    175. open_encoder(V_WIDTH, V_HEIGHT, &enc_ctx);
    176. //创建 AVFrame
    177. AVFrame *frame = create_frame(V_WIDTH, V_HEIGHT);
    178. //创建编码后输出的Packet
    179. AVPacket *newpkt = av_packet_alloc();
    180. if (!newpkt) {
    181. printf("Error, Failed to alloc avpacket!\n");
    182. goto __ERROR;
    183. }
    184. // read data from device
    185. while (((ret = av_read_frame(fmt_ctx, &pkt)) == 0) && (count++ < 100)) {
    186. int i = 0;
    187. av_log(NULL, AV_LOG_INFO, "packet size is %d(%p)\n", pkt.size,
    188. pkt.data);
    189. // YUYVYUYVYUYVYUYV YUYV422
    190. // YYYYYYYYUUVV YUV420
    191. yuyv422ToYuv420p(frame, &pkt);
    192. fwrite(frame->data[0], 1, 307200, yuvoutfile);
    193. fwrite(frame->data[1], 1, 307200 / 4, yuvoutfile);
    194. fwrite(frame->data[2], 1, 307200 / 4, yuvoutfile);
    195. frame->pts = base++;
    196. encode(enc_ctx, frame, newpkt, outfile);
    197. //
    198. av_packet_unref(&pkt); // release pkt
    199. }
    200. encode(enc_ctx, NULL, newpkt, outfile);
    201. __ERROR:
    202. if (yuvoutfile) {
    203. // close file
    204. fclose(yuvoutfile);
    205. }
    206. // close device and release ctx
    207. if (fmt_ctx) {
    208. avformat_close_input(&fmt_ctx);
    209. }
    210. av_log(NULL, AV_LOG_DEBUG, "finish!\n");
    211. return;
    212. }
    213. int main(int argc, char *argv[]) {
    214. rec_video();
    215. return 0;
    216. }

    二、Linux 下OpenCV库的编译

    预参考:linux下的opencv-4.5.5 及 opencv_contrib 扩展模块安装_顽張先生的博客-CSDN博客

    如下是本课题组其他一位同学的文章,作为参考:

    ubuntu18.04下opencv4.5.4编译_DemoFY的博客-CSDN博客

    Linux下编译Opencv和contrib_偏安一隅,占山为王的博客-CSDN博客_linux 编译opencv

    三、环境变量设置

    3.1环境变量PATH

    这个环境变量,表示“可执行程序的查找路径”。

    1、查看PATH的值,输入“echo $PATH”。

    2、修改PATH的值的方法

    (1)暂时性修改

    在命令行输入“export PATH=PATH:xxx”,xxx表示新添加的内容。
    这样修改的话,关闭终端后就失效了。
    (2)永久性修改

    修改~/.bashrc(建议)或系统级别的/etc/profile文件,在其中添加“export PATH=xxx:$PATH”,然后“source .bashrc”。xxx表示新添加的内容。
    Source命令也称为“点命令”,通常用于重新执行刚修改的初始化文件,使之立即生效,而不必注销并重新登录。

    在/etc/profile下修改,一个PATH多个值用:隔开,你只写一个,其他的会覆盖掉

    export PATH=/home/hkx/ffmpeg_3_4_9/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:PATH

    3.2 环境变量LD_LIBRARY_PATH

    这个环境变量,表示“动态库的查找路径”。

    1、查看LD_LIBRARY_PATH的值,输入“echo $LD_LIBRARY_PATH”。

    2、修改LD_LIBRARY_PATH的值的方法

    (1)暂时性修改

    在命令行输入“export  LD_LIBRARY_PATH=LD_LIBRARY_PATH:xxx”,xxx表示新添加的内容。
    这样修改的话,关闭终端后就失效了。
    (2)永久性修改

    修改~/.bashrc或系统级别的/etc/profile文件,在其中添加“export PATH=xxx:$LD_LIBRARY_PATH”,然后“source .bashrc”。xxx表示新添加的内容。
    (3)不修改LD_LIBRARY_PATH

    在/etc/ld.so.conf中添加一行/usr/local/mysql/lib,然后在命令行输入命令“ldconfig”。
    ldconfig命令的作用:在默认搜寻目录(/lib和/usr/lib))和动态库配置文件/etc/ld.so.conf内所列的目录中,搜索出可共享的动态链接库(格式如lib*、.so*)),进而创建出动态装入程序(ld.so)所需要的链接和缓存文件。缓存文件默认为/etc/ld.so.cache,该文件保存着已排好序的动态链接库的名字列表。

     linux环境变量设置方法(PATH等环境变量)_天糊土的博客-CSDN博客_查看ld_library_path

    ~/ .bashrc或~/.bash_profile是对当前用户有效,而/etc/profile是对所有用户有效,source后无效的话,就重启一下

    编译FFmpeg用到的也就上述两个全局路径,其他遇到再补充吧!

    四、FFmpeg Linux交叉编译

    本文所在系统环境ubuntu18.04

    交叉编译工具链gcc-linaro-5.5.0-2017.10-x86_64_aarch64-linux-gnu

    4.1 FFmpeg不依赖其他库编译

    ffmpeg arm linux编译_aarch64-linux-gnu+交叉编译x264_csdnLN的博客-CSDN博客_aarch64 ffmpeg

    1. ./configure --enable-cross-compile --target-os=linux --arch=arm64 \
    2. --cross-prefix=--cross-prefix=/home/hkx/gcc-linaro-5.5.0-2017.10-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu- \
    3. --cc=--cross-prefix=/home/hkx/gcc-linaro-5.5.0-2017.10-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc \
    4. --prefix=/home/hkx/arm_ffmpeg \
    5. --disable-asm --enable-parsers --disable-decoders --disable-debug --enable-ffmpeg --enable-shared --disable-static --disable-stripping --disable-doc --disable-yasm --disable-libx264

    4.2 FFmpeg编译依赖库(如X264)

    ./configure --enable-cross-compile --target-os=linux --arch=arm64 \
    --cross-prefix=--cross-prefix=/home/hkx/gcc-linaro-5.5.0-2017.10-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu- \
    --cc=--cross-prefix=/home/hkx/gcc-linaro-5.5.0-2017.10-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc \
    --prefix=/home/hkx/arm_ffmpeg \
    --disable-asm  --enable-parsers --disable-decoders --disable-debug --enable-ffmpeg --enable-shared --disable-static --disable-stripping --disable-doc --disable-yasm --enable-libx264

    --extra-cflags=-I/home/jiajia/work/FFmpeg/include --extra-ldflags=-L/home/jiajia/work/FFmpeg/lib

    --extra-cflags    --extra-ldflags  这一部分不一定正确,这一部分可以在configure文件里详细看到。交叉编译带x264的库的,我我没验证。

    后面这个写的我也还没验证:

    交叉编译ffmpeg-4.2.2_Golden_Chen的博客-CSDN博客_ffmpeg 交叉编译

    如果需要依赖其他库来编译,那么进行如下设置:

    FFmpeg: A complete, cross-platform solution to record, convert and stream audio and video.
    官网地址: http://ffmpeg.org/
    下载最新的 FFmpeg 4.2.2源码。参考了其他文章,FFmpeg编译还需要以下3个开源库:
    1)x264  -- library and application for encoding video streams into the H.264/MPEG-4 AVC compression format
    官网地址: http://www.videolan.org/developers/x264.html

    2)YASM  -- a complete rewrite of the NASM assembler
    官网地址:http://yasm.tortall.net/ 

    3)SDL2 --  Simple DirectMedia Layer
    官网地址:http://www.libsdl.org/index.php

    初始化编译工具交叉变量 CROSS_COMPILE

      export CROSS_COMPILE=/home/golden/gcc/usr/bin/arm-oe-linux-gnueabi/arm-oe-linux-gnueabi-
      export CC=${CROSS_COMPILE}gcc

    或者直接这样:

    export CC=/home/hkx/Voice_Video/gcc-linaro-5.5.0-2017.10-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc

    查看是否设置成功:

    echo $CC

    1) 编译x264
    # wget https://code.videolan.org/videolan/x264/-/archive/master/x264-master.tar.bz2
    # tar xvjf  x264-master.tar.bz2  
    # cd x264-master
    # ./configure --host=arm-linux --prefix=/usr/local/x264-arm --enable-shared --enable-debug --disable-asm

    # make
    # make install

    --prefix=/usr/local/x264-arm可以自己修改

    2) 编译YASM
    # wget http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz
    # tar xvzf yasm-1.3.0.tar.gz
    # cd yasm-1.3.0
    # ./configure  --prefix=/usr/local/ --host=arm-linux
    # make
    # make install

    3) 编译SDL2
    # wget http://www.libsdl.org/release/SDL2-2.0.12.tar.gz
    # tar xvzf SDL2-2.0.12.tar.gz
    # cd SDL2-2.0.12
    # ./configure --prefix=/usr/local/ --host=arm-linux --target=arm-linux
    # make
    # make install

    以上3部分验证过得,是正确的,第四步没验证

    4) 最后编译FFmpeg
    # wget https://ffmpeg.org/releases/ffmpeg-4.2.2.tar.bz2
    # tar xvjf ffmpeg-4.2.2.tar.bz2
    # cd ffmpeg-4.2.2
    # ./configure --cross-prefix=$CROSS_COMPILE --enable-cross-compile --target-os=linux --cc="$CC" --arch=arm --prefix=/usr/local/arm_ffmpeg --enable-shared --disable-static --enable-gpl --enable-nonfree --enable-swscale --enable-pthreads --disable-armv5te --disable-armv6 --disable-armv6t2 --disable-yasm   --enable-ffmpeg --enable-ffplay --enable-sdl2 
     

    五、OpenCV的交叉编译

    交叉编译Opencv带Contrib_偏安一隅,占山为王的博客-CSDN博客

    Linux下编译Opencv和contrib_偏安一隅,占山为王的博客-CSDN博客_linux 编译opencv

    六、OpenCV在Windows下的编译 结合VS2019

    如下是我很早之前的一次操作了,仅作为参考,不带扩展模块可以直接下载:

    Windows下OpenCV4.1.2和Contrib的编译_偏安一隅,占山为王的博客-CSDN博客

  • 相关阅读:
    窗口看门狗
    maven 下载和安装
    【每天学习一点新知识】浏览器的同源策略
    基于Spring Boot与Vue的智能房产匹配平台+文档
    在 SEO 中,一个好的网页必须具备哪些 HTML 标签和属性?
    使用Spring Gateway为对象存储系统MinIo和kkFileView文档预览增加登录验证
    LeetCode每日一题(2266. Count Number of Texts)
    Ai时代降临,我们的未来又在哪里?
    ProGuard使用简介
    vue3前端excel导出;组件表格,自定义表格导出;Vue3 + xlsx + xlsx-style
  • 原文地址:https://blog.csdn.net/qq_42475191/article/details/127625439