该软件用于切割视频,手动选取视频片段的起始帧和结束帧并保存为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())