ffmpeg视频拼接.md

视频拼接参考: https://www.jianshu.com/p/98a0c091c4bf
ffmpeg下载: http://ffmpeg.org/
拼接参考: https://blog.csdn.net/east196/article/details/79821361

import os
import tempfile

def get_temp_file(self, data, suffix=None):  # 传入缓存路径,数据,文件后缀例如 ".txt"
    """临时文件,需要close, tmp_file_obj.close()"""
    if not os.path.exists(self.tempfile_tmp_path):
        os.makedirs(self.tempfile_tmp_path)
    tmp_file_obj = tempfile.NamedTemporaryFile(suffix=suffix, dir=self.tempfile_tmp_path)
    tmp_file_obj.write(data)
    tmp_file_obj.seek(0)
    return tmp_file_obj
import os
import shutil
import threading
import uuid


class DataUtils(object):

    def __init__(self, storage_path="/data/report"):
        # 文件存储路径
        self.storage_path = storage_path
        # 缓存路径
        self.tmp_path = os.path.join(storage_path, "tmp/").replace("\\", "/")

    def save_data(self, task_id, device_id, file_obj, path=None):
        """保存文件,同名覆盖.
        path传入绝对路径的话只有path生效,相对路径的话,path会添加到file_path后面
        """

        file_path = os.path.join(self.storage_path, task_id, device_id).replace("\\", "/")
        file_name = file_obj.name.replace("\\", "/").split("/")[-1]

        if path:
            dir_name = os.path.dirname(path)
            base_name = os.path.basename(path)
            if dir_name:
                file_path = os.path.join(file_path, dir_name).replace("\\", "/")
            if base_name:
                file_name = base_name

        if not os.path.exists(file_path):
            os.makedirs(file_path)
        dst_path = os.path.join(file_path, file_name).replace("\\", "/")

        # 保存
        if 'b' in file_obj.mode:
            with open(dst_path, "wb") as f:
                f.write(file_obj.read())
                return

        with open(dst_path, "w") as f:
            f.write(file_obj.read())

    def async_save_data(self, task_id, device_id, file_obj, path=None):
        threading.Thread(target=self.save_data, args=(task_id, device_id, file_obj, path)).start()

    def stitching_video_mp4(self, task_id, device_id, video_path=None, dst_path="video.mp4"):
        """将目录下多个mp4视频拼接成一个,系统需要安装ffmpeg,并加入环境变量
        video_path 要合并的视频所在目录,
        dst_path传入绝对路径的话只有dst_path生效,相对路径的话,dst_path会添加到file_path后面
        """
        if not dst_path.endswith('.mp4'):
            raise RuntimeError("dst_path,目标路径必须以 '.mp4' 结尾")

        if not video_path:
            video_path = os.path.join(self.storage_path, task_id, device_id, 'video').replace("\\", "/")  # 视频源默认目录
        else:
            video_path = video_path.replace('\\', '/')

        # 目录不存在就返回
        if not os.path.exists(video_path) or not os.path.isdir(video_path):
            raise RuntimeError("video_path 参数路径不存在或者不是目录: " + str(video_path))

        video_list = []
        for name in os.listdir(video_path):
            if name.endswith(".mp4"):
                video_list.append(name.split('.')[0])

        # 没有 mp4 就返回
        if not video_list:
            return

        if not os.path.exists(self.tmp_path):  # 检查 tmp 目录是否存在
            os.makedirs(self.tmp_path)
        tmp_output_path = os.path.join(self.tmp_path, str(uuid.uuid1()) + '.mp4').replace("\\", "/")  # 临时video文件路径

        cmd_ts = "echo y | ffmpeg -i %s -vcodec copy -acodec copy -vbsf h264_mp4toannexb %s"
        cmd_out = 'echo y | ffmpeg -i "concat:%s" -acodec copy -vcodec copy -absf aac_adtstoasc %s'
        video_list.sort()  # 视频拼接顺序
        src_list = []
        out_ts_list = []
        for video_name in video_list:
            src_path = os.path.join(video_path, video_name + ".mp4").replace("\\", "/")
            ts_path = os.path.join(video_path, video_name + ".ts").replace("\\", "/")
            get_ts = cmd_ts % (src_path, ts_path)

            os.system(get_ts)
            src_list.append(src_path)
            out_ts_list.append(ts_path)
        get_out = cmd_out % ("|".join(out_ts_list), tmp_output_path)  # 生成临时video文件
        os.system(get_out)

        for ts in out_ts_list:  # 删除中间文件
            os.remove(ts)

        if os.path.exists(tmp_output_path):  # 临时文件存在
            # 拼接后的文件生成了才可以删除源文件,但是相同目录存在同名文件的情况不能处理,应对cmd异常。
            for ts in src_list:
                os.remove(ts)  # 删除源目录文件
            output_path = os.path.join(video_path, os.path.dirname(dst_path)).replace("\\", "/")  # 存储目录路径
            if not os.path.exists(output_path):  # 创建目标路径
                os.makedirs(output_path)
            output_path = os.path.join(output_path, os.path.basename(dst_path)).replace("\\", "/")  # 存储文件路径
            # 移动到目标路径
            shutil.move(tmp_output_path, output_path)
            # return output_path
        # return None

    def async_stitching_video_mp4(self, task_id, device_id, video_path=None, dst_path="video.mp4"):
        threading.Thread(target=self.stitching_video_mp4, args=(task_id, device_id, video_path, dst_path)).start()

仅供参考
目录