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

使用py-ffmpeg批量合成视频的脚本

我有一个小米摄像头,用它录出来的视频全部都是3s一段3s一段的。其中有几个小时的视频我需要保存,当初直接把摄像头的卡文件导出来重命名掉了,那时候没有注意,之后想剪辑/发送给别人的时候发现疯了:

1.剪辑的话,py导入特别卡

2.发送的话,打包发完打开对面是一脸懵

但是我已经重命名了文件,要是把对象再插回摄像头用自带软件导出也太折腾了。思来想去一番后,我觉得先合成,再剪辑/发送

但是问题又来了,最开始我使用格式工厂合并,但是发现格式工厂默认会对全部文件搞一次重新编码,这就把我的电脑搞得特别卡,总共几个小时1w多个文件编码了几个小时都好不了

左思右想后,还是决定自己用py整一个(因为我好像没有在格式工厂里面找到相关的功能可以禁用掉自动编码)

我决定使用FFmpeg进行操作,FFmpeg需要在官网上下载,这里需要根据各自的os选择

然后要将这个添加到工作路径中,你可以

1.把这个添加到系统变量里面(我由于没有大量的使用需求,加上希望换一个电脑也可以用,就选择下面一个方案)

2.使用 subprocess.run 指定 ffmpeg 的路径

就像这样:

# 使用 subprocess.run 指定 ffmpeg 的路径,并将输出重定向到日志文件
        with open(log_file, "w") as log:
            result = subprocess.run([ffmpeg_path] + command.split(), stdout=log, stderr=log)

这个path应该是ffmpeg.exe结尾的路径,具体可以在ffmpeg的包里面一个bin文件夹下看到、

接下来是实际操作部分:

FFmpeg 命令的格式我就不说了,毕竟也是查来了,里面两个关键的点:

1.需要提供一个带有文件路径的txt文件

可以这样生成(一定要加utf8,要不然可能会以gbk的形式生成,导致FFmpeg 识别不了中文字符)

    with open("file_list.txt", "w",encoding="utf-8") as f:
        for file in video_files:
            f.write(f"file '{file}'\n")

2.以copy模式构造ffmpeg命令

    command = f"-f concat -safe 0 -i file_list.txt -c copy {output_file}"

源代码:

import os
import subprocess
import send2trash
import time

def merge_videos_in_directory(input_dir, output_file, ffmpeg_path):
    start_time = time.time()  # 记录开始时间
    print("脚本开始运行时间:", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(start_time)))
    """
    使用 FFmpeg 从指定目录合并所有视频文件,保留原画质,并将原文件移动到回收站。
    """
    video_files = [os.path.join(input_dir, file) for file in os.listdir(input_dir)
                   if file.endswith(('.mp4', '.avi', '.mov', '.mkv'))]
    video_files.sort()

    if not video_files:
        print("没有找到视频文件。")
        return

    with open("file_list.txt", "w",encoding="utf-8") as f:
        for file in video_files:
            f.write(f"file '{file}'\n")

    # 构造FFmpeg 命令
    command = f"-f concat -safe 0 -i file_list.txt -c copy {output_file}"
    log_file = "ffmpeg_log.txt"  # 日志文件路径

    try:
        # 使用 subprocess.run 指定 ffmpeg 的路径,并将输出重定向到日志文件
        with open(log_file, "w") as log:
            result = subprocess.run([ffmpeg_path] + command.split(), stdout=log, stderr=log)

        if result.returncode != 0:
            print(f"FFmpeg 命令执行失败,详情请查看日志文件:{log_file}")
            return
        else:
            print("FFmpeg 命令执行成功,日志已保存到:", log_file)
    except Exception as e:
        print(f"运行 FFmpeg 时发生错误: {e}")
        return

    send2trash.send2trash("file_list.txt")
    end_time = time.time()  # 记录结束时间
    elapsed_time = end_time - start_time  # 计算总耗时
    hours, rem = divmod(elapsed_time, 3600)
    minutes, seconds = divmod(rem, 60)
    print("脚本结束运行时间:", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(end_time)))
    print("总耗时:{:0>2}:{:0>2}:{:05.2f}".format(int(hours), int(minutes), seconds))
    send2trash.send2trash(input_dir)


input_directory = r"你的一堆视频文件的路径"
output_video = "你的输出文件名.mkv" 
ffmpeg_path = r"ffmpeg.exe的ffmpeg程序路径"

merge_videos_in_directory(input_directory, output_video, ffmpeg_path)

最后,先拿几千个文件试了一下,效果很好,几分钟就完成了:

脚本开始运行时间: 2025-03-14 17:43:30
FFmpeg 命令执行成功,日志已保存到: ffmpeg_log.txt
脚本结束运行时间: 2025-03-14 17:48:13
总耗时:00:04:43.12

文件大小也差不多,应该没问题

ps:我最早使用的输出文件名是mp4,然后报错了:

FFmpeg 命令执行失败: ffmpeg version 2025-03-13-git-958c46800e-essentials_build-www.gyan.dev Copyright (c) 2000-2025 the FFmpeg developers
  built with gcc 14.2.0 (Rev1, Built by MSYS2 project)
  configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-bzlib --enable-lzma --enable-zlib --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-sdl2 --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxvid --enable-libaom --enable-libopenjpeg --enable-libvpx --enable-mediafoundation --enable-libass --enable-libfreetype --enable-libfribidi --enable-libharfbuzz --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-dxva2 --enable-d3d11va --enable-d3d12va --enable-ffnvcodec --enable-libvpl --enable-nvdec --enable-nvenc --enable-vaapi --enable-libgme --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libtheora --enable-libvo-amrwbenc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-librubberband
  libavutil      59. 59.100 / 59. 59.100
  libavcodec     61. 33.102 / 61. 33.102
  libavformat    61.  9.107 / 61.  9.107
  libavdevice    61.  4.100 / 61.  4.100
  libavfilter    10.  9.100 / 10.  9.100
  libswscale      8. 13.101 /  8. 13.101
  libswresample   5.  4.100 /  5.  4.100
  libpostproc    58.  4.100 / 58.  4.100
[aist#0:0/pcm_alaw @ 0000024bcba1cd00] Guessed Channel Layout: mono
Input #0, concat, from 'file_list.txt':
  Duration: N/A, start: 0.000000, bitrate: 1215 kb/s
  Stream #0:0(und): Audio: pcm_alaw (alaw / 0x77616C61), 8000 Hz, mono, s16, 64 kb/s
    Metadata:
      creation_time   : 2022-10-13T03:50:14.000000Z
      vendor_id       : [0][0][0][0]
  Stream #0:1(und): Video: hevc (Main) (hvc1 / 0x31637668), yuv420p(tv, unknown/bt709/unknown), 2304x1296, 1151 kb/s, 20 fps, 20 tbr, 90k tbn
    Metadata:
      creation_time   : 2022-10-13T03:50:14.000000Z
      vendor_id       : [0][0][0][0]
Stream mapping:
  Stream #0:1 -> #0:0 (copy)
  Stream #0:0 -> #0:1 (copy)
[mp4 @ 0000024bcb7b2640] Could not find tag for codec pcm_alaw in stream #1, codec not currently supported in container
[out#0/mp4 @ 0000024bcb7b2440] Could not write header (incorrect codec parameters ?): Invalid argument
Conversion failed!

查了一下之后,发现是小米挖的坑:它使用了一个叫做PCM Alaw的编码,这个编码正常是不允许出现在mp4文件里面的。这只能说 小米啊小米 还得是您

所以后面我把输出文件名改为了兼容性更强一点的mkv文件,后面想转mp4的话用格式工厂走一下就好了

pps:

我只测试过全都是我自己那款小米摄像头出来的mp4文件格式,虽然说我在检测文件夹下视频路径的代码里面加了好几个类型,但是不保证会不会多种东西杂在一起又闹出什么不可描述的奇葩事,所以建议转码的时候将相同源的放在一起转,最后再一起合并(是相同源,比如说一个摄像机出来的这种。因为叫mp4的文件未必内部编码都是相同的)

ppps:

如果你不想看到print,全部注释掉就好

pppps:

数据有风险,由于ffmpeg并不是在所有情况下都会报错:

1.比如我的代码之前在一些特定文件名下出错了,但是是直接正常回调(result.returncode甚至是0,不是其他的。)就导致它往下走了删除,我是发现文件大小不对才意识到的。

2.视频文件不能是损坏的或者空文件 如果是那样的话可能会报错:

[mov,mp4,m4a,3gp,3g2,mj2 @ 00000190127b9680] Format mov,mp4,m4a,3gp,3g2,mj2 detected only with low score of 1, misdetection possible!
[mov,mp4,m4a,3gp,3g2,mj2 @ 00000190127b9680] moov atom not found
[concat @ 0000019011b2f7c0] Impossible to open 'F:\record\20221013_045834.mp4'
[in#0/concat @ 0000019011b2f3c0] Error during demuxing: Invalid data found when processing input
[out#0/matroska @ 0000019012445180] video:393957KiB audio:31858KiB subtitle:0KiB other streams:0KiB global headers:0KiB muxing overhead: 0.471186%
frame=81560 fps=13679 q=-1.0 Lsize=  427822KiB time=01:07:58.00 bitrate= 859.4kbits/s speed= 684x    

并且会直接中断,留下目前为止的剩余合并好的文件

所以建议可以把

send2trash.send2trash(input_dir)

删了,检查无误后再手动删除


http://www.kler.cn/a/591990.html

相关文章:

  • CentOS7 安装 jdk8(Java安装)
  • Next.js 的渲染体系架构
  • Spring Boot 核心知识点深度详解:自动化配置 (Auto-configuration) - 解锁 Spring Boot 的 “魔法”
  • 【从零开始学习计算机科学】软件工程(三)需求工程
  • 《算法笔记》9.2小节——数据结构专题(2)->二叉树的遍历 问题 B: 二叉树
  • 批量清空 Excel 文档主题、标记、作者、保存时间、总编辑时间元数据
  • 23年蓝桥杯 ———— 阶乘求和
  • rk3588 linux的rootfs.img挂载后通过chroot切换根目录安装应用提示空间不足
  • 一、Redis简介篇
  • 一文梳理清楚Vsync/Choreographer/SurfaceFlinger/Surface/SurfaceHolder/硬件刷新频率关系
  • Springboot是怎么保证WebSocket的链接对象是唯一的
  • 孜然SEO静态页面生成系统V1.0
  • 卷积神经网络 - 卷积层
  • 《基于Spring Boot+Vue的智慧养老系统的设计与实现》开题报告
  • 【鸿蒙】封装日志工具类 ohos.hilog打印日志
  • pthon转换SR785频谱仪的代码
  • 基于mediapipe深度学习的运动人体姿态提取系统python源码
  • 项目开发 1-确定选题,制作原型
  • 【css酷炫效果】纯CSS实现波浪形分割线
  • Git 分支删除操作指南(含本地与远程)