云端录制直播流视频,上传云盘

前言

哪一天我心血来潮,想把我儿子学校的摄像头视频流录制下来,并保存到云盘上,这样我就可以在有空的时候看看我儿子在学校干嘛。想到么就干,当时花了一些时间开发了一个后端服务,通过数据库配置录制参数,以后的设想是能够通过页面去配置,能够自动捕获直播视频流,这还得要求自己先学会vue,所以还得缓缓。

实现

技术栈:Spring Boot、Webflux、r2dbc、javacv

架构图:
在这里插入图片描述
流程很简单,主要还是要用到JavaCV从视频流里捕获视频,先报错到本地,然后有一个定时任务会定时去检测目录内是否有新生成的文件,有就上传到配置的云盘(百度云)。

1、创建pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.4</version><relativePath /> <!-- lookup parent from repository --></parent><groupId>net.178le</groupId><artifactId>video-cloud-record</artifactId><version>0.0.1-SNAPSHOT</version><name>video-cloud-record</name><description>视频云录制</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-r2dbc</artifactId></dependency><dependency><groupId>dev.miku</groupId><artifactId>r2dbc-mysql</artifactId></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.22</version></dependency><dependency><groupId>org.bytedeco</groupId><artifactId>javacv-platform</artifactId><version>1.4.4</version></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpcore</artifactId><version>4.4.10</version></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.6</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>io.projectreactor</groupId><artifactId>reactor-test</artifactId><scope>test</scope></dependency></dependencies><build><finalName>video-cloud-record</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

2、定时异常信息

package net.video.record.config;import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;import lombok.extern.slf4j.Slf4j;/*** @desc 全局异常捕捉并转换异常*/
@Slf4j	
@RestControllerAdvice(basePackages = "net.video.record")
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)public Result<String> handleException(Exception e) {log.error("{}", e);return Result.error("", e.getMessage());}}

3、统一结果集

package net.video.record.config;import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Data;@Data
@AllArgsConstructor
public class Result<T> {private String code;private T data;private String msg;public static <T> Result<T> ok(T data) {return new Result<T>("0", data, "");}public static <T> Result<T> error(String code, String msg) {code = StrUtil.isEmpty(code)? "500" : code;return new Result<T>(code, null, msg);}
}

4、定义两个Model

TaskList 用来保存用户相关的录制任务

package net.video.record.entity.model;import java.time.LocalDateTime;
import java.util.Date;import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;import lombok.Data;@Data
@Table("task_list")
public class TaskList {@Idprivate Integer id;private String name;private String streamUrl;private Integer userId;private Integer status;private Integer delFlag;private LocalDateTime createTime;private LocalDateTime modifyTime;private String runRule;private LocalDateTime lastRunTime;private Integer recordTime;private Integer segTime;}

User 定义用户信息,保存了用过相关的录制参数

package net.video.record.entity.model;import java.time.LocalDateTime;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;import lombok.Data;
import lombok.experimental.Accessors;@Data
@Accessors(chain = true)
@Table("user")
public class User {public static Map<Integer, User> userMap = new ConcurrentHashMap<Integer, User>();@Idprivate Integer id;private String userName;private String password;private String bdAccessToken;private String bdRefreshToken;private LocalDateTime createTime;private LocalDateTime modifyTime;}

5、几个VO

TaskReq 任务请求参数

package net.video.record.entity.vo;import lombok.Data;
import lombok.experimental.Accessors;@Data
@Accessors(chain = true)
public class TaskReq {private Integer taskId;
}

UserReq

package net.video.record.entity.vo;import lombok.Data;@Data
public class UserReq {private String userName;private String password;
}

UserRes

package net.video.record.entity.vo;import java.time.LocalDateTime;import com.fasterxml.jackson.annotation.JsonFormat;import lombok.Data;@Data
public class UserRes {private Integer id;private String userName;private String password;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime createTime;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime modifyTime;
}

6、把网盘接口封装一下

我封装的是百度网盘,可以去网盘开放平台查看文档,这里贴出主要的上传代码。

public String upload(BdFileUpload req, TaskList task) {User user = User.userMap.get(task.getUserId());if (user == null) {throw new RuntimeException("用户信息不存在");}//大于4m的话分片,这里先不处理分片File file = req.getFile();req.setAccess_token(user.getBdAccessToken());List<String> fileMd5 = Arrays.asList(SecureUtil.md5(file));PreCreateReq preCreateReq = new PreCreateReq().setAccess_token(req.getAccess_token()).setAutoinit(1).setIsdir(0).setRtype(1).setPath("/apps/直播云存储/" + task.getId() + "/" + DateUtil.today() + "/" + file.getName()).setSize(String.valueOf(file.length())).setBlock_list(JSONUtil.toJsonStr(fileMd5));PreCreateRes preCreate = preCreate(preCreateReq);for (int i = 0; i < fileMd5.size(); i++) {SegUploadReq segUploadReq = new SegUploadReq().setAccess_token(req.getAccess_token()).setPath(preCreate.getPath()).setUploadid(preCreate.getUploadid()).setPartseq(i).setFile(req.getFile());SegUploadRes segUploadRes = SegUpload(segUploadReq);}CreateFileReq createFileReq = new CreateFileReq().setAccess_token(req.getAccess_token()).setBlock_list(JSONUtil.toJsonStr(fileMd5)).setPath(preCreateReq.getPath()).setSize(preCreateReq.getSize()).setIsdir(preCreateReq.getIsdir()).setRtype(preCreateReq.getRtype()).setUploadid(preCreate.getUploadid());CreateFileRes createFile = createFile(createFileReq);return createFile.getServer_filename();}

7、视频流录制部分

/*** 录制视频* @param inputFile 该地址可以是网络直播/录播地址,也可以是远程/本地文件路径* @param outputFile 该地址只能是文件地址,如果使用该方法推送流媒体服务器会报错,原因是没有设置编码格式* @param audioChannel 是否录制音频 1录制* @param time 录制时间* @throws Exception* @throws org.bytedeco.javacv.FrameRecorder.Exception*/public void frameRecord(String inputFile, String outputFile, int audioChannel, int time)throws Exception, org.bytedeco.javacv.FrameRecorder.Exception {// 获取视频源FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFile);// 流媒体输出地址,分辨率(长,高),是否录制音频(0:不录制/1:录制)FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, 1280, 720, audioChannel);recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);//设置分片recorder.setFormat("segment");//生成模式 实时recorder.setOption("segment_list_flags", "live");//分片时长 60srecorder.setOption("segment_time", "60");//锁定分片时长recorder.setOption("segment_atclocktime", "1");//用来严格控制分片时长recorder.setOption("break_non_keyframes", "1");//设置日志级别avutil.av_log_set_level(avutil.AV_LOG_ERROR);// 开始取视频源try {grabber.start();recorder.start();Frame frame = null;Date startDate = new Date();while ((frame = grabber.grabFrame()) != null && DateUtil.between(startDate, new Date(), DateUnit.SECOND) <= time * 60) {recorder.record(frame);}recorder.stop();grabber.stop();} finally {if (grabber != null) {grabber.stop();}}}

总结

这里我只贴出了部分代码,如果有想要了解具体实现的,也可以留言跟我交流。这个系统我也只是快速实现了一下,只达到能用的程度,其中对javacv、webflux进行了一定学习研究,后续的完善,还要看我哪天再次心血来潮。


作者其他文章推荐:
基于Spring Boot 3.1.0 系列文章

  1. Spring Boot 源码阅读初始化环境搭建
  2. Spring Boot 框架整体启动流程详解
  3. Spring Boot 系统初始化器详解
  4. Spring Boot 监听器详解
  5. Spring Boot banner详解
  6. Spring Boot 属性配置解析
  7. Spring Boot 属性加载原理解析
  8. Spring Boot 异常报告器解析
  9. Spring Boot 3.x 自动配置详解

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

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

相关文章

挑战杯 python+深度学习+opencv实现植物识别算法系统

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的植物识别算法研究与实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;4分工作量&#xff1a;4分创新点&#xff1a;4分 &#x1f9ff; 更多…

93.网游逆向分析与插件开发-游戏窗口化助手-升级经验数据获取的逆向分析

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;显示游戏数据到小助手UI 码云地址&#xff08;游戏窗口化助手 分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号&#xff1a;852c339f5e4c103390b123e0eaed…

浅析现代计算机启动流程

文章目录 前言启动流程概述磁盘分区格式MBR磁盘GPT磁盘隐藏分区 传统BIOS引导传统BIOS启动流程 UEFI引导UEFI引导程序UEFI启动流程 引导加载程序启动操作系统相关参考 前言 现代计算机的启动是一个漫长的流程&#xff0c;这个流程中会涉及到各种硬件的配置与交互&#xff0c;包…

Python接口自动化测试框架运行原理及流程

这篇文章主要介绍了Python接口自动化测试框架运行原理及流程,文中通过示例代码介绍的非常详细&#xff0c;对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 本文总结分享介绍接口测试框架开发&#xff0c;环境使用python3selenium3unittestddtrequests测试框…

【GAMES101】Lecture 17 材质

目录 材质 漫反射 镜面反射 折射-Snell’s Law Fresnel Reflection / Term&#xff08;菲涅耳项&#xff09; 微表面模型 各向同性与各向异性 BRDF的性质 测量BRDF 材质 渲染方程中的BRDF描述了物体是如何与光线作用的&#xff0c;而物体的材质决定了它看起来是怎么样…

【深度学习理论】持续更新

文章目录 1.统计学习理论 1.统计学习理论 统计学习理论&#xff0c;一款适合零成本搞深度学习的大冤种的方向 从人类学习到机器学习的对比&#xff08;学习的过程分为归纳和演绎 &#xff09;&#xff0c;引出泛化和过拟合的概念。 如何表示归纳的函数规律呢&#xff1f;以监督…

RTthread线程间通信(邮箱,消息队列,信号/软件中断)---01实际使用API函数

layout: post title: “RT-Thread线程间通信” date: 2024-2-5 15:39:08 0800 tags: RT-Thread 线程间通信 这一篇是实际使用, 代码分析看后面的文章 一般可以使用全局变量以及线程间同步进行实现 RT-Thread也提供了一部分的通信机制 邮箱 一个线程发送, 另外的线程接受信息…

高清符合要求的SCI图片使用RStudio导出

4.图片格式区别和常识 在计算机中&#xff0c;JPEG&#xff08;发音为jay-peg, IPA&#xff1a;[ˈdʒeɪpɛg]&#xff09;是一种针对照片视频而广泛使用的有损压缩标准方法。这个名称代表Joint Photographic Experts Group&#xff08;联合图像专家小组&#xff09;。此团队创…

微信小程序学习指南:从基础知识到代码展示

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

Qt 常见容器类用法(一)

目录 QMap类 QHash类 QVector类 QMap类 QMap<key,T>提供一个从类型为Key的键到类型为T的值的映射。通常&#xff0c;QMap存储的数据形式是一个键对应一个值&#xff0c;并且按照键Key的次序存储数据。为了能够支持一键多值的情况&#xff0c;QMap提供QMap<key,T&g…

0206作业

TCP&#xff08;传输控制协议&#xff09;和 UDP&#xff08;用户数据报协议&#xff09;是两种常用的网络传输协议。它们之间的主要区别在于&#xff1a; 可靠性&#xff1a;TCP 是一种可靠的传输协议&#xff0c;它提供了数据传输的确认、重传和排序功能。如果数据在传输过程…

分享76个节日PPT,总有一款适合您

分享76个节日PPT&#xff0c;总有一款适合您 76个节日PPT下载链接&#xff1a;https://pan.baidu.com/s/1-j7toLaBUBAJbkd85xe4VQ?pwd8888 提取码&#xff1a;8888 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理更不易…

C#验证字符串是否大写、小写,正则表达式vs用Char.IsUpper和Char.IsLower方法遍历字符数组

目录 一、使用的方法 1.正则表达式 2.用Char.IsUpper或Char.IsLower方法 二、源代码 1.源码 2.生成效果 一、使用的方法 1.正则表达式 正则表达式“^[A-Z]$”&#xff0c;其中[A-Z]表示匹配一个到多个大写字母。 正则表达式“^[a-z]$”&#xff0c;其中[a-z]表示匹配一个…

EasyExcel下载带下拉框和批注模板

EasyExcel下载带下拉框和批注模板 一、 代码实现 controller下载入口 /***下载excel模板* author youlu* date 2023/8/14 17:31* param response* param request* return void*/PostMapping("/downloadTemplate")public void downloadExcel(HttpServletResponse r…

Mysql-数据库优化-客户端连接参数

客户端参数 原文地址 # 连接池配置 # 初始化连接数 spring.datasource.druid.initial-size1 # 最小空闲连接数&#xff0c;一般设置和initial-size一致 spring.datasource.druid.min-idle1 # 最大活动连接数&#xff0c;一个数据库能够支撑最大的连接数是多少呢&#xff1f; …

javaEE - 23( 21000 字 Servlet 入门 -1 )

一&#xff1a;Servlet 1.1 Servlet 是什么 Servlet 是一种实现动态页面的技术. 是一组 Tomcat 提供给程序猿的 API, 帮助程序猿简单高效的开发一个 web app. 构建动态页面的技术有很多, 每种语言都有一些相关的库/框架来做这件事&#xff0c;Servlet 就是 Tomcat 这个 HTTP…

[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析

一、单继承 1、概述 C最重要的特征是代码重用&#xff0c;通过继承机制可以利用已有的数据类型来定义新的数据类型&#xff0c;新的类不仅拥有旧类的成员&#xff0c;还拥有新定义的成员。 例如一个B类继承于A类&#xff0c;或称从类A派生类B。这样的话&#xff0c;类A成为基类…

【Qt】常见问题

1.存在未解析的标识符 将build文件夹删掉重新编译。 2.左侧项目目录栏无法删除已添加项目 打开目标项目上一级的pro文件&#xff0c;将目标文件名字注释或者删除掉&#xff0c;最后保存&#xff0c;qt就会自动更新&#xff0c;将该项目隐藏掉。 3.在qt creator下添加槽函数…

模拟串口LV2,解决硬件串口资源不足问题!!!!

模拟串口通信 2.0 版本&#xff01;&#xff01; 我在前面的文章里面有写了 虚拟串口通信&#xff0c;虽然说能用&#xff0c;但是用过的小伙伴都说 “好!” 优缺点: 先说一点&#xff0c;2.0版本并不适用于同硬件串口的所有场合&#xff0c;仅仅针对自己开发的电子垃圾的主…

[office] 网优必备的10大经典函数公式! #知识分享#媒体

网优必备的10大经典函数公式! Excel软件看似简单&#xff0c;其实花样很多&#xff0c;尤其Excel表格。但其实只要用心多练&#xff0c;效率轻松提升个十倍百倍真不是问题!赶紧一起来get新技能吧~ ▋函数公式一 我们都知道从网管中查询出来的经纬度是没有小数点的。我看到不…