书接上回,今天我们关注的内容是项目的主题文件,即main.py的部分
导入库
首先导入所需的库
import os
import subprocess
import pygame
import time
import curses
from pathlib import Path
import convert
大部分内容站主已经介绍过了,这里注意pathlib和convert。
pathlib是Python 3.4+中引入的一个标准库,用于面向对象的文件系统路径操作。它提供了比传统的os.path模块更直观、更Pythonic的方式来处理文件系统路径。(虽然后面我可能还用了os.path)
convert则是我们自定义的库,具体内容见下期convert.py,这里我们只需要知道它通过convert.write()把对应路径的视频转换为格式化的字符码的文件并且存储到根目录上就行了。
基本定义
首先定义视频和音频的文件路径
# 视频和音频文件路径
VIDEO_PATH = "data/1.mp4"
BGM_PATH = "data/bgm.mp3" # 音频文件输出路径
然后是帧率,决定我们每一帧图像持续的时间
# 帧率
FRAME_RATE = 1 / 30
对于我们要转换的视频,如果它自带bgm的话,其实直接从中提取是方便的。(大部分时候,我们下载的MP4格式的视频都已经有了音频),这时候我们需要特意讲音频部分提取出来,方便后续对纯视频部分转换的文档进行合并。(当然,如果不想用视频自带的音频的话,把自己的bgm.mp3放到输出路径下也是可以的)
这时我们需要定义一个把视频转换为音频的函数,我们用subprocess调用外部的FFmpeg来实现
# 提取视频中的音频
def extract_audio(video_path, output_audio_path):
# 使用 FFmpeg 提取音频
command = [
"ffmpeg",
"-i", video_path, # 输入视频路径
"-vn", # 不处理视频流
"-acodec", "libmp3lame", # 音频编码格式为 MP3
"-ab", "192k", # 音频比特率
"-ar", "44100", # 音频采样率
output_audio_path # 输出音频路径
]
subprocess.run(command, check=True)
command这个列表实际上指的是终端命令的各个组成部分,最后一句话的作用其实相当于在终端中执行如下命令:
ffmpeg -i video.mp4 -vn -acodec libmp3lame -ab 192k -ar 44100 output.mp3
前面的几项都很好理解(“-i”就是input嘛,编码格式也很常见),这个音频比特率和音频采样率是什么玩意?其实比特率可以理解为最终音频文件每秒的数据量,通常高质量的文件这个数会越大,而采样率使我们每秒对于文件的采样次数。用照片来理解的话,采样率相当于像素密度,而比特率相当于压缩质量。
这里其实不屑也是可以的,通常默认比特率通常是 128k,采样率则有音频默认,如果不是刻意追求某些特征,不会造成很大的影响。
转换音频
# 如果没有音频文件,尝试从视频提取音频
if not Path(BGM_PATH).exists():
if not Path(VIDEO_PATH).exists():
print("视频文件不存在: ", VIDEO_PATH)
extract_audio(VIDEO_PATH, BGM_PATH) # 提取音频
print(f"音频已提取并保存为: {BGM_PATH}")
经典的if not和print格式,无需多言。
# 检查并删除旧的 video_data.py 文件
if os.path.exists("video_data.py"):
os.remove("video_data.py")
这一步是为了避免老是用上一次的文本文件,当然如果想要直接缓存下来直接播放也是可以的,这就是后话了。(我觉得甚至可以做一个文件系统,保存已经缓存好的资料,当然现在太懒了)
转换文本文件
# 载入视频数据
if Path("video_data.py").exists():
from video_data import video_data
else:
video_data = convert.write(VIDEO_PATH) # 转换视频为字符数据
input("\n转换完成, 按任意键继续...")
初始化pygame音频
# 初始化 pygame 音频
pygame.mixer.init()
pygame.mixer.music.load(BGM_PATH) # 加载提取的音频
pygame.mixer.music.play() # 播放音频
没什么好说的,经典的加载播放流程。
在终端上显示视频
# 初始化 curses 库
count = 0
stdsrc = curses.initscr()
curses.start_color()
stdsrc.resize(30, 100)
now = time.time()
# 遍历并显示视频帧数据
for frame_data in video_data:
for i in range(len(frame_data)):
stdsrc.addstr(i, 0, frame_data[i], curses.COLOR_WHITE)
while time.time() - now < count * FRAME_RATE:
time.sleep(count * FRAME_RATE - time.time() + now)
stdsrc.refresh()
count += 1
curses.endwin()
这个帧率时间控制的思路还是很不错的。注意这里的stdsrc大小,最重要和我们的convert转换出来的文本文件匹配,这是后话了。
结语
这就是main.py的内容了,完整代码如下,感谢您的阅读,下期讲实现convert的过程,拜拜!
import os
import subprocess
import pygame
import time
import curses
from pathlib import Path
import convert
# 视频和音频文件路径
VIDEO_PATH = "data/1.mp4"
BGM_PATH = "data/bgm.mp3" # 音频文件输出路径
# 帧率
FRAME_RATE = 1 / 30
# 提取视频中的音频
def extract_audio(video_path, output_audio_path):
# 使用 FFmpeg 提取音频
command = [
"ffmpeg",
"-i", video_path, # 输入视频路径
"-vn", # 不处理视频流
"-acodec", "libmp3lame", # 音频编码格式为 MP3
"-ab", "192k", # 音频比特率
#"-ar", "44100", # 音频采样率
output_audio_path # 输出音频路径
]
subprocess.run(command, check=True)
# 如果没有音频文件,尝试从视频提取音频
if not Path(BGM_PATH).exists():
if not Path(VIDEO_PATH).exists():
print("视频文件不存在: ", VIDEO_PATH)
extract_audio(VIDEO_PATH, BGM_PATH) # 提取音频
print(f"音频已提取并保存为: {BGM_PATH}")
# 检查并删除旧的 video_data.py 文件
if os.path.exists("video_data.py"):
os.remove("video_data.py")
# 载入视频数据
if Path("video_data.py").exists():
from video_data import video_data
else:
video_data = convert.write(VIDEO_PATH) # 转换视频为字符数据
input("\n转换完成, 按任意键继续...")
# 检查音频文件
if not Path(BGM_PATH).exists():
print("音乐不存在: ", BGM_PATH)
# 初始化 pygame 音频
pygame.mixer.init()
pygame.mixer.music.load(BGM_PATH) # 加载提取的音频
pygame.mixer.music.play() # 播放音频
# 初始化 curses 库
count = 0
stdsrc = curses.initscr()
curses.start_color()
stdsrc.resize(30, 100)
now = time.time()
# 遍历并显示视频帧数据
for frame_data in video_data:
for i in range(len(frame_data)):
stdsrc.addstr(i, 0, frame_data[i], curses.COLOR_WHITE)
while time.time() - now < count * FRAME_RATE:
time.sleep(count * FRAME_RATE - time.time() + now)
stdsrc.refresh()
count += 1
curses.endwin()