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

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

前言

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

相关文章:

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

前言 哪一天我心血来潮&#xff0c;想把我儿子学校的摄像头视频流录制下来&#xff0c;并保存到云盘上&#xff0c;这样我就可以在有空的时候看看我儿子在学校干嘛。想到么就干&#xff0c;当时花了一些时间开发了一个后端服务&#xff0c;通过数据库配置录制参数&#xff0c;…...

【靶场实战】Pikachu靶场XSS跨站脚本关卡详解

Nx01 系统介绍 Pikachu是一个带有漏洞的Web应用系统&#xff0c;在这里包含了常见的web安全漏洞。 如果你是一个Web渗透测试学习人员且正发愁没有合适的靶场进行练习&#xff0c;那么Pikachu可能正合你意。 Nx02 XSS跨站脚本概述 Cross-Site Scripting 简称为“CSS”&#xff…...

蓝桥杯每日一题-----数位dp

前言 今天浅谈一下数位dp的板子&#xff0c;我最初接触到数位dp的时候&#xff0c;感觉数位dp老难了&#xff0c;一直不敢写&#xff0c;最近重新看了一些数位dp&#xff0c;发现没有想象中那么难&#xff0c;把板子搞会了&#xff0c;变通也会变的灵活的多&#xff01; 引入…...

sklearn 计算 tfidf 得到每个词分数

from sklearn.feature_extraction.text import TfidfVectorizer# 语料库 可以换为其它同样形式的单词 corpus [list(range(-5, 5)),list(range(-6,4)),list(range(12)),list(range(13))]# corpus [ # [Two, wrongs, don\t, make, a, right, .], # [The, pen, is, might…...

Qt拖拽事件,实现控件内项的相互拖拽

文章目录 1拖拽演示2 步骤3 实现 这里主要以QTableview控件为例&#xff0c;实现表格内数据的相互拖拽。 1拖拽演示 2 步骤 自定以QTableView类&#xff0c;在自定义类中重写拖拽事件&#xff1a; void dropEvent(QDropEvent *event); void dragEnterEvent(QDragEnterEvent *…...

基于MATLAB实现的OFDM仿真调制解调,BPSK、QPSK、4QAM、16QAM、32QAM,加性高斯白噪声信道、TDL瑞利衰落信道

基于MATLAB实现的OFDM仿真调制解调&#xff0c;BPSK、QPSK、4QAM、16QAM、32QAM&#xff0c;加性高斯白噪声信道、TDL瑞利衰落信道 相关链接 OFDM中的帧&#xff08;frame&#xff09;、符号&#xff08;symbol&#xff09;、子载波&#xff08;subcarriers&#xff09;、导频…...

Redis核心技术与实战【学习笔记】 - 21.Redis实现分布式锁

概述 在《20.Redis原子操作》我们提到了应对并发问题时&#xff0c;除了原子操作&#xff0c;还可以通过加锁的方式&#xff0c;来控制并发写操作对共享数据的修改&#xff0c;从而保证数据的正确性。 但是&#xff0c;Redis 属于分布式系统&#xff0c;当有多个客户端需要争…...

17.Golang channel的基本定义及使用

目录 概述实践无缓冲 channel代码结果 缓冲 channel代码结果 channel的关闭特点代码结果range代码结果 select channel代码结果 结束 概述 此篇文章介绍 channel 的用法 无缓冲 channel缓冲 channelchannel的关闭特点range channelselect channel 每一种&#xff0c;配上完整…...

Linux - iptables 防火墙

一. 安全技术和防火墙 1.安全技术 入侵检测系统&#xff08;Intrusion Detection Systems&#xff09;&#xff1a;特点是不阻断任何网络访问&#xff0c;量化、定位来自内外网络的威胁情况&#xff0c;主要以提供报警和事后监督为主&#xff0c;提供有针对性的指导措施和安全…...

如何在FBX剔除Lit.shader依赖

1&#xff09;如何在FBX剔除Lit.shader依赖 2&#xff09;Unity出AAB包&#xff08;PlayAssetDelivery&#xff09;模式下加载资源过慢问题 3&#xff09;如何在URP中正确打出Shader变体 4&#xff09;XLua打包Lua文件粒度问题 这是第371篇UWA技术知识分享的推送&#xff0c;精…...

cesium-测量高度垂直距离

cesium做垂直测量 完整代码 <template><div id"cesiumContainer" style"height: 100vh;"></div><div id"toolbar" style"position: fixed;top:20px;left:220px;"><el-breadcrumb><el-breadcrumb-i…...

Adobe Illustrator CEP插件开发入门指南

引言 Adobe Creative Cloud&#xff08;创意云&#xff09;中的Illustrator作为一款全球领先的矢量图形设计软件&#xff0c;为设计师提供了丰富的功能和无限的创作可能性。为了进一步增强其功能并满足个性化工作流程需求&#xff0c;Adobe引入了Common Extensibility Platform…...

【Spring】自定义注解 + AOP 记录用户的使用日志

目录 ​编辑 自定义注解 AOP 记录用户的使用日志 使用背景 落地实践 一&#xff1a;自定义注解 二&#xff1a;切面配置 三&#xff1a;Api层使用 使用效果 自定义注解 AOP 记录用户的使用日志 使用背景 &#xff08;1&#xff09;在学校项目中&#xff0c;安防平台…...

linux互斥锁:递归锁,非递归锁用法详解

在实际的项目中经常涉及到共享资源,共享资源被多个线程访问会出现竞争现象;为了解决竞争和保护共享资源常用的机制之一就是互斥锁! 互斥锁又分为递归锁和非递归锁,互斥锁默认是非递归锁,也是我们常用的上锁方式。那么什么是递归锁和非递归锁呢? 非递归锁(Non-recursive …...

MacOS安装dmg提示已文件已损坏的解决方法

MacOS安装dmg提示已文件已损坏的解决方法 导致原因是应用没有上传到苹果的appstroe&#xff0c;系统限制了安装&#xff0c;破碎提示是苹果的误导小手段 方法 一 App 在macOS Catalina&#xff08;比较新的系统&#xff0c;例如m1&#xff0c;m2也适用&#xff09;下提示已损坏…...

前端输入框简单实现检测@成员输入

大体逻辑是 给input框添加一个input监听&#xff0c;并判断输入是否为获取当前光标的位置&#xff0c;你输入的肯定在光标之前&#xff0c;且肯定是最后一个input输入的内容换行可以被认为空格&#xff0c;需要进行全局替换判断成功的逻辑分为两部分&#xff0c;前方一般来说是…...

通过与chatGPT交流实现零样本事件抽取

1、写作动机&#xff1a; 近来的大规模语言模型&#xff08;例如Chat GPT&#xff09;在零样本设置下取得了很好的表现&#xff0c;这启发作者探索基于提示的方法来解决零样本IE任务。 2、主要贡献&#xff1a; 提出了基于chatgpt的多阶段的信息抽取方法&#xff1a;在第一阶…...

使用nodejs和html布局一个简单的视频播放网站,但是使用localhost:端口访问html无法加载视频

js代码&#xff1a; // app.js const express require(express); const path require(path); const app express();// 设置静态文件目录&#xff0c;这里假设你的视频文件在public/videos/目录下 app.use(express.static(path.join(__dirname, )));// 设置主页路由&#xf…...

【AG32VF407】国产MCU+FPGA Verilog双边沿检测输出方波

视频讲解 [AG32VF407]国产MCUFPGA Verilog双边沿检测输出方波 实验过程 本次使用使用AG32VF407开发板中的FPGA&#xff0c;使用双clk的双边沿进行检测&#xff0c;同步输出方波 同时可以根据输出的方波检测clk的频率&#xff0c;以及双clk的相位关系&#xff0c;如下为verilog…...

[晓理紫]每日论文分享(有中文摘要,源码或项目地址)--强化学习、模仿学习、机器人

专属领域论文订阅 关注{晓理紫}&#xff0c;每日更新论文&#xff0c;如感兴趣&#xff0c;请转发给有需要的同学&#xff0c;谢谢支持 如果你感觉对你有所帮助&#xff0c;请关注我&#xff0c;每日准时为你推送最新论文。 为了答谢各位网友的支持&#xff0c;从今日起免费为3…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

css实现圆环展示百分比,根据值动态展示所占比例

代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

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

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

visual studio 2022更改主题为深色

visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中&#xff0c;选择 环境 -> 常规 &#xff0c;将其中的颜色主题改成深色 点击确定&#xff0c;更改完成...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

【C++进阶篇】智能指针

C内存管理终极指南&#xff1a;智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

LabVIEW双光子成像系统技术

双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制&#xff0c;展现出显著的技术优势&#xff1a; 深层组织穿透能力&#xff1a;适用于活体组织深度成像 高分辨率观测性能&#xff1a;满足微观结构的精细研究需求 低光毒性特点&#xff1a;减少对样本的损伤…...

django blank 与 null的区别

1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是&#xff0c;要注意以下几点&#xff1a; Django的表单验证与null无关&#xff1a;null参数控制的是数据库层面字段是否可以为NULL&#xff0c;而blank参数控制的是Django表单验证时字…...