视频循环存储的实现

news2024/12/26 11:22:57

目录

1. 三方工具

2. 视频存储的实现 

2.1 分段存储 - 比如每15分钟

2.2 对齐到15分钟整边界

2.3 循环存储的实现 video_space_daemon.sh

3.封装

 3.1 主执行程序,修订版

3.2 创建服务 

3.3 service关联的执行脚本文件

4.额外的工作

附录A: ffmpeg视频存储,运行时错误处理 

1.运行ffmpeg存储视频时

1.1 处理

附录B 服务创建加载以及运行时异常处理 

1.service无法enable

1.1 处理


1. 三方工具

建议使用ffmpeg,这个工具多平台可用,命令行和API都有提供,非常便捷。下载的位置:

Download FFmpeg

我工作在debian环境,下载对应的源码后编译:

./configure

make

make install

2. 视频存储的实现 

2.1 分段存储 - 比如每15分钟

下面的脚本里 MIN_PER_FILE控制的是每个视频文件的最大尺寸

#usage: video_recorder <video_file_path> <min_per_file> <rtsp_path>
RTSP_URL_WITH_PASSWORD='rtsp://admin:xxxx@192.168.0.6:554/Streaming/Channels/101?transportmode=unicast&amp;profile=Profile_1'
MIN_PER_FILE=03
VIDEO_FILE_PATH='/tmp/video/' #include postfix
mkdir -p $VIDEO_FILE_PATH
ffmpeg -rtsp_transport tcp -i $RTSP_URL_WITH_PASSWORD -c:v libx265 -preset fast -crf 28 -t 00:$MIN_PER_FILE:00 $VIDEO_FILE_PATH$(date +"%Y%m%d%H%M%S").mp4

这个命令执行时因为环境的原因,可能会提示错误,我这边的一个纠错参见附录A,正确调用后:

Output #0, mp4, to '/tmp/video/20240807094508.mp4':
  Metadata:
    title           : Media Presentation
    encoder         : Lavf61.1.100
  Stream #0:0: Video: hevc (hev1 / 0x31766568), yuvj420p(pc, bt709, progressive), 1920x1080, q=2-31, 10 fps, 10240 tbn
      Metadata:
        encoder         : Lavc61.3.100 libx265
      Side data:
        cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: N/A
frame= 1555 fps= 10 q=31.9 size=   12288KiB time=00:02:35.30 bitrate= 648.2kbits/s speed=   1x 

估算,最终的视频文件尺寸,单通道.h265大概每15分钟45~50MBytes.

2.2 对齐到15分钟整边界

虽然命令行已经提供了分段存储,比如15分钟一个文件的功能,但是它没有对齐到15分钟整边界,可以看到这个逻辑。注意那个重启视频录制进程的工作使用systemctl的语法来实现的,这个在第3节封装部分会引入。

#!/bin/bash

# 获取传递的参数数量
num_args=$#

if [ $num_args -ne 1 ]; then
    echo "usage: $0 <chIdxbase1>"
    exit 1
else
    CHIDX_BASE1=$(printf "%02d" "$1")
fi


# 变量
SERVICE_NAME="guide_video_storage_ch"$CHIDX_BASE1

# 获取当前时间的秒数
now=$(date +%s)

# 获取当前分钟数和秒数
minute=$(date +%M)
second=$(date +%S)
minute=$(echo $minute | sed 's/^0*//')  # 移除前导0
second=$(echo $second | sed 's/^0*//')  # 移除前导0


# 计算最近 15 分钟整边界
remainder=$((minute % 15))
if (( remainder == 0 && second == 0 )); then
  target_time=$now
  echo $target_time
else
  if (( remainder == 0 )); then
    # 当前时间在整边界时刻,但秒数不为0
    target_time=$((now + (60 - second)))
    echo $target_time
  else
    # 计算下一个整边界的时间
    next_minute=$(( (minute + (15 - remainder)) % 60 ))
    next_hour=$(date +%H)
    if ((minute + (15 - remainder) >= 60)); then
      next_hour=$(echo $next_hour | sed 's/^0*//')  # 移除前导0
      next_hour=$(( (next_hour + 1) % 24 ))
    fi

    # 计算目标时间
    target_time=$(date -d "$next_hour:$next_minute:00" +%s)
    echo $target_time
  fi
fi

# 计算需要等待的秒数
seconds_to_wait=$((target_time - now))

# 输出等待时间
echo "等待 $seconds_to_wait 秒"
sleep $seconds_to_wait

#重启脚本
sudo systemctl restart $SERVICE_NAME

2.3 循环存储的实现 video_space_daemon.sh

为了防止出现磁盘快速磨损,使用了回差的概念。

#!/bin/bash

VIDEO_FILE_PATH='/tmp/video/' #include postfix
mkdir -p $VIDEO_FILE_PATH

CH_CNTS=5
DIRECTORY=$VIDEO_FILE_PATH
MAX_SIZE_MB=$((50*$CH_CNTS))
CTRL_SIZE_MB=$(awk "BEGIN {print $MAX_SIZE_MB * 0.8}")
TEMP_FILE="/tmp/file_sizes.txt"

# 计算目录中所有文件的总大小
total_size=$(du -sm "$DIRECTORY" | cut -f1)

# 如果总大小超过最大允许值
if [ "$total_size" -gt "$MAX_SIZE_MB" ]; then
  # 列出文件大小和路径,按时间排序(最旧的文件在前)
  find "$DIRECTORY" -type f -printf '%T+ %s %p\n' | sort | awk '{print $2, $3}' > "$TEMP_FILE"
  
  # 删除最旧的文件,直到总大小低于 90MB
  while [ "$total_size" -gt "$CTRL_SIZE_MB" ]; do
    oldest_file=$(head -n 1 "$TEMP_FILE" | awk '{print $2}')
    file_size=$(head -n 1 "$TEMP_FILE" | awk '{print $1}')
    
    # 删除文件
    rm "$oldest_file"
    
    # 更新总大小
    total_size=$((total_size - file_size / 1024 / 1024))
    
    # 重新列出文件
    tail -n +2 "$TEMP_FILE" > "$TEMP_FILE.tmp" && mv "$TEMP_FILE.tmp" "$TEMP_FILE"
  done
  
  # 删除临时文件
  rm "$TEMP_FILE"
fi

3.封装

因为视频源可能会出问题,所以需要有看护程序,最终使用Service来处理进程的遇错自动重启。然后用一个看护程序,来对齐到15分钟整边界。这里有全部的代码:

 3.1 主执行程序,修订版

#!/bin/bash
#usage: gpVideoRecorder <video_file_path> <min_per_file> <rtsp_path> <chIdxbase1>


# 函数:确保目录路径以斜杠结尾
ensure_trailing_slash() {
    local dir=$1
    if [[ "$dir" != */ ]]; then
        echo "${dir}/"
    else
        echo "$dir"
    fi
}

# 获取传递的参数数量
num_args=$#

if [ $num_args -eq 0 ]; then
    VIDEO_FILE_PATH='/tmp/video/' #include postfix
    MIN_PER_FILE=03
    RTSP_URL_WITH_PASSWORD='rtsp://admin:xxxxx@192.168.0.6:554/Streaming/Channels/101?transportmode=unicast&amp;profile=Profile_1'
    CH_STR="ch"$(printf "%02d" "0")_
elif [ $num_args -ne 4 ]; then
    echo "usage: $0 <video_file_path> <min_per_file> <rtsp_path> <chIdxbase1>"
else
    VIDEO_FILE_PATH=$1
    MIN_PER_FILE=$2
    RTSP_URL_WITH_PASSWORD=$3
    CH_STR="ch"$(printf "%02d" "$4")_
fi

# 确保目录存在
mkdir -p $VIDEO_FILE_PATH
# 确保目录路径以斜杠结尾
VIDEO_FILE_PATH=$(ensure_trailing_slash "$VIDEO_FILE_PATH")/

echo $VIDEO_FILE_PATH$CH_STR$(date +"%Y%m%d%H%M%S").mp4
ffmpeg -rtsp_transport tcp -i $RTSP_URL_WITH_PASSWORD -c:v libx265 -preset fast -crf 28 -t 00:$MIN_PER_FILE:00 $VIDEO_FILE_PATH$CH_STR$(date +"%Y%m%d%H%M%S").mp4

3.2 创建服务 

#!/bin/bash

# 获取传递的参数数量
num_args=$#

if [ $num_args -ne 1 ]; then
    echo "usage: $0 <chIdxbase1>"
    exit 1
else
    # 使用 printf 将数字格式化为两位数
    CHIDX_BASE1=$(printf "%02d" "$1")
fi


# 变量
PROGRAM_DIR="/etc/program"
SERVICE_NAME="guide_video_storage_ch"$CHIDX_BASE1
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
#service_name lookslike guide_video_storage_ch01


# 1. 创建目录
if [ ! -d "$PROGRAM_DIR" ]; then
    echo "创建目录 $PROGRAM_DIR"
    sudo mkdir -p "$PROGRAM_DIR"
fi

# 2. 创建服务文件
echo "创建服务文件 $SERVICE_FILE"
cat <<EOL | sudo tee "$SERVICE_FILE"
[Unit]
Description=video streamer local storage service

[Service]
ExecStart=sudo -E /home/app/common/start_video_recording.sh $CHIDX_BASE1
Restart=always
RestartSec=5
User=root

[Install]
WantedBy=multi-user.target
EOL

# 3. 重新加载 systemd 配置
echo "重新加载 systemd 配置"
sudo systemctl daemon-reload

# 4. 启动服务
echo "启动服务 ${SERVICE_NAME}"
sudo systemctl start "${SERVICE_NAME}"

# 5. 设置服务开机启动
echo "设置服务开机启动"
sudo systemctl enable "${SERVICE_NAME}"

echo "服务 ${SERVICE_NAME} 创建并启动完成。"

3.3 service关联的执行脚本文件

因为service不支持同时启动多个命令,所以要有这个.sh,注意那个背景进程的消灭。理论上不这样做也可以。毕竟视频文件截断的动作是从video_split_daemon.sh发出的。

#这里会对齐至整边界
sudo /home/app/common/video_split_daemon.sh 5&
# 获取 p1 的进程 ID
SPLIT_PID=$!

#space_dameon只执行一次无需关心
sudo /home/app/common/video_space_daemon.sh &

#这里的文件记录长度可以略长(注意rtsp的用户名密码的规则:)
sudo /home/app/common/gpVideoRecorder /tmp/video 30 'rtsp://admin:a1234567@192.168.0.6:554/Streaming/Channels/101?transportmode=unicast&amp;profile=Profile_1' 5

# 在脚本退出时终止后台进程 p1
trap 'kill $SPLIT_PID' EXIT

 注意最终的那个调用需要是个阻塞命令。

3.4 整个环境运行时的截屏

上面刚经历过一次重启,时间是从xx:45开始的,对齐到45分整边界,视频文件列表:
 

切换前列表切换后

能看到循环存储已经生效。 所有的可执行文件列表:

4.额外的工作

在实际使用时,至少还有如下工作要做:

  • json配置文件解析和开机boot
  • mqtt远程命令执行接口
  • 状态信息redis缓存

这个环节不再赘述。

附录A: ffmpeg视频存储,运行时错误处理 

1.运行ffmpeg存储视频时

Unrecognized option 'preset'.
Error splitting the argument list: Option not found

1.1 处理

  1.  检查是x265编码器可能没有装
  2. ./configure --enable-libx265 --enable-libx264 --disable-x86asm --enable-gpl --pkg-config="pkg-config --static"
    1. 提示:ERROR: x264 not found using pkg-config
  3. sudo apt install pkg-config
  4. sudo apt install libx264-dev
  5. sudo apt install libx265-dev
  6. ./configure --enable-libx265 --enable-libx264 --disable-x86asm --enable-gpl
  7. make
  8. make install
     

附录B 服务创建加载以及运行时异常处理 

1.service无法enable

Failed to enable unit: Unit file /etc/systemd/system/guide_video_storage_ch05.service is masked.

1.1 处理

在更新.service前,一定记得要先disable。如果出现mask:

sudo systemctl unmask guide_video_storage_ch05

sudo systemctl stop guide_video_storage_ch05

sudo systemctl disable guide_video_storage_ch05

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

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

相关文章

中电金信三步法全面助力银行数字化营销体系建设

存量用户竞争时代 精细化经营、个性化服务与多场景覆盖 成为银行经营的重要策略 营销数字化转型不可或缺 但是&#xff0c;与所有转型的曲折、阵痛等特征一样&#xff0c;银行构建数字化营销运营体系过程中&#xff0c;亦走过一些弯路&#xff0c;包括&#xff1a; 缺少顶层…

收银系统源码-连锁店版本

千呼新零售2.0系统是零售行业连锁店一体化收银系统&#xff0c;包括线下收银线上商城连锁店管理ERP管理商品管理供应商管理会员营销等功能为一体&#xff0c;线上线下数据全部打通。 私有化独立部署/全开源源码&#xff0c;系统开发语言&#xff1a; 核心开发语言: PHP、HTML…

【漏洞复现】某赛通电子文档安全管理系统 PolicyAjax SQL注入漏洞

0x01 产品简介 某赛通电子文档安全管理系统&#xff08;简称&#xff1a;CDG&#xff09;是一款电子文档安全加密软件&#xff0c;该系统利用驱动层透明加密技术&#xff0c;通过对电子文档的加密保护&#xff0c;防止内部员工泄密和外部人员非法窃取企业核心重要数据资产&…

医药行业如何对内部机密数据进行加密保护?

一、医药行业加密解决方案 1、药品研发过程中加密保护重要数据&#xff0c;防止信息泄露 考虑到药品研发部门数据安全重要性高&#xff0c;且与其他部门较少业务关系往来&#xff0c;为防止公司研发配方泄密到其他部门或者公司外&#xff0c;研发部门实行权限控制。做到研发部…

JESD204B/C协议学习笔记

JESD204B基础概念 204B包含传输层&#xff0c;链路层&#xff0c;物理层。 应用层是对 JESD204B 进行配置的接口&#xff0c;在标准协议中是不含此层&#xff0c;只是为了便于理解&#xff0c;添加的一个层。 协议层指工程中生成的IP核JESD204B&#xff0c;负责处理输入的用户…

pod详解 list-watch机制 预选优选策略 如何指定节点调度pod

K8S是通过 list-watch 机制实现每个组件的协同工作 controller-manager、scheduler、kubelet 通过 list-watch 机制监听 apiserver 发出的事件&#xff0c;apiserver 也会监听 etcd 发出的事件 scheduler的调度策略&#xff1a; 预选策略&#xff08;Predicates&#xff09;…

JavaScript中的__setitem__方法

1、问题背景 Python中存在一个名为__setitem__的方法&#xff0c;该方法能够在向对象中设置值时对其进行处理。例如&#xff0c;以下代码演示了如何在Python中使用__setitem__方法对一个字典中的键值对进行平方处理&#xff1a; class CustomDict(dict):def __setitem__(self…

PMP证书3A一次通过攻略(内含血泪教训和踩过的坑,避雷必看)

手把手教你如何3A速通PMP证书 首先&#xff1a;你考PMP到底图什么&#xff1f; 清晰、强烈的动机是意志力的基础&#xff0c;你是想要加薪升职&#xff1f;转行转岗&#xff1f;还是只是为了知识&#xff1f;无论什么理由都可以&#xff0c;当搞明白到底图什么&#xff0c;学习…

URDF在线可视化网站

​ URDF全称&#xff08;United Robotics Description Format&#xff09;统一机器人描述格式&#xff0c;是一个XML语法框架下用来描述机器人的语言格式&#xff0c;URDF在ROS界很流行。 关于URDF的详细介绍&#xff0c;请参考博文URDF学习&#xff08;一&#xff09;什么是U…

海康gige工业相机无驱动取像突破(c#实现,版本更新,你也可以移植到linux下去用)

我们前面有一个版本&#xff0c;没有整理&#xff0c;只能是500万海康gige工业相机无驱动取像成功&#xff08;黑白相机gm&#xff09;。 这里&#xff0c;版本更新了&#xff0c;可以不是500万&#xff0c;200万&#xff0c;80万也可以&#xff0c;你可以去试一试&#xff0c…

【C++二分查找 树状数组】2424. 最长上传前缀

本文涉及的基础知识点 C二分查找 树状数组 LeetCode2424. 最长上传前缀 给你一个 n 个视频的上传序列&#xff0c;每个视频编号为 1 到 n 之间的 不同 数字&#xff0c;你需要依次将这些视频上传到服务器。请你实现一个数据结构&#xff0c;在上传的过程中计算 最长上传前缀…

“AI+Security”系列第2期(一):对抗!大模型自身安全的攻防博弈

近日&#xff0c;由安全极客、Wisemodel 社区和 InForSec 网络安全研究国际学术论坛联合主办的“AISecurity”系列第 2 期——对抗&#xff01;大模型自身安全的攻防博弈线上活动如期举行。本次活动邀请了君同未来创始人兼 CEO 韩蒙、前阿里云高级安全专家郑瀚、ChaMd5 AI 组负…

浏览器突然无法正常访问网页的解决方案

文章目录 1.问题描述&#xff1a;2.解决方案3.总结 1.问题描述&#xff1a; 浏览器之前是可以正常搜索的&#xff0c;但是这次打开显示无法正常访问&#xff0c;但是部分网页又是可以正常访问的 2.解决方案 1.搜索控制面板 2.点击网络和Internet 3.点击网络共享中心 4.点击…

大数据面试SQL(五):查询最近一笔有效订单

文章目录 查询最近一笔有效订单 一、题目 二、分析 三、SQL实战 四、样例数据参考 查询最近一笔有效订单 一、题目 现有订单表t5_order&#xff0c;包含订单ID&#xff0c;订单时间&#xff0c;下单用户&#xff0c;当前订单是否有效。 请查询出每笔订单的上一笔有效订…

Fanuc机床的数据采集解决方案

对于制造业而言随着工业数字化的发展&#xff0c;完善工厂信息化管理&#xff0c;消除信息孤岛是越来越多工控人关注的事情。其中不得不提到的就是CNC系统&#xff0c;科学的程序管理和规范可以减少机床辅助时间&#xff0c;提高机床利用效率&#xff1b;及时准确的获取机床实时…

线程池c代码实现

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、线程池是什么&#xff1f;二、代码示例总结 前言 线程池c代码简单实现&#xff1a; 大致思路如下&#xff1a; 一个管理线程轮询工作线程是否空闲&#xf…

程序员如何在人工智能时代保持核心竞争力

目录 1.概述 1.1. 技术深度与广度的平衡 1.2. 软技能的培养 1.3. 持续学习和适应性 1.4. 理解和应用AI 1.5. 伦理和责任意识 2.AI辅助编程对程序员工作的影响 2.1.AI工具对编码实践的积极影响 2.2.AI工具的潜在风险 2.3.如何平衡利与弊 3.程序员应重点发展的核心能力…

RabbitMQ docker安装

后台配置文件 rabbitmq:image: rabbitmq:latestcontainer_name: rabbitmqports:- "5672:5672" # RabbitMQ server port- "15672:15672" # RabbitMQ management console portenvironment:RABBITMQ_DEFAULT_USER: adminRABBITMQ_DEFAULT_PASS: admin 若要打…

使用 Mojo 中的 Mandelbrot 和 Python 绘图

Mojo不仅非常适合编写高性能代码,而且还允许我们利用庞大的Python生态系统中的库和工具。通过无缝的Python互操作性,Mojo可以使用Python来做它擅长的事情,特别是gui,而不会牺牲关键代码的性能。让我们使用经典的Mandelbrot集合算法并在Mojo中实现它。 本教程展示了Mojo的两…

UE 选中框

【UE】框选功能_ue框选-CSDN博客 虚幻4 小功能教程系列-P33 覆盖物框选(框选场景里的物体)_哔哩哔哩_bilibili 步骤 先重载 UI中函数 OnPaint Position&#xff1a;起始位置 Size&#xff1a;大小 Brush: 选中框样式设置 在内容浏览器中新建一个“Slate笔刷” Tint&#x…