pyqt5 制作视频剪辑软件,切割视频

该软件用于切割视频,手动选取视频片段的起始帧和结束帧并保存为json文件。gui界面如下:包含快进、快退、暂停等功能,

代码如下:

# coding=UTF-8
"""
theme: pyqt5实现动作起始帧和结束帧的定位,将定位到的帧数保存json文件
time:  2024-6-27
author: cong
"""
import json
import re
import sys
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QMediaPlaylist
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

# 使用 QMediaPlayer 可以进行视频文件解码,视频播放必须将视频帧在某个界面组件上显示,
# 有 QVideoWidget 和 QGraphicsVideoItem 两种视频显示组件,也可以从这两个类继承,自定义视频显示组件。
# QMediaPlayer 也可以结合 QMediaPlaylist 实现视频文件列表播放。


class VideoWin(QWidget):
    save_flag = 0
    save_start_count = 1
    save_end_count = 2


    def __init__(self):
        super(VideoWin, self).__init__()
        self.setWindowTitle("MediaPlayer")
        # 播放画面
        self.player = QMediaPlayer()
        self.video_widget = QVideoWidget(self)  # 定义视频显示的widget,界面组件
        self.video_widget.setFixedSize(1280,720)
        self.player.setVideoOutput(self.video_widget)  # 视频播放输出的widget,就是上面定义的

        # 当前播放的进度,显示调整视频进度条
        self.label_time = QLabel()
        self.timeSlider = QSlider()
        self.timeSlider.setOrientation(Qt.Horizontal)
        self.timeSlider.setValue(0)
        self.timeSlider.setMinimum(0)
        self.player.positionChanged.connect(self.get_time)
        self.timeSlider.sliderPressed.connect(self.player.pause)
        self.timeSlider.sliderMoved.connect(self.change_time)
        self.timeSlider.sliderReleased.connect(self.player.play)

        # 打开视频
        self.open_button = QPushButton('打开')
        self.open_button.clicked.connect(self.open_file)
        # 快进
        self.right_button = QPushButton('快进')
        self.right_button.clicked.connect(self.up_time)
        # play
        self.play_button = QPushButton('播放')
        self.play_button.clicked.connect(self.player.play)
        # pause
        self.mid_button = QPushButton('暂停')
        self.mid_button.clicked.connect(self.player.pause)
        # 快退
        self.left_button = QPushButton('快退')
        self.left_button.clicked.connect(self.down_time)
        # 保存开始时间
        self.start_button = QPushButton('保存动作开始时间')
        self.start_button.clicked.connect(self.save_start_time)
        # 保存结束时间
        self.end_button = QPushButton('保存动作结束时间')
        self.end_button.clicked.connect(self.save_end_time)
        # 所有时间选定,最终保存按钮
        self.done_button = QPushButton('完成并保存')
        self.done_button.setFixedSize(100,40)
        self.done_button.clicked.connect(self.save_json)




        # 视频路径 entry 布局
        self.path_entry = QLineEdit()

        # 创建一个网格布局
        grid_layout = QGridLayout()
        self.entry_names = [f'entry_{i + 1}' for i in range(80)]
        for i in range(80):

            if (i+1) % 2 == 1:
                label = QLabel(f"start_frame_d{int((i+1)// 2 + 1)}:")
            else:
                label = QLabel(f"end_frame_d{int((i+1) / 2)}:")
            self.entry_names[i] = QLineEdit(self)
            grid_layout.addWidget(label, i // 2, (i % 2) * 2)
            grid_layout.addWidget(self.entry_names[i], i // 2, (i % 2) * 2 + 1)
        # 上述按钮布局
        button_layout = QHBoxLayout()
        button_layout.addWidget(self.open_button)
        button_layout.addWidget(self.right_button)
        button_layout.addWidget(self.play_button)
        button_layout.addWidget(self.mid_button)
        button_layout.addWidget(self.left_button)
        button_layout.addWidget(self.start_button)
        button_layout.addWidget(self.end_button)

        # 左侧布局
        left_layout = QVBoxLayout()
        left_layout.addWidget(self.video_widget)
        left_layout.addWidget(self.label_time, alignment=Qt.AlignRight)

        left_layout.addWidget(self.timeSlider)

        left_layout.addLayout(button_layout)

        left_layout.addSpacing(100)

        left_layout.addWidget(QLabel("视频路径:"))
        left_layout.addWidget(self.path_entry)

        # 中间布局
        middle_layout = QVBoxLayout()
        middle_layout.addLayout(grid_layout)
        # 右侧布局
        right_layout = QVBoxLayout()
        right_layout.addWidget(self.done_button)
        # 总布局
        all_layout = QHBoxLayout()
        all_layout.addLayout(left_layout)
        all_layout.addLayout(middle_layout)
        all_layout.addLayout(right_layout)
        self.setLayout(all_layout)
        self.showMaximized()

    # 打开视频
    def open_file(self):
        a = QFileDialog.getOpenFileUrl()[0]
        self.video_path = a.toString()
        self.player.setMedia(QMediaContent(a))  # 选取视频文件
        msg = QMessageBox.information(self, '提示', "已经打开视频文件")
        self.path_entry.setText(self.video_path)


    # 调节播放进度
    def change_time(self, num):
        self.player.setPosition(num)

    # 快进
    def up_time(self):
        # print(self.player.duration())
        # num = self.player.position() + int(self.player.duration() / 20)
        num = self.player.position() + 200
        self.player.setPosition(num)

    # 快退
    def down_time(self):
        # num = self.player.position() - int(self.player.duration() / 20)
        num = self.player.position() - 200
        self.player.setPosition(num)

    # 获取进度条进度
    def get_time(self, num):
        self.timeSlider.setMaximum(self.player.duration())
        self.timeSlider.setValue(num)
        frame_count = int(num / 1000 * 30)
        # d = QDateTime.fromMSecsSinceEpoch(num).toString("mm:ss")
        # print(d)
        all = self.player.duration()
        total_count = int(all / 1000 * 30)
        # all_d = QDateTime.fromMSecsSinceEpoch(all).toString("mm:ss")
        self.label_time.setText(str(frame_count) + '/' + str(total_count))


    def closeEvent(self, event):  # 关闭前需要self.player.pause()操作,否则报错
        self.player.pause()
        reply = QMessageBox.question(self, '提示',
                                     "是否退出",
                                     QMessageBox.Yes | QMessageBox.No,
                                     QMessageBox.No)
        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()

    def save_start_time(self):

        if self.save_flag == 0:
            self.save_flag = 1
            start_time = self.player.position()
            start_frame = int(start_time / 1000 * 30)
            self.entry_names[self.save_start_count-1].setText(str(start_frame))
            self.save_start_count += 2
            QMessageBox.information(self, "保存成功", f"已保存当前时间点:第{start_frame}帧 ")

        else:
            QMessageBox.information(self, "保存失败", f"请先保存动作结束时间 ")

    def save_end_time(self):
        if self.save_flag == 1:
            self.save_flag = 0
            end_time = self.player.position()
            end_frame = int(end_time / 1000 * 30)
            self.entry_names[self.save_end_count-1].setText(str(end_frame))
            self.save_end_count += 2
            QMessageBox.information(self, "保存成功", f"已保存当前时间点:第{end_frame}帧 ")
        else:
            QMessageBox.information(self, "保存失败", f"请先保存动作开始时间 ")

    def save_json(self):
        result = {}
        single_part = {}

        video_path = self.video_path
        print('当前保存结果来源于视频文件', video_path)
        result['video_path'] = video_path
        result['split_result'] = []
        # video_path: 'file:///D:/SplitVideo/dmh2.avi'
        file_path = re.split('/', video_path)[-1] + '.json'
        for i in range(len(self.entry_names)):
            if self.entry_names[i].text() != '':
                if (i + 1) % 2 == 1:
                    label_key = f"start_frame_d{int((i + 1) // 2 + 1)}:"
                else:
                    label_key = f"end_frame_d{int((i + 1) / 2)}:"
                single_part[label_key]= int(self.entry_names[i].text())
                # print(self.single_part)
        result['split_result'].append(single_part)

        with open(file_path, 'w') as f:
            json.dump(result, f)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.aboutToQuit.connect(app.deleteLater)
    win = VideoWin()
    win.show()
    sys.exit(app.exec())


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值