当前位置: 首页 > news >正文

使用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示例程序源码还是太稀少&#xff0c;找资料很是麻烦&#xff0c;下面是自己用业余时间开发实现的一个对批量rtsp码流源进行关键帧截图并存盘的rust demo源码记录。 要编译这个源码需要先安装vcpkg&#xff0c;然后用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&#xff1f;2. SentencePiece 基础概念2.1 SentencePiece 的工作原理2.2 SentencePiece 的优点 3. SentencePiece 的使用3.1 安装 SentencePiece3.2 训练模型与加载模型3.3 encode&#xff08;高频&#xff09;3.4 decode&#xff08;高频&#x…...

一个简单的摄像头应用程序1

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

通过PHP获取商品详情

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

【Android】获取备案所需的公钥以及签名MD5值

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

看480p、720p、1080p、2k、4k、视频一般需要多大带宽呢?

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

解决IDEA中@Autowired红色报错的实用指南:原因与解决方案

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

408知识点自检(一)

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

负载均衡--相关面试题(六)

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

【Unity踩坑】Unity更新Google Play结算库

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

Redis:hash类型

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

力扣9.30

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

kafka下载配置

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

nlp任务之预测中间词-huggingface

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

《程序猿之Redis缓存实战 · Redis 与数据库一致性》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…...

【无标题】observer: error while loading shared libraries: libmariadb.so.3处理办法

文章目录 1.记录新装的oceanbase,使用observer帮助时&#xff0c;出现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{}&#xff1a;任意类型 缓存击穿&#xff1a;一个高并发的请求查询一个缓存中不存在的数据项&#xff0c;因此这个请求穿透缓存直接到达后端数据库或数据源来获取数据。如果这种请求非常频繁&#xff0c;就会导致后端系统的负载突然…...

[MAUI]数据绑定和MVVM:MVVM的属性验证

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

2024年水利水电安全员考试题库及答案

一、判断题 1.采用水下钻孔爆破方案时&#xff0c;侧面应采用预裂爆破&#xff0c;并严格控制单响药量以保护附近建&#xff08;构&#xff09;筑物的安全。 答案&#xff1a;正确 2.围堰爆破拆除工程的实施应成立爆破指挥机构&#xff0c;并应按设计确定的安全距离设置警戒。…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...