音视频开发—FFmpeg 打开摄像头进行RTMP推流

实验平台:Ubuntu20.04

摄像头:普通USB摄像头,输出格式为YUV422

1.配置RTMP服务器推流平台

使用Nginx 配置1935端口即可,贴上教程地址

ubuntu20.04搭建Nginx+rtmp服务器)

2.配置FFmpeg开发环境

过程较为简单,这里不再赘述,可以看博主的往期博客,贴上教程地址:配置FFmpeg开发环境 (Vscode+CMake

3.推流具体实现流程

总体流程图

在这里插入图片描述

3.1 设备初始化

有些摄像头可能支持输出多种参数,因此一定要检查摄像头支持的格式

v4l2-ctl: 一个命令行工具,用于控制和调试V4L2设备。可以查询设备信息、设置参数、捕获视频帧等。

v4l2-ctl --list-formats-ext  # 列出设备支持的所有格式
v4l2-ctl --set-fmt-video=width=1920,height=1080,pixelformat=H264  # 设置视频格式
v4l2-ctl --stream-mmap --stream-count=100 --stream-to=output.raw  # 捕获视频流

查看本次实验的摄像头的相关参数

marxist@ubuntu:~/Desktop/audio_test/build$ v4l2-ctl --list-formats-ext
ioctl: VIDIOC_ENUM_FMTType: Video Capture[0]: 'MJPG' (Motion-JPEG, compressed)Size: Discrete 1920x1080Interval: Discrete 0.033s (30.000 fps)Size: Discrete 640x480Interval: Discrete 0.008s (120.101 fps)Interval: Discrete 0.011s (90.000 fps)Interval: Discrete 0.017s (60.500 fps)Interval: Discrete 0.033s (30.200 fps)Size: Discrete 1280x720Interval: Discrete 0.017s (60.000 fps)Interval: Discrete 0.033s (30.500 fps)Size: Discrete 1024x768Interval: Discrete 0.033s (30.000 fps)Size: Discrete 800x600Interval: Discrete 0.017s (60.000 fps)Size: Discrete 1280x1024Interval: Discrete 0.033s (30.000 fps)Size: Discrete 320x240Interval: Discrete 0.008s (120.101 fps)[1]: 'YUYV' (YUYV 4:2:2)Size: Discrete 1920x1080Interval: Discrete 0.167s (6.000 fps)Size: Discrete 640x480Interval: Discrete 0.033s (30.000 fps)Size: Discrete 1280x720Interval: Discrete 0.111s (9.000 fps)Size: Discrete 1024x768Interval: Discrete 0.167s (6.000 fps)Size: Discrete 800x600Interval: Discrete 0.050s (20.000 fps)Size: Discrete 1280x1024Interval: Discrete 0.167s (6.000 fps)Size: Discrete 320x240Interval: Discrete 0.033s (30.000 fps)

由上述可知,摄像头一共支持两种格式,一是MJPG格式,已经由硬件压缩好的一种格式,一种就是常见的YUV422格式,YUV同样支持多种分辨率格式。

设置输入格式上下文,Linux系统对应的是V4L2,查找视频流信息

AVInputFormat *input_format = av_find_input_format("v4l2");if ((ret = avformat_open_input(&input_ctx, "/dev/video0", input_format, &options)) < 0){fprintf(stderr, "Could not open input\n");return ret;}// 查找流信息ret = avformat_find_stream_info(input_ctx, NULL);if (ret < 0){std::cerr << "could not find stream info" << std::endl;return -1;}// 查找视频流for (size_t i = 0; i < input_ctx->nb_streams; i++){if (input_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){video_stream_index = i;break;}}if (video_stream_index == -1){std::cerr << "no video stream found" << std::endl;return -1;}

3.2 初始化编码器

本次推流实验使用的是H264编码器,CPU软编码,没有使用到硬件编码,用到的库是X264。

主要流程为 查找编码器——分配编码器上下文——设置编码器参数——打开编码器

    // 查找编码器AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);if (!codec){fprintf(stderr, "Could not find AV_CODEC_ID_H264\n");return -1;}// 分配编码器上下文AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);if (!codec_ctx){std::cerr << "Could not allocate video codec context" << std::endl;return -1;}// 设置编码器参数codec_ctx->codec_id = codec->id;codec_ctx->bit_rate = 400000;codec_ctx->width = 1280;codec_ctx->height = 720;codec_ctx->time_base = (AVRational){1, 9};codec_ctx->framerate = (AVRational){9, 1};codec_ctx->gop_size = 10;codec_ctx->max_b_frames = 0;             // 不需要B帧codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; // 传入的420P 格式,而cam 默认输出422 则一会 需要作转换// 打开编码器if (avcodec_open2(codec_ctx, codec, NULL) < 0){fprintf(stderr, "Could not open codec\n");return -1;}

这里将B帧参数设置为了0,因为加入B帧之后,虽然提高了压缩效率,但是也显著增加了编码的复杂性。编码B帧需要更多的计算资源,因为它不仅需要前向预测,还需要后向预测。对于资源受限的设备(如移动设备、嵌入式系统等),不使用B帧可以减少编码器的负担。

3.3 设置输出流

这里的输出流地址则特指的RTMP服务器地址,也就是说FFmpeg将编码好的数据传输到RTMP服务器。如果需要写入到文件,输出流地址也可以是文件路径。

相关代码操作

 // 创建输出流AVStream *out_stream = avformat_new_stream(output_ctx, codec);if (!out_stream){fprintf(stderr, "Could not avformat_new_stream\n");return -1;}// 从输入流复制参数到输出流avcodec_parameters_from_context(out_stream->codecpar, codec_ctx);out_stream->time_base = codec_ctx->time_base;// 打开输出URLif (!(output_ctx->oformat->flags & AVFMT_NOFILE)){if (avio_open(&output_ctx->pb, output_url, AVIO_FLAG_WRITE) < 0){std::cerr << "Could not open output URL" << std::endl;return -1;}}// 写输出文件头if (avformat_write_header(output_ctx, NULL) < 0){fprintf(stderr, "Could not write header\n");return -1;}

3.4 读取摄像头数据

av_read_frame(input_ctx, &pkt)

代码作用是从输入设备读取数据帧,封装到packet中。

根据上文已经获取到了视频流索引,在此判断一下是不是视频流,因为有些摄像头支持语音输入,packet中存放的也可能是音频流

pkt.stream_index == video_stream_index

3.5 颜色空间转换

在上述过程中, 已经指定输出YUV422的数据了因此需要转换为YUV420数据

大体流程为:原始帧—转换上下文—YUV420帧

首先初始化转换上下文

 // 准备颜色空间色彩转换SwsContext *sws_ctx = sws_getContext(codec_ctx->width, codec_ctx->height, AV_PIX_FMT_YUYV422,codec_ctx->width, codec_ctx->height, AV_PIX_FMT_YUV420P,SWS_BILINEAR, NULL, NULL, NULL);if (!sws_ctx){std::cerr << "Could not initialize the conversion context" << std::endl;return -1;}

分辨率与编码器参数保持一致

准备原始数据帧,从摄像头读取的数据包中得到

AVFrame *temp_frame = av_frame_alloc();if (!temp_frame){std::cerr << "Could not allocate temporary frame" << std::endl;av_packet_unref(&pkt);continue;}// 分配临时帧的内存空间if (av_image_alloc(temp_frame->data, temp_frame->linesize, codec_ctx->width, codec_ctx->height, AV_PIX_FMT_YUYV422, 1) < 0){std::cerr << "Could not allocate temporary frame buffer" << std::endl;av_frame_free(&temp_frame);av_packet_unref(&pkt);continue;}// 将pkt.data中的数据填充到temp_frameret = av_image_fill_arrays(temp_frame->data, temp_frame->linesize, pkt.data, AV_PIX_FMT_YUYV422, codec_ctx->width, codec_ctx->height, 1);if (ret < 0){std::cerr << "Error filling arrays" << std::endl;av_freep(&temp_frame->data[0]);av_frame_free(&temp_frame);av_packet_unref(&pkt);continue;}

初始化YUV420的帧

    // 分配AVFrame并设置参数AVFrame *frame = av_frame_alloc();if (!frame){std::cerr << "Could not allocate video frame" << std::endl;return -1;}frame->format = codec_ctx->pix_fmt;frame->width = codec_ctx->width;frame->height = codec_ctx->height;av_frame_get_buffer(frame, 32);

最后执行转换即可

sws_scale(sws_ctx, temp_frame->data, temp_frame->linesize, 0, codec_ctx->height, frame->data, frame->linesize);

3.6 编码并输出到RTMP服务器

得到YUV420的数据,就可以进行最后的操作了

 // 编码视频数据ret = avcodec_send_frame(codec_ctx, frame);if (ret < 0){std::cerr << "Error sending frame to encoder" << std::endl;break;}while (ret >= 0){ret = avcodec_receive_packet(codec_ctx, &pkt);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){break;}else if (ret < 0){std::cerr << "Error encoding frame" << std::endl;break;}// 将编码后的视频数据推送到RTMP服务器pkt.stream_index = out_stream->index;av_packet_rescale_ts(&pkt, codec_ctx->time_base, out_stream->time_base);pkt.pos = -1;ret = av_interleaved_write_frame(output_ctx, &pkt);if (ret < 0){std::cerr << "Error writing frame" << std::endl;break;}av_packet_unref(&pkt);}

4.完整代码

extern "C"
{
#include <libavformat/avformat.h>
#include <libavdevice/avdevice.h>
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include "libavutil/imgutils.h"
}
#include <iostream>
#include <cstdlib>
using namespace std;int main(int argc, char *argv[])
{const char *output_url = "rtmp://192.168.1.79:1935/orin/live"; // 替换为你的RTMP推流地址AVFormatContext *input_ctx = NULL;AVPacket pkt;int ret;int video_stream_index = -1;AVDictionary *options = nullptr; // 摄像头相关参数int64_t pts = 0;                 // 初始化 PTS// 初始化libavformat和注册所有muxers, demuxers和协议avdevice_register_all();avformat_network_init();// 打开摄像头开始//   // 摄像头支持多种参数,因此使用option 指定参数 最大支持到9帧av_dict_set(&options, "video_size", "1280*720", 0);av_dict_set(&options, "framerate", "9", 0);av_dict_set(&options, "input_format", "yuyv422", 0);AVInputFormat *input_format = av_find_input_format("v4l2");if ((ret = avformat_open_input(&input_ctx, "/dev/video0", input_format, &options)) < 0){fprintf(stderr, "Could not open input\n");return ret;}// 查找流信息ret = avformat_find_stream_info(input_ctx, NULL);if (ret < 0){std::cerr << "could not find stream info" << std::endl;return -1;}// 查找视频流for (size_t i = 0; i < input_ctx->nb_streams; i++){if (input_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){video_stream_index = i;break;}}if (video_stream_index == -1){std::cerr << "no video stream found" << std::endl;return -1;}// 查找编码器AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);if (!codec){fprintf(stderr, "Could not find AV_CODEC_ID_H264\n");return -1;}// 分配编码器上下文AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);if (!codec_ctx){std::cerr << "Could not allocate video codec context" << std::endl;return -1;}// 设置编码器参数codec_ctx->codec_id = codec->id;codec_ctx->bit_rate = 400000;codec_ctx->width = 1280;codec_ctx->height = 720;codec_ctx->time_base = (AVRational){1, 9};codec_ctx->framerate = (AVRational){9, 1};codec_ctx->gop_size = 10;codec_ctx->max_b_frames = 0;             // 不需要B帧codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; // 传入的420P 格式,而cam 默认输出422 则一会 需要作转换// 打开编码器if (avcodec_open2(codec_ctx, codec, NULL) < 0){fprintf(stderr, "Could not open codec\n");return -1;}// 分配输出上下文AVFormatContext *output_ctx = nullptr;ret = avformat_alloc_output_context2(&output_ctx, NULL, "flv", output_url);if (!output_ctx){fprintf(stderr, "Could not create output context\n");return ret;}// 创建输出流AVStream *out_stream = avformat_new_stream(output_ctx, codec);if (!out_stream){fprintf(stderr, "Could not avformat_new_stream\n");return -1;}// 从输入流复制参数到输出流avcodec_parameters_from_context(out_stream->codecpar, codec_ctx);out_stream->time_base = codec_ctx->time_base;// 打开输出URLif (!(output_ctx->oformat->flags & AVFMT_NOFILE)){if (avio_open(&output_ctx->pb, output_url, AVIO_FLAG_WRITE) < 0){std::cerr << "Could not open output URL" << std::endl;return -1;}}// 写输出文件头if (avformat_write_header(output_ctx, NULL) < 0){fprintf(stderr, "Could not write header\n");return -1;}// 分配AVFrame并设置参数AVFrame *frame = av_frame_alloc();if (!frame){std::cerr << "Could not allocate video frame" << std::endl;return -1;}frame->format = codec_ctx->pix_fmt;frame->width = codec_ctx->width;frame->height = codec_ctx->height;av_frame_get_buffer(frame, 32);// 准备颜色空间色彩转换SwsContext *sws_ctx = sws_getContext(codec_ctx->width, codec_ctx->height, AV_PIX_FMT_YUYV422,codec_ctx->width, codec_ctx->height, AV_PIX_FMT_YUV420P,SWS_BILINEAR, NULL, NULL, NULL);if (!sws_ctx){std::cerr << "Could not initialize the conversion context" << std::endl;return -1;}while (true){if (av_read_frame(input_ctx, &pkt) >= 0){if (pkt.stream_index == video_stream_index){// 从相机出来的原始帧 为YUV 422 需要转换为420P// 数据是YUYV422格式,需要转换为YUV420PAVFrame *temp_frame = av_frame_alloc();if (!temp_frame){std::cerr << "Could not allocate temporary frame" << std::endl;av_packet_unref(&pkt);continue;}// 分配临时帧的内存空间if (av_image_alloc(temp_frame->data, temp_frame->linesize, codec_ctx->width, codec_ctx->height, AV_PIX_FMT_YUYV422, 1) < 0){std::cerr << "Could not allocate temporary frame buffer" << std::endl;av_frame_free(&temp_frame);av_packet_unref(&pkt);continue;}// 将pkt.data中的数据填充到temp_frameret = av_image_fill_arrays(temp_frame->data, temp_frame->linesize, pkt.data, AV_PIX_FMT_YUYV422, codec_ctx->width, codec_ctx->height, 1);if (ret < 0){std::cerr << "Error filling arrays" << std::endl;av_freep(&temp_frame->data[0]);av_frame_free(&temp_frame);av_packet_unref(&pkt);continue;}// 转换颜色空间到YUV420Psws_scale(sws_ctx, temp_frame->data, temp_frame->linesize, 0, codec_ctx->height, frame->data, frame->linesize);// 设置帧的 PTSframe->pts = pts++;// 编码视频数据ret = avcodec_send_frame(codec_ctx, frame);if (ret < 0){std::cerr << "Error sending frame to encoder" << std::endl;break;}while (ret >= 0){ret = avcodec_receive_packet(codec_ctx, &pkt);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){break;}else if (ret < 0){std::cerr << "Error encoding frame" << std::endl;break;}// 将编码后的视频数据推送到RTMP服务器pkt.stream_index = out_stream->index;av_packet_rescale_ts(&pkt, codec_ctx->time_base, out_stream->time_base);pkt.pos = -1;ret = av_interleaved_write_frame(output_ctx, &pkt);if (ret < 0){std::cerr << "Error writing frame" << std::endl;break;}av_packet_unref(&pkt);}av_frame_free(&temp_frame);}}}av_write_trailer(output_ctx);// 释放资源av_frame_free(&frame);avcodec_free_context(&codec_ctx);avformat_close_input(&input_ctx);if (output_ctx && !(output_ctx->oformat->flags & AVFMT_NOFILE)){avio_closep(&output_ctx->pb);}avformat_free_context(output_ctx);sws_freeContext(sws_ctx);return 0;
}

5.获取推流数据

常用的工具为VLC播放器,选择打开网络串流地址

在这里插入图片描述

就能播放推流画面了

在这里插入图片描述

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

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

相关文章

决战技术管理转型:决策之道-管理中的智慧与策略

文章目录 引言一、决策的重要性二、常见的决策方式1. 理性决策&#xff08;Rational Decision Making&#xff09;2. 有限理性&#xff08;Bounded Rationality&#xff09;3. 直觉决策&#xff08;Intuitive Decision Making&#xff09;4. 循证管理&#xff08;Evidence-Base…

智能血压计,让健康“听”得见- WT588F02B血压计语音方案

一、语音血压计开发背景&#xff1a; 在快节奏的现代生活中&#xff0c;健康成为了我们最宝贵的财富。而血压&#xff0c;作为反映人体健康状态的重要指标之一&#xff0c;更是需要我们时刻关注。传统的血压计虽然能够为我们提供准确的血压数据&#xff0c;但往往因为操作复杂…

C#ListView的单元格支持添加基本及自定义任意控件

功能说明 使用ListView时&#xff0c;希望可以在单元格显示图片或其他控件&#xff0c;发现原生的ListView不支持&#xff0c;于是通过拓展&#xff0c;实现ListView可以显示任意控件的功能&#xff0c;效果如下&#xff1a; 实现方法 本来想着在单元格里面实现控件的自绘的…

20240621在飞凌的OK3588-C开发板的Buildroot系统中集成i2ctool工具

20240621在飞凌的OK3588-C开发板中打开i2ctool工具 2024/6/21 17:44 默认继承的i2c工具&#xff1a; rootrk3588-buildroot:/# rootrk3588-buildroot:/# i2c i2c-stub-from-dump i2cdump i2cset i2cdetect i2cget i2ctransfer rootrk3588-…

史上最全的整合Harbor安装教程,哈哈哈哈

一、安装docker 下载地址&#xff1a;https://download.docker.com/linux/static/stable/x86_64/docker-23.0.4.tgz 1.1 解压二进制包 wget https://download.docker.com/linux/static/stable/x86_64/docker-23.0.4.tgz tar zxvf docker-23.0.4.tgz mv docker/* /usr/bin1.2…

Flutter 实现软鼠标

文章目录 前言一、如何实现&#xff1f;1、记录鼠标偏移2、MouseRegion获取偏移3、Transform移动图标 二、完整代码三、使用示例总结 前言 flutter在嵌入式系统中运行时&#xff0c;有可能遇到drm鼠标无法使用的情况&#xff0c;但鼠标事件却可以正常接收&#xff0c;此时如果…

用Vite基于Vue3+ts+DataV+ECharts开发数据可视化大屏,即能快速开发又能保证屏幕适配

数据可视化大屏 基于 Vue3、Typescript、DataV、ECharts5 框架的大数据可视化&#xff08;大屏展示&#xff09;开发。此项目vue3实现界面&#xff0c;采用新版动态屏幕适配方案&#xff0c;全局渲染组件封装&#xff0c;支持数据动态刷新渲染、内部DataV、ECharts图表都支持自…

SQLite扩展插件终极集合

作为一个嵌入式数据库引擎&#xff0c;SQLite 与其他数据库管理系统相比&#xff0c;缺少了一些功能。不过 SQLite 提供了一个扩展机制&#xff0c;因此我们可以在网络上找到大量的 SQLite 插件。 今天我们介绍的这个插件叫做 sqlean&#xff0c;它打包了许多流行的 SQLite 扩…

消息队列kafka中间件详解:案例解析(第10天)

系列文章目录 1- 消息队列&#xff08;熟悉&#xff09;2- Kafka的基本介绍&#xff08;掌握架构&#xff0c;其他了解&#xff09;3- Kafka的相关使用&#xff08;掌握kafka常用shell命令&#xff09;4- Kafka的Python API的操作&#xff08;熟悉&#xff09; 文章目录 系列文…

kafka的基本模型

kafka官网 线程和线程之间的数据交互 在jvm里不同的线程有自己的栈内存&#xff0c;但彼此之间交互可以在共享的内存中进行&#xff0c;即堆内存&#xff0c;堆内存会将这些消息放到队列中&#xff0c;具体实现jvm见&#xff0c;栈内存各自维护&#xff0c;堆内存大家共享 进…

微信小程序UI组件库合集

文章目录 前言参考地址推荐组件库1.官方WeUI&#xff08;建议使用☆☆☆☆&#xff09;2.ColorUI&#xff08;广告很多&#xff0c;不建议使用&#xff09;3.vantUI又名&#xff1a;ZanUI&#xff08;操作简单&#xff0c;建议使用☆☆☆☆&#xff09;4.MinUI&#xff08;比较…

STM32---SPI通信协议(小白入、含源码)

写在前面&#xff1a;在单片机的学习过程中&#xff0c;各种通信协议的学习是必不可少的&#xff0c;在前面我们学习了串口通信、IIC通信&#xff0c;本节我们来认识一下SPI通信协议。包括其SPI基本概念、NORFLASH芯片的介绍以及相关的例程实验。 目录 一、SPI介绍 1.1什么是…

【小白专用24.6.18】C# SqlSugar:连接数据库实现简单的,增、删、改、查

【小白专用 已验证24.6.18】C# SqlSugar操作MySQL数据库实现增删改查-CSDN博客 通过NuGet包管理器搜索SqlSugar&#xff08;MySql还要安装MySql.Data、Newtonsoft.Json&#xff09;包并安装 SqlSugarClient db new SqlSugarClient(new ConnectionConfig(){ConnectionString …

中国信通院专访镜舟科技:开源商业化走了多远?

据《2023 中国开源发展蓝皮书》显示&#xff0c;随着数字化转型的深入&#xff0c;开源生态在去年快速发展&#xff0c;开源商业化的模式也逐渐成型。镜舟科技作为开源商业化的先行者&#xff0c;也在技术创新和商业拓展中稳步增长。 日前&#xff0c;中国信息通信研究院&…

Python基础教程(二十八):pip模块

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

NGINX_十二 nginx 地址重写 rewrite

十二 nginx 地址重写 rewrite 1 什么是Rewrite Rewrite对称URL Rewrite&#xff0c;即URL重写&#xff0c;就是把传入Web的请求重定向到其他URL的过程。URL Rewrite最常见的应用是URL伪静态化&#xff0c;是将动态页面显示为静态页面方式的一种技术。比如 http://www.123.com…

2024097期传足14场胜负前瞻

2024097期售止时间为6月22日&#xff08;周六&#xff09;20点30分&#xff0c;敬请留意&#xff1a; 本期14场由欧洲杯、美洲杯、美职联组成&#xff0c;1.5以下赔率2场&#xff0c;1.5-2.0赔率10场&#xff0c;其他场次是平半盘、平盘。本期14场难度中等。以下为基础盘前瞻&a…

docker将容器打包提交为镜像,再打包成tar包

将容器打包成镜像可以通过以下步骤来实现。这里以 Docker 为例&#xff0c;假设你已经安装了 Docker 并且有一个正在运行的容器。 1. 找到正在运行的容器 首先&#xff0c;你需要找到你想要打包成镜像的容器的 ID 或者名字。可以使用以下命令查看所有正在运行的容器&#xff…

【0-1系列】从0-1快速了解搜索引擎是什么以及怎么用(上)

友情链接 社区开发版安装部署与使用教程社区版家族V2024.5版本更新说明 START>>1.快速了解搜索引擎 什么是搜索引擎数据库 搜索引擎数据库是一类专门用于数据内容搜索的NoSQL数据库&#xff0c;是非结构化大数据处理分析领域中重要的基础支撑软件。 伴随互联网、移动…

DSP应用市场的大蛋糕,国产厂商能吃下多少?

DSP是数字信号处理器&#xff08;Digital Signal Processor&#xff09;的简称&#xff0c;是一种专门用于高速数学运算的微处理器。DSP能够快速且准确地处理数字信号&#xff0c;同时具备可编程和低功耗等特点&#xff0c;如今在各个领域发挥着越来越重要的作用。 &#xff08…