使用rust实现rtsp码流截图
中文互联网上的rust示例程序源码还是太稀少,找资料很是麻烦,下面是自己用业余时间开发实现的一个对批量rtsp码流源进行关键帧截图并存盘的rust demo源码记录。
要编译这个源码需要先安装vcpkg,然后用vcpkg install ffmpeg安装最新版本的ffmpeg库,当然了,你要是想vcpkg成功编译安装ffmpeg,vc编译器和windows sdk也是必不可少的,这些对于做rust windows开发的人来说都不是事,还有llvm及clang windows编译器环境也要安装,这都是准备工作。
代码使用了ffmpeg-next库,这个库在ubuntu 22上面使用sudo apt install 的ffmpeg相关libdev包和windows不一样,ubuntu 22里面默认是ffmpeg 4.3,windows平台默认是ffmpeg 7.0.2 ,这就导致了在跨平台编译的时候会出现问题,linux平台获取video decodec解码器和windows平台不一样,代码里面注释掉的内容就是在linux平台编译的时候要使用的函数,如果要在linux平台且使用ffmpeg 4.x版本编译注意打开注释掉的内容。
use ffmpeg_next as ffmpeg;
use tokio;
use std::sync::Arc;
use tokio::sync::Semaphore;
use std::error::Error;
use image::{ImageBuffer, Rgb};
use std::fmt;
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering};
use ffmpeg::format::input;
use ffmpeg::software::scaling::{context::Context, flag::Flags};
use ffmpeg::util::frame::video::Video;
use ffmpeg::format::stream::Stream;#[derive(Debug)]
enum CustomError {FfmpegError(ffmpeg::Error),ImageError(image::ImageError),Other(String),
}impl fmt::Display for CustomError {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {match self {CustomError::FfmpegError(e) => write!(f, "FFmpeg error: {}", e),CustomError::ImageError(e) => write!(f, "Image error: {}", e),CustomError::Other(e) => write!(f, "Other error: {}", e),}}
}impl std::error::Error for CustomError {}impl From<ffmpeg::Error> for CustomError {fn from(error: ffmpeg::Error) -> Self {CustomError::FfmpegError(error)}
}impl From<image::ImageError> for CustomError {fn from(error: image::ImageError) -> Self {CustomError::ImageError(error)}
}impl From<&str> for CustomError {fn from(error: &str) -> Self {CustomError::Other(error.to_string())}
}struct RtspSource {url: String,
}fn get_decoder(input_stream: &Stream) -> Result<ffmpeg::decoder::Video, ffmpeg::Error> {let decoder_params = input_stream.parameters();let mut ctx = ffmpeg::codec::context::Context::new();ctx.set_parameters(decoder_params)?;ctx.decoder().video()
}// #[cfg(not(feature = "ffmpeg_5_0"))]
// fn get_decoder(input_stream: &Stream) -> Result<ffmpeg::decoder::Video, ffmpeg::Error> {
// input_stream.codec().decoder().video()
// }async fn capture_frame(source: &RtspSource, frame_counter: Arc<AtomicUsize>) -> Result<(), Box<dyn Error>> {let mut ictx = input(&source.url)?;let input_stream = ictx.streams().best(ffmpeg::media::Type::Video).ok_or("Could not find best video stream")?;let video_stream_index = input_stream.index();let mut decoder = get_decoder(&input_stream)?;let mut scaler = Context::get(decoder.format(),decoder.width(),decoder.height(),ffmpeg::format::Pixel::RGB24,decoder.width(),decoder.height(),Flags::BILINEAR,)?;let mut frame = Video::empty();let current_path = std::env::current_dir()?;for (stream, packet) in ictx.packets() {if stream.index() == video_stream_index && packet.is_key() {decoder.send_packet(&packet)?;while decoder.receive_frame(&mut frame).is_ok() {let mut rgb_frame = Video::empty();scaler.run(&frame, &mut rgb_frame)?;let buffer = rgb_frame.data(0);let width = rgb_frame.width() as u32;let height = rgb_frame.height() as u32;let img: ImageBuffer<Rgb<u8>, _> =ImageBuffer::from_raw(width, height, buffer.to_owned()).ok_or("Failed to create image buffer")?;let index = frame_counter.fetch_add(1, Ordering::SeqCst);let file_save_name = format!("captured_frame_{}.jpg", index);let save_path: PathBuf = current_path.join("./images/").join(&file_save_name);img.save(&save_path)?;println!("Frame captured and saved to {}", save_path.display());return Ok(());}}}Ok(())
}async fn process_sources(sources: Vec<RtspSource>, max_concurrent: usize) -> Result<(), Box<dyn Error>> {let semaphore = Arc::new(Semaphore::new(max_concurrent));let frame_counter = Arc::new(AtomicUsize::new(0));let mut handles = vec![];for source in sources {let permit = semaphore.clone().acquire_owned().await?;let frame_counter_clone = Arc::clone(&frame_counter);let handle = tokio::spawn(async move {let result = capture_frame(&source, frame_counter_clone).await;match result {Ok(_) => println!("Successfully captured frame from {}", source.url),Err(e) => eprintln!("Error capturing frame from {}: {}", source.url, e),}drop(permit);});handles.push(handle);}for handle in handles {handle.await?;}Ok(())
}#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {ffmpeg::init()?;let mut sources:Vec<RtspSource>=Vec::with_capacity(100);for _ in 1..=100 {sources.push(RtspSource {url: format!("rtsp://你的rtsp源ip地址:8554/stream"),});}let max_concurrent = 20; // Set the maximum number of concurrent captureslet start_time = tokio::time::Instant::now();process_sources(sources, max_concurrent).await?;let end_time = tokio::time::Instant::now();println!("Time taken to capture frames: {:?}", end_time.duration_since(start_time));Ok(())
}
本文发表于https://blog.csdn.net/peihexian,欢迎转载,当博客写完的时候我想到一个问题,那就是其实是不是可以通过调用ffmpeg.exe命令行的方式传参实现截图的抓取,不过在实现上面的算法中我尝试了连上rtsp源头以后立马抓第一帧图像就存盘是不行的,因为没有关键帧数据,第一帧抓到的是乱码,所以代码里面改成了抓关键帧,这样存盘的时候肯定是完整的图像,不知道使用命令行方式传参的方式能不能解决取关键帧的问题。
补充一下Cargo.toml的文件内容:
[package]
name = "ffmpeg-test1"
version = "0.1.0"
edition = "2021"[dependencies]
ffmpeg-next = { version = "7.0" }
tokio = { version = "1.0", features = ["full"] }
image = "0.25"
相关文章:

使用rust实现rtsp码流截图
中文互联网上的rust示例程序源码还是太稀少,找资料很是麻烦,下面是自己用业余时间开发实现的一个对批量rtsp码流源进行关键帧截图并存盘的rust demo源码记录。 要编译这个源码需要先安装vcpkg,然后用vcpkg install ffmpeg安装最新版本的ffmpe…...

Cpp::STL—string类的模拟实现(12)
文章目录 前言一、string类各函数接口总览二、默认构造函数string(const char* str "");string(const string& str);传统拷贝写法现代拷贝写法 string& operator(const string& str);传统赋值构造现代赋值构造 ~string(); 三、迭代器相关函数begin &…...

一文搞懂SentencePiece的使用
目录 1. 什么是 SentencePiece?2. SentencePiece 基础概念2.1 SentencePiece 的工作原理2.2 SentencePiece 的优点 3. SentencePiece 的使用3.1 安装 SentencePiece3.2 训练模型与加载模型3.3 encode(高频)3.4 decode(高频&#x…...

一个简单的摄像头应用程序1
这个Python脚本实现了一个基于OpenCV的简单摄像头应用,我们在原有的基础上增加了录制视频等功能,用户可以通过该应用进行拍照、录制视频,并查看已拍摄的照片。以下是该脚本的主要功能和一些使用时需要注意的事项: 功能 拍照: 用户可以通过点击界面上的“拍照”按钮或按…...

通过PHP获取商品详情
在电子商务的浪潮中,数据的重要性不言而喻。商品详情信息对于电商运营者来说尤为宝贵。PHP,作为一种广泛应用的服务器端脚本语言,为我们提供了获取商品详情的便捷途径。 了解API接口文档 开放平台提供了详细的API接口文档。你需要熟悉商品详…...

【Android】获取备案所需的公钥以及签名MD5值
目录 重要前提 获取签名MD5值 获取公钥 重要前提 生成jks文件以及gradle配置应用该文件。具体步骤请参考我这篇文章:【Android】配置Gradle打包apk的环境_generate signed bundle or apk-CSDN博客 你只需要从头看到该文章的配置build.gradle(app&…...

看480p、720p、1080p、2k、4k、视频一般需要多大带宽呢?
看视频都喜欢看高清,那么一般来说看电影不卡顿需要多大带宽呢? 以4K为例,这里引用一位网友的回答:“视频分辨率4092*2160,每个像素用红蓝绿三个256色(8bit)的数据表示,视频帧数为60fps,那么一秒钟画面的数据量是:4096*2160*3*8*60≈11.9Gbps。此外声音大概是视频数据量…...

解决IDEA中@Autowired红色报错的实用指南:原因与解决方案
前言: 在使用Spring Boot开发时,Autowired注解是实现依赖注入的常用方式。然而,许多开发者在IDEA中使用Autowired时,可能会遇到红色报错,导致代码的可读性降低。本文将探讨导致这种现象的原因,并提供几种解…...

408知识点自检(一)
一、细节题 虚电路是面向连接的吗?虚电路线路上会不会有其他虚电路通过?虚电路适合什么类型的数据交换?虚电路的可靠性靠其他协议还是自己?固态硬盘的优势体现在什么存取方式?中断向量地址是谁的地址?多播…...

负载均衡--相关面试题(六)
在负载均衡的面试中,可能会遇到一系列涉及概念、原理、实践应用以及技术细节的问题。以下是一些常见的负载均衡面试题及其详细解答: 一、什么是负载均衡? 回答:负载均衡是一种将网络请求或数据传输工作分配给多个服务器或网络资源…...

【Unity踩坑】Unity更新Google Play结算库
一、问题描述: 在Google Play上提交了app bundle后,提示如下错误。 我使用的是Unity 2022.01.20f1,看来用的Play结算库版本是4.0 查了一下文档,Google Play结算库的维护周期是两年。现在需要更新到至少6.0。 二、更新过程 1. 下…...

Redis:hash类型
Redis:hash类型 hash命令设置与读取HSETHGETHMGET 哈希操作HEXISTSHDELHKEYSHVALSHGETALLHLENHSETNXHINCRBYHINCRBYFLOAT 内部编码ziplisthashtable 目前主流的编程语言中,几乎都提供了哈希表相关的容器,Redis自然也会支持对应的内容…...

力扣9.30
1749. 任意子数组和的绝对值的最大值 给你一个整数数组 nums 。一个子数组 [numsl, numsl1, ..., numsr-1, numsr] 的 和的绝对值 为 abs(numsl numsl1 ... numsr-1 numsr) 。 请你找出 nums 中 和的绝对值 最大的任意子数组(可能为空),…...

kafka下载配置
下载安装 参开kafka社区 zookeeperkafka消息队列群集部署https://apache.csdn.net/66c958fb10164416336632c3.html 下载 kafka_2.12-3.2.0安装包快速下载地址分享 官网下载链接地址: 官网下载地址:https://kafka.apache.org/downloads 官网呢下载慢…...

nlp任务之预测中间词-huggingface
目录 1.加载编码器 1.1编码试算 2.加载数据集 3.数据集处理 3.1 map映射:只对数据集中的sentence数据进行编码 3.2用filter()过滤 单词太少的句子过滤掉 3.3截断句子 4.创建数据加载器Dataloader 5. 下游任务模型 6.测试预测代码 7.训练代码 8.保…...

《程序猿之Redis缓存实战 · Redis 与数据库一致性》
📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数…...

【无标题】observer: error while loading shared libraries: libmariadb.so.3处理办法
文章目录 1.记录新装的oceanbase,使用observer帮助时,出现lib文件无法找到的处理过程 ./observer --help ./observer: error while loading shared libraries: libmariadb.so.3: cannot open shared object file: No such file or directory2.做一个strace跟踪&…...

极客兔兔Gee-Cache Day1
极客兔兔7Days GeeCache - Day1 interface{}:任意类型 缓存击穿:一个高并发的请求查询一个缓存中不存在的数据项,因此这个请求穿透缓存直接到达后端数据库或数据源来获取数据。如果这种请求非常频繁,就会导致后端系统的负载突然…...

[MAUI]数据绑定和MVVM:MVVM的属性验证
一、MVVM的属性验证案例 Toolkit.Mvvm框架中的ObservableValidator类,提供了属性验证功能,可以使用我们熟悉的验证特性对属性的值进行验证,并将错误属性提取和反馈给UI层。以下案例实现对UI层的姓名和年龄两个输入框,进行表单提交验证。实现效果如下所示 View<ContentP…...

2024年水利水电安全员考试题库及答案
一、判断题 1.采用水下钻孔爆破方案时,侧面应采用预裂爆破,并严格控制单响药量以保护附近建(构)筑物的安全。 答案:正确 2.围堰爆破拆除工程的实施应成立爆破指挥机构,并应按设计确定的安全距离设置警戒。…...

【快速删除 node_modules 】rimraf
目录 1. 什么是node_modules 2. 卸载一个npm包 3. 删除 node_modules 为什么这么慢 4. rimraf 5. 为什么rimraf 这么快 作为前端开发,无论我们关注不关注,每天都能接触到node_modules。通常产生于一个npm install命令,之后就不会多加关注…...

毕业设计选题:基于ssm+vue+uniapp的教学辅助小程序
开发语言:Java框架:ssmuniappJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:M…...

13-指针和动态内存-内存泄漏
一、视频笔记: C语言通过malloc,来获取堆上的内存。 动态调用内存: malloc 和 free ;new 和 delete 都行。 内存泄漏指的是我们动态申请了内存,但是即是是使用完了之后(从来都不去释放它)。只…...

基于深度学习的视频摘要生成
基于深度学习的视频摘要生成是一种通过自动化方式从长视频中提取关键片段,生成简洁且有代表性的视频摘要的技术。其目的是在保留视频主要内容的基础上,大幅缩短视频的播放时长,方便用户快速理解视频的核心信息。以下是视频摘要生成的主要方法…...

适合初学者的[JAVA]: 基础面试题
目录 说明 前言 String/StringBuffer/StringBuilder区别 第一点: 第二点: 总结: 反射机制 JVM内存结构 运行时数据区域被划分为5个主要组件: 方法区(Method Area) 堆区(Heap Area) 栈区&#x…...

internal.KaptWithoutKotlincTask$KaptExecutionWorkAction 问题 ---Room数据库
Caused by: java.lang.Exception: No native library is found for os.nameMac and os.archaarch64. path/org/sqlite/native/Mac/aarch64 m3 目前使用的是MAC M3芯片的配置会出现这个问题。M1就应该就有这个问题 解决: 在project层级的build.gradle中的allprojec…...

Frequency-aware Feature Fusion for Dense Image Prediction 论文阅读
摘要:密集图像预测任务要求具有强类别信息和高分辨率精确空间边界细节的特征。为了实现这一点,现代分层模型通常利用特征融合,直接添加来自深层的上采样粗特征和来自较低层次的高分辨率特征。在本文中,我们观察到融合特征值在对象内的快速变化…...

Springboot + netty + rabbitmq + myBatis
目录 0.为什么用消息队列1.代码文件创建结构2.pom.xml文件3.三个配置文件开发和生产环境4.Rabbitmq 基础配置类 TtlQueueConfig5.建立netty服务器 rabbitmq消息生产者6.建立常规队列的消费者 Consumer7.建立死信队列的消费者 DeadLetterConsumer8.建立mapper.xml文件9.建立map…...

电磁兼容(EMC):整改案例(四)人体对EFT测试影响有多大?
目录 1. 异常现象 2. 原因分析 3. 整改方案 4. 总结 1. 异常现象 某产品按GB/T 17626.4标准进行电快速瞬变脉冲群测试,测试条件为:频率5kHz/100kHz,测试电压L,N线间2kV,L,N线对PE线4kV。测试过程中需要…...

数据可视化基础:让数据说话
一、引言 在信息洪流中,数据可视化如同灯塔,照亮了数据的海洋,让我们能够洞察数据背后的意 义。 下面是对数据可视化的详细介绍,包括定义、作用、类型、原则、工具方法以及应用场景, 并附上具体的代码示例。 二、数…...