ffmpeg更改视频的帧率

news2024/11/15 17:22:19

note

视频帧率调整
    帧率(fps-frame per second)
    例如:原来帧率为30,调整后为1   现象:原来是每秒有30张图像,调整后每秒1张图像,看着图像很慢
    实现:在每秒的时间区间里,取一张图像

version

#define LIBAVFILTER_VERSION_MINOR 12

#define LIBAVFILTER_VERSION_MICRO 100

#define LIBAVCODEC_VERSION_MINOR 31

#define LIBAVCODEC_VERSION_MICRO 102

code

void CFfmpegOps::ChangeVideoFPS(const char *in_mp4, const char *out_mp4)
{
    AVFormatContext *in_fmt_ctx = nullptr;
    const AVInputFormat *in_fmt = nullptr;
    AVFormatContext *out_fmt_ctx = nullptr;
    const AVOutputFormat *out_fmt = nullptr;
    int ret = -1;
    int video_stream_index = 0;
    const AVCodec *decoder = nullptr;
    AVCodecContext *decoder_ctx = nullptr;
    const AVCodec* encoder = nullptr;
    AVCodecContext* encoder_ctx = nullptr;
    AVStream* in_avstream = nullptr;
    AVStream* out_avstream = nullptr;
    const AVFilter * avfilter_buffer_src = nullptr; // 源,video buffer可设置的参数有
    AVFilterContext* avfilter_ctx_buffer_src = nullptr;
    const AVFilter * avfilter_fps = nullptr;    // 中间节点,video fps可设置的参数包含fps
    AVFilterContext* avfilter_ctx_fps = nullptr;
    const AVFilter* avfilter_buffer_sink = nullptr; // 终,video buffersink可设置的参数只有pixel formats
    AVFilterContext* avfilter_ctx_buffer_sink = nullptr;
    AVFilterGraph* avfiltergraph = nullptr;
    AVPacket* avpacket_src = nullptr;
    AVFrame* avframe_src = nullptr;
    AVFrame* avframe_dest = nullptr;
    AVPacket* avpacket_dest = nullptr;
    AVRational pixel_aspect = {.num = 1, .den = 1};

    ret = avformat_open_input(&in_fmt_ctx, in_mp4, nullptr, nullptr);
    if (ret < 0)
    {
        printf("avformat_open_input error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }
    in_fmt = in_fmt_ctx->iformat;

    ret = avformat_find_stream_info(in_fmt_ctx, nullptr);
    if (ret < 0)
    {
        printf("avformat_find_stream_info error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }

    ret = av_find_best_stream(in_fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
    if (ret < 0)
    {
        printf("av_find_best_stream error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }
    video_stream_index = ret;
    in_avstream = in_fmt_ctx->streams[video_stream_index];

    decoder = avcodec_find_decoder(in_avstream->codecpar->codec_id);
    if (!decoder)
    {
        printf("avcodec_find_decoder error\n");
        goto END;
    }

    decoder_ctx = avcodec_alloc_context3(decoder);
    if (!decoder_ctx)
    {
        printf("avcodec_alloc_context3 error\n");
        goto END;
    }

    ret = avcodec_parameters_to_context(decoder_ctx, in_avstream->codecpar);
    if (ret < 0)
    {
        printf("avcodec_parameters_to_context error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }

    ret = avcodec_open2(decoder_ctx, decoder, nullptr);
    if (ret < 0)
    {
        printf("avcodec_open2 error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }

    avfiltergraph = avfilter_graph_alloc();
    if (!avfiltergraph)
    {
        printf("avfilter_graph_alloc error\n");
        goto END;
    }

    avfilter_buffer_src = avfilter_get_by_name("buffer");   // buffer对应video,abuffer对应audio
    if (!avfilter_buffer_src)
    {
        printf("avfilter_get_by_name error\n");
        goto END;
    }
    printf("avfilter_buffer_src->desc:%s\n", avfilter_buffer_src->description);

    avfilter_ctx_buffer_src = avfilter_graph_alloc_filter(avfiltergraph, avfilter_buffer_src, avfilter_buffer_src->name);
    if (!avfilter_ctx_buffer_src)
    {
        printf("avfilter_graph_alloc_filter error\n");
        goto END;
    }

    // 设置avfilter_ctx_buffer_src的可选参数
    // 参照libavfilter/buffersrc.c文件中对video buffer的可选参数描述
    ret = av_opt_set(avfilter_ctx_buffer_src, 
                "width", std::to_string(in_avstream->codecpar->width).c_str(), 
                AV_OPT_SEARCH_CHILDREN);
    if (ret < 0)
    {
        printf("av_opt_set error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }
    ret = av_opt_set(avfilter_ctx_buffer_src, 
                "height", std::to_string(in_avstream->codecpar->height).c_str(), 
                AV_OPT_SEARCH_CHILDREN);
    if (ret < 0)
    {
        printf("av_opt_set error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }
    ret = av_opt_set(avfilter_ctx_buffer_src, 
                "pix_fmt", av_get_pix_fmt_name((AVPixelFormat)(in_avstream->codecpar->format)), 
                AV_OPT_SEARCH_CHILDREN);
    if (ret < 0)
    {
        printf("av_opt_set error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }
    ret = av_opt_set_q(avfilter_ctx_buffer_src,
                "time_base", in_avstream->time_base,
                AV_OPT_SEARCH_CHILDREN);
    if (ret < 0)
    {
        printf("av_opt_set error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }
    ret = av_opt_set_q(avfilter_ctx_buffer_src,
                "frame_rate", in_avstream->r_frame_rate,
                AV_OPT_SEARCH_CHILDREN);
    if (ret < 0)
    {
        printf("av_opt_set error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }

    ret = av_opt_set_q(avfilter_ctx_buffer_src,
                    "pixel_aspect", pixel_aspect,
                    AV_OPT_SEARCH_CHILDREN);
    if (ret < 0)
    {
        printf("av_opt_set error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }

    // 上面已经设置过参数,初始化就不传参了
    ret = avfilter_init_str(avfilter_ctx_buffer_src, nullptr);
    if (ret < 0)
    {
        printf("avfilter_init_str error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }

    avfilter_fps = avfilter_get_by_name("fps");
    if (!avfilter_fps)
    {
        printf("avfilter_get_by_name error\n");
        goto END;
    }
    printf("avfilter_fps->desc:%s\n", avfilter_fps->description);

    avfilter_ctx_fps = avfilter_graph_alloc_filter(avfiltergraph, avfilter_fps, avfilter_fps->name);
    if (!avfilter_ctx_fps)
    {
        printf("avfilter_graph_alloc_filter error\n");
        goto END;
    }

    ret = av_opt_set(avfilter_ctx_fps, 
                    "fps", "1",
                    AV_OPT_SEARCH_CHILDREN);
    if (ret < 0)
    {
        printf("av_opt_set error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }
    
    ret = avfilter_init_str(avfilter_ctx_fps, nullptr);
    if (ret < 0)
    {
        printf("avfilter_init_str error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }

    avfilter_buffer_sink = avfilter_get_by_name("buffersink");
    if (!avfilter_buffer_sink)
    {
        printf("avfilter_get_by_name error\n");
        goto END;
    }
    printf("avfilter_buffer_sink->desc:%s\n", avfilter_buffer_sink->description);

    avfilter_ctx_buffer_sink = avfilter_graph_alloc_filter(avfiltergraph, avfilter_buffer_sink, avfilter_buffer_sink->name);
    if (!avfilter_ctx_buffer_sink)
    {
        printf("avfilter_graph_alloc_filter error\n");
        goto END;
    }

    ret = avfilter_init_str(avfilter_ctx_buffer_sink, nullptr);
    if (ret < 0)
    {
        printf("avfilter_init_str error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }

    // 把avfilter组成链
    // avfilter_link的作用,在src和dest之间新建AVFilterLink实例
    // srcpad:src的输出通道编号
    // destpad:dest的输入通道编号
    ret = avfilter_link(avfilter_ctx_buffer_src, 0, avfilter_ctx_fps, 0);
    if (ret != 0)
    {
        printf("avfilter_link error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }

    ret = avfilter_link(avfilter_ctx_fps, 0, avfilter_ctx_buffer_sink, 0);
    if (ret != 0)
    {
        printf("avfilter_link error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }

    ret = avfilter_graph_config(avfiltergraph, nullptr);
    if (ret < 0)
    {
        printf("avfilter_graph_config error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }

    ret = avformat_alloc_output_context2(&out_fmt_ctx, nullptr, nullptr, out_mp4);
    if (ret < 0)
    {
        printf("avformat_alloc_output_context2 error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }
    out_fmt = out_fmt_ctx->oformat;

    encoder = avcodec_find_encoder(decoder->id);
    if (!encoder)
    {
        printf("avcodec_find_encoder error\n");
        goto END;
    }

    encoder_ctx = avcodec_alloc_context3(encoder);
    if (!encoder_ctx)
    {
        printf("avcodec_alloc_context3 error\n");
        goto END;
    }
    encoder_ctx->pix_fmt = (AVPixelFormat)(av_buffersink_get_format(avfilter_ctx_buffer_sink));
    encoder_ctx->width = av_buffersink_get_w(avfilter_ctx_buffer_sink);
    encoder_ctx->height = av_buffersink_get_h(avfilter_ctx_buffer_sink);
    encoder_ctx->time_base = av_buffersink_get_time_base(avfilter_ctx_buffer_sink);
    encoder_ctx->framerate = av_buffersink_get_frame_rate(avfilter_ctx_buffer_sink);
    encoder_ctx->gop_size = decoder_ctx->gop_size;
    encoder_ctx->max_b_frames = decoder_ctx->max_b_frames;

    if (out_fmt->flags & AVFMT_GLOBALHEADER)
    {
        encoder_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    }

    ret = avcodec_open2(encoder_ctx, encoder, nullptr);
    if (ret < 0)
    {
        printf("avcodec_open2 error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }

    out_avstream = avformat_new_stream(out_fmt_ctx, encoder);
    if (!out_avstream)
    {
        printf("avformat_new_stream error\n");
        goto END;
    }
    out_avstream->time_base = av_buffersink_get_time_base(avfilter_ctx_buffer_sink);
    out_avstream->r_frame_rate = av_buffersink_get_frame_rate(avfilter_ctx_buffer_sink);
    out_avstream->avg_frame_rate = av_buffersink_get_frame_rate(avfilter_ctx_buffer_sink);

    ret = avcodec_parameters_from_context(out_avstream->codecpar, encoder_ctx);
    if (ret < 0)
    {
        printf("avcodec_parameters_from_context error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }

    ret = avio_open(&(out_fmt_ctx->pb), out_mp4, AVIO_FLAG_WRITE);
    if (ret < 0)
    {
        printf("avio_open error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }

    // 调用avformat_write_header后,out_avstream的时间基发生了变化(10->102400),容器要求?
    ret = avformat_write_header(out_fmt_ctx, nullptr);
    if (ret < 0)
    {
        printf("avformat_write_header error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }

    avpacket_src = av_packet_alloc();
    if (!avpacket_src)
    {
        printf("av_packet_alloc error\n");
        goto END;
    }

    avpacket_dest = av_packet_alloc();
    if (!avpacket_dest)
    {
        printf("av_packet_alloc error\n");
        goto END;
    }

    avframe_src = av_frame_alloc();
    if (!avframe_src)
    {
        printf("av_frame_alloc error\n");
        goto END;
    }

    avframe_dest = av_frame_alloc();
    if (!avframe_dest)
    {
        printf("av_frame_alloc error\n");
        goto END;
    }

    while (1)
    {
        // 从输入文件读取压缩视频帧
        ret = av_read_frame(in_fmt_ctx, avpacket_src);
        if (ret < 0)
        {
            printf("av_read_frame error(%s)\n", GetFfmpegERR(ret));
            break;
        }

        if (avpacket_src->stream_index != video_stream_index)
        {
            av_packet_unref(avpacket_src);
            continue;
        }

        // 把压缩视频帧发送给解码器
        ret = avcodec_send_packet(decoder_ctx, avpacket_src);
        if (ret != 0)
        {
            printf("avcodec_send_packet error(%s)\n", GetFfmpegERR(ret));
        }
        else
        {
            // 接收解码器的输出视频帧
            ret = avcodec_receive_frame(decoder_ctx, avframe_src);
            if (ret != 0)
            {
                printf("avcodec_receive_frame error(%s)\n", GetFfmpegERR(ret));
            }
            else
            {
                // 把视频帧交给avfilter
                ret = av_buffersrc_write_frame(avfilter_ctx_buffer_src, avframe_src);
                if (ret != 0)
                {
                    printf("av_buffersrc_write_frame error(%s)\n", GetFfmpegERR(ret));
                }
                else
                {
                    // 从avfilter获取视频帧
                    ret = av_buffersink_get_frame(avfilter_ctx_buffer_sink, avframe_dest);
                    if (ret < 0)
                    {
                        printf("av_buffersink_get_frame error(%s)\n", GetFfmpegERR(ret));
                    }
                    else
                    {
                        // 把视频帧交给编码器
                        ret = avcodec_send_frame(encoder_ctx, avframe_dest);
                        if (ret != 0)
                        {
                            printf("avcodec_send_frame error(%s)\n", GetFfmpegERR(ret));
                        }
                        else
                        {
                            // 从编码器获取压缩视频帧
                            ret = avcodec_receive_packet(encoder_ctx, avpacket_dest);
                            if (ret != 0)
                            {
                                printf("avcodec_receive_packet error(%s)\n", GetFfmpegERR(ret));
                            }
                            else
                            {
                                av_packet_rescale_ts(avpacket_dest, encoder_ctx->time_base, out_avstream->time_base);

                                // 写入到输出文件
                                ret = av_write_frame(out_fmt_ctx, avpacket_dest);
                                if (ret < 0)
                                {
                                    printf("av_write_frame error(%s)\n", GetFfmpegERR(ret));
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    ret = av_write_trailer(out_fmt_ctx);
    if (ret < 0)
    {
        printf("av_write_trailer error(%s)\n", GetFfmpegERR(ret));
        goto END;
    }

END:
    if (avframe_dest)
    {
        av_frame_free(&avframe_dest);
        avframe_dest = nullptr;
    }
    if (avframe_src)
    {
        av_frame_free(&avframe_src);
        avframe_src = nullptr;
    }
    if (avpacket_src)
    {
        av_packet_free(&avpacket_src);
        avpacket_src = nullptr;
    }
    if (avpacket_dest)
    {
        av_packet_free(&avpacket_dest);
        avpacket_dest = nullptr;
    }
    if (avfilter_ctx_buffer_sink)
    {
        avfilter_free(avfilter_ctx_buffer_sink);
        avfilter_ctx_buffer_sink = nullptr;
    }
    if (avfilter_ctx_fps)
    {
        avfilter_free(avfilter_ctx_fps);
        avfilter_ctx_fps = nullptr;
    }
    if (avfilter_ctx_buffer_src)
    {
        avfilter_free(avfilter_ctx_buffer_src);
        avfilter_ctx_buffer_src = nullptr;
    }
    if (avfiltergraph)
    {
        avfilter_graph_free(&avfiltergraph);
        avfiltergraph = nullptr;
    }
    if (encoder_ctx)
    {
        avcodec_free_context(&encoder_ctx);
        encoder_ctx = nullptr;
    }
    if (decoder_ctx)
    {
        avcodec_free_context(&decoder_ctx);
        decoder_ctx = nullptr;
    }
    if (out_fmt_ctx)
    {
        avformat_free_context(out_fmt_ctx);
        out_fmt_ctx = nullptr;
    }
    if (in_fmt_ctx)
    {
        avformat_free_context(in_fmt_ctx);
        in_fmt_ctx = nullptr;
    }
}

performance

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1950642.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据库 第五次作业

1.触发器 建立两个表:goods(商品表)、orders(订单表)并 在商品表中导入商品记录 建立触发器&#xff0c;订单表中增加订单数量后&#xff0c;商品表商品数量同步减少对应的商品订单出数量,并测试 建立触发器&#xff0c;实现功能:客户取消订单&#xff0c;恢复商品表对应商品的…

Llama 3.1 这一最强模型按时降临!扎克伯格最新的访谈表明:Llama 将会成为 AI 领域中的 Linux 。

&#x1f431; 个人主页&#xff1a;TechCodeAI启航&#xff0c;公众号&#xff1a;TechCodeAI &#x1f64b;‍♂️ 作者简介&#xff1a;2020参加工作&#xff0c;专注于前端各领域技术&#xff0c;共同学习共同进步&#xff0c;一起加油呀&#xff01; &#x1f4ab; 优质专…

PyQt5 使用系统托盘

Qt5 中 QSystemTrayIcon 类提供了系统托盘的功能 具体功能 初始化 初始化 QSystemTrayIcon 对象, 需要传递一个父控件作为初始参数 import sys from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QWidgetapp QApplication(sys.argv) window QWidget() tray Q…

barrier failt to connect to server: timeout

win10做server&#xff0c;mac做client&#xff0c;一直运行失败&#xff0c;观察日志发现timeout。 最后发现是win10的防火墙入站规则里的Barrier Listener里设置的端口&#xff0c;和Barrier中设置的端口不一致。 因为局域网内有其他同事也用这个&#xff0c;所以修改了端口…

API资源对象CRD、认识Operator-理论知识和认识Operator-初次上手(2024-07-17)

一、API资源对象CRD Kubernetes 自定义资源定义&#xff08;Custom Resource Definition&#xff0c;简称 CRD&#xff09;是一种强大的 Kubernetes API 扩展机制&#xff0c;允许你定义和创建自己的资源类型&#xff0c;以满足您的应用程序或基础设施需求。 CRD 的核心思想是…

原型图设计指南:从基础到精通

用户体验设计师和原型设计的主要功能 PM、网站开发工程师通过展示产品内容、结构和粗略布局来沟通最初产品设想的重要工具&#xff0c;说明用户将如何与产品互动&#xff0c;而不是视觉设计。在大厂中&#xff0c;岗位分工更加细致明确&#xff0c;大部分原型都是产品经理做的&…

Oracle系统表空间的加解密

实验环境 数据库选择的是orclpdb1&#xff0c;当前系统表空间未加密&#xff1a; SQL> show con_nameCON_NAME ------------------------------ ORCLPDB1SQL> select TABLESPACE_NAME, STATUS, ENCRYPTED from dba_tablespaces;TABLESPACE_NAME STATUS …

【Ubuntu】安装 Snipaste 截图软件

Snipaste 下载安装并使用 Snipastefor more information报错解决方案每次启动软件需要输入的命令如下添加开机自启动 下载 下载地址 安装并使用 Snipaste 进入终端输入命令 # 1、进入到 Snipaste-2.8.9-Beta-x86_64.AppImage 所在目录&#xff08;根据自己的下载目录而定&…

uniapp安卓plus原生选择系统文件

uniapp安卓plus原生选择系统文件 效果&#xff1a; 组件代码&#xff1a; <template xlang"wxml" minapp"mpvue"><view></view> </template> <script>export default {name: file-manager,props: {},data() {return {is…

qt--做一个拷贝文件器

一、项目要求 使用线程完善文件拷贝器的操作 主窗口不能假死主窗口进度条必须能动改写文件大小的单位&#xff08;自适应&#xff09; 1TB1024GB 1GB1024MB 1MB1024KB 1KB1024字节 二、所需技术 1.QFileDialog 文件对话框 QFileDialog也继承了QDialog类&#xff0c;直接使用静态…

Hi-Fix 介绍

一、HIFIX 定义与功能 HIFIX是连接安装在自动检测装置&#xff08;Tester&#xff09;本体内的功能卡(Resource board)和承载被测设备(Device)探针卡&#xff08;Probe card&#xff09;的媒介&#xff0c;将来自检测装置的大量测试信号统一发送到被测设备(Device)。 HiFIX 能…

Java语言程序设计——篇八(1)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; Java常用核心类 主要内容Object: 终极父类toString( )方法equals( )方法getClass( )方法hashCode( )方法clone( )方法finalize( )方法实战演练 …

CentOS6.0安装telnet-server启用telnet服务

CentOS6.0安装telnet-server启用telnet服务 一步到位 fp"/etc/yum.repos.d" ; cp -a ${fp} ${fp}.$(date %0y%0m%0d%0H%0M%0S).bkup echo [base] nameCentOS-$releasever - Base baseurlhttp://mirrors.163.com/centos-vault/6.0/os/$basearch/http://mirrors.a…

使用echo写入多行文字到文件时换行的处理

目标 想使用echo写入如下内容到文件program.c里 #include<stdio.h> int main(){printf("hello!\n"); } 需要处理 1、如何处理行换 2、代码中的换行如何处理 实际例子 创建文件夹 mkdir test cd test chmod 777 . 创建文件写入内容 查看 cat -n program.c…

SLS 数据加工全面升级,集成 SPL 语法

作者&#xff1a;灵圣 数据加工概述 在系统开发、运维过程中&#xff0c;日志是最重要的信息之一&#xff0c;其最大的优点是简单直接。不过在整个日志的生命周期里有一对很难调和的矛盾&#xff1a;输出和采集日志要求尽可能的简单便捷 vs 日志分析时需要数据格式化并能够按…

【SpringBoot】3 项目配置及部署

配置文件配置 将 application.properties 改为 application.yml &#xff0c;写法不一样&#xff0c;本人比较习惯用 yaml 格式。 配置项目名称和项目端口号。 application.yml server:port: 8888 spring:application:name: system配置外置 Servlet 容器 如果要在 Tomcat 容器…

数据结构·AVL树

1. AVL树的概念 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果存数据时接近有序&#xff0c;二叉搜索将退化为单支树&#xff0c;此时查找元素效率相当于在顺序表中查找&#xff0c;效率低下。因此两位俄罗斯数学家 G.M.Adelson-Velskii 和E.M.Landis 在1962年发明了一种解…

音视频入门基础:WAV专题(1)——使用FFmpeg命令生成WAV音频文件

在文章《音视频入门基础&#xff1a;PCM专题&#xff08;1&#xff09;——使用FFmpeg命令生成PCM音频文件并播放》中讲述了生成PCM文件的方法。通过FFmpeg命令可以把该PCM文件转为WAV格式的音频文件&#xff1a; ./ffmpeg -ar 44100 -ac 2 -f s16le -acodec pcm_s16le -i aud…

「Ant Design」Antd 中卡片如何完全不展示内容区域、按需展示内容区域、不展示标题

前言 下面是默认的 Antd 卡片&#xff0c;由以下区域组成 处理 Antd 的 Card 展示形式大致有下面三种 卡片完全不展示内容区域 const App () > (<Card title"Default size card" extra{<a href"#">More</a>} style{{ width: 300 }}b…

工厂数字化转型,该如何建设数字孪生车间?

在工业4.0的浪潮下&#xff0c;数字化转型已成为工厂升级的必然趋势&#xff0c;而数字孪生技术的引入则为这一转型注入了强大动力。智汇云舟作为数字孪生行业头部企业和视频孪生技术首倡者&#xff0c;以创新的视角和前沿的技术&#xff0c;为数字工业建设助力&#xff0c;给众…