网上最近很火的段子是各种营销号的文案,比方说下面的段子
核桃核不能吞下去是怎么回事呢?核桃核相信大家都很熟悉,但是核桃核不能吞下去是怎么回事呢,下面就让小编带大家一起了解吧。核桃核不能吞下去,其实就是核桃核太大了,吞下去容易噎着,大家可能会很惊讶核桃核怎么会不能吞下去呢?但事实就是这样,小编也感到非常惊讶。这就是关于核桃核不能吞下去的事情了,大家有什么想法呢,欢迎在评论区告诉小编一起讨论哦!
其实我平时见到这些文案或者视频都很少,因为我没用过今日头条、抖音之类的 APP
然后我就在想,如果指定文案、BGM、视频,给定这三个东西,能否批量生成营销号视频,于是就有了这个项目
项目说明我录了个视频放在 B 站了,源码放在了 Github
下面详细说明一下代码吧
设计思路
首先我把这个程序分为了以下几步:
- 剪裁视频→
getVideo()
- 将台词写入文本→
getText(file)
- 获取视频总时长→
getLength(video)
- 给视频添加背景音乐→
add_audio(video, mp3, output='out.mp4')
- 给视频添加字幕→
subTitle(text_file, video_file, output='out_sub.mp4')
- 文本转人声→讯飞 API
- 给视频添加人声→
add_people(mp3_file, video_file)
- 清除中间生成的文件→
clean()
1. 裁剪视频
对于大部分营销号视频来说,长度都在 45 秒左右,我也没有弄的太复杂,就直接将给定的 mp4 文件的前 50 秒保留了下来,其余的部分不要
- def getVideo():
- cmd = 'ffmpeg -y -i in.mp4 -ss 00:00:00 -t 00:00:50 -acodec copy -vcodec copy -async 1 in_sub.mp4'
- subprocess.call(cmd, shell=True)
上面代码的意思是在 cmd 命令行中执行 cmd
这句命令,这个命令的意思是将 in.mp4
从 0 秒开始,裁剪到 50 秒,并保存为 in_sub.mp4
文件
2. 将台词写入文本
这个其实是最简单的,网上有很多 “营销号文案生成器”,我随便找了个源码借鉴了下。首先你需要在一个文本文件中设定好主体
、事件
、另一种说法
,例如
那么他就会生成文案
核桃核不能吞下去是怎么回事呢?核桃核相信大家都很熟悉,但是核桃核不能吞下去是怎么回事呢,下面就让小编带大家一起了解吧。核桃核不能吞下去,其实就是核桃核太大了,吞下去容易噎着,大家可能会很惊讶核桃核怎么会不能吞下去呢?但事实就是这样,小编也感到非常惊讶。这就是关于核桃核不能吞下去的事情了,大家有什么想法呢,欢迎在评论区告诉小编一起讨论哦!
其实非常简单,就是一个字符串替换
- def getText(file): # 组装台词
- with open(file) as f:
- body = f.readline().strip()
- thing = f.readline().strip()
- other_word = f.readline().strip()
-
- # 主题框架
- txt = '''{}{}是怎么回事呢?:4:0
- {}相信大家都很熟悉,但是{}{}是怎么回事呢?:7:4
- 下面就让小编带大家一起了解吧:3:11
- {}{},其实就是{}:8:15
- 大家可能会很惊讶{}怎么会{}呢?:6:23
- 但事实就是这样,小编也感到非常惊讶:5:29
- 这就是关于{}{}的事情了,大家有什么想法呢?:6:34
- 欢迎在评论区告诉小编一起讨论哦!:5:40'''.format(body, thing, body, body, thing, body, thing, other_word, body, thing, body, thing)
-
- # 台词写入
- with open('text.txt', mode='w') as f:
- f.write(txt)
文本后面的数字代表意思分别是字幕的持续时间、开始出现的时间。比方说下面就让小编带大家一起了解吧
这句话就会从第 11 秒开始出现,持续显示 3 秒
3. 获取视频总时长
做这一步主要是考虑到 BGM 和视频应该差不多时长,假如 BGM 有 60 秒,视频只有 50 秒,那就会出现这样一个现象,整个视频的时长是 60 秒,但是当视频播放到第 50 秒之后,画面就不变化了,而 BGM 还在播放
所以这里获取的视频时长,是为了后面剪裁 BGM 使用的
但是又有人可能会觉得,你一开始不是设定了 50 秒,那这里为啥还要动态获取时长,而不写 50 秒呢?因为我考虑到后面的可扩展性,如果有用户想要修改视频时长,比方说改为 60 秒,那代码就有很多地方也要修改。但是如果我用一个变量来代替,用户就只需要修改第一步里的视频时长即可
- def getLength(video): # 获取视频时长
- cmd = 'ffprobe -v quiet -select_streams v -show_entries stream=duration -of csv="p=0" {video}'.format(video=video)
- seconds = os.popen(cmd, 'r')
- seconds = math.ceil(float(seconds.read()))
- m, s = divmod(seconds, 60)
- h, m = divmod(m, 60)
- return "%02d:%02d:%02d" % (h, m, s)
由于 ffmpeg
这个库对时间的格式有要求,必须是 00:00:00
这种格式,所以我稍微做了一些处理
4. 给视频添加背景音乐
这一步里面又分三小步
- 裁剪 BGM
- 降低 BGM 音量
- 给视频添加 BGM
为什么要降低 BGM 音量,是因为我在后续测试的过程中发现 BGM 声音有点太大了,掩盖住了人声
- def add_audio(video, mp3, output='out.mp4'): # 将背景音乐添加到视频中
- BGM = 'ffmpeg -i {mp3} -ss 00:00:00.0 -t {time} -acodec copy BGM.mp3'.format(mp3=mp3, time=total_time)
- subprocess.call(BGM, shell=True)
-
- volume = 'ffmpeg -i BGM.mp3 -vcodec copy -af "volume=-20dB" BGM_volume.mp3'
- subprocess.call(volume, shell=True)
-
- command = "ffmpeg -i {mp3} -i {video} -y {output}".format(video=video, mp3='BGM_volume.mp3', output=output)
- subprocess.call(command, shell=True)
5. 给视频添加字幕
添加字幕我用的是 moviepy
这个库,它里面有一个 TextClip
方法,参数有很多,可以非常方便的添加字幕对象,然后将这些对象和视频对象一起进行整合,就可以了
- def subTitle(text_file, video_file, output='out_sub.mp4'):
- video1 = VideoFileClip(video_file)
- sentences = [] # 台词列表
- with open(text_file) as f:
- text_tmp = f.readlines()
- for i in text_tmp:
- sentences.append(i.strip().split(':'))
- print(sentences)
-
- txts = [] # 所有字幕剪辑
- with open('args.txt') as f:
- color = f.readlines()[4].strip()
- for sentence, span, start in sentences:
- txt = (TextClip(sentence, fontsize=50, align='center', color=color, font='SimHei')
- .set_position(("center","bottom")).set_duration(int(span)).set_start(int(start)))
- txts.append(txt)
-
- video2 = CompositeVideoClip([video1, *txts])
- video2.write_videofile(output)
6. 文本转人声
所有的步骤都可以自己完成,唯独文本转人声不行,只能调用第三方接口,百度、腾讯、讯飞都可以,这里我用的讯飞的接口
代码也是使用的讯飞提供的 demo (python3),由于太多了,就不在这放出来了
实现的时候有一个细节要提一下,就是如何做到字幕和人声同步
从上面的添加字幕可以看出,字幕的各种时间都是设定死的,那么能操作的也就是有人声了。具体操作我是这样做的,每一个字幕,生成一个人声,一共有 8 段字幕,所以我有 8 个 mp3 文件,由于人声是无法设置持续时间的,只能设置什么时间开始发声,即只能设置开始时间,所以我把每一个 mp3 文件的开始时间都设置成每个字幕的开始时间相同。这样做就会出现一个现象,假设某一段文本非常短,很快就读完了,字幕也消失了,但是下一段字幕以及对应的人声不会立即出现,而是单纯的只有 BGM,只有等这段字幕和人声到点了,它们才会出现。不过我测试了实际效果并不突兀
7. 给视频添加人声
- def addPeople(mp3_file, video_file):
- my_clip = VideoFileClip(video_file)
- audio_background = AudioFileClip(mp3_file)
- final_audio = CompositeAudioClip([my_clip.audio, audio_background])
- final_clip = my_clip.set_audio(final_audio)
- final_clip.write_videofile('final.mp4')
8. 清除中间生成的文件
这步也非常简单,因为使用 ffmpeg
库的时候,中间有一些临时保存的 mp3、mp4 文件,在最后的时候删除掉就行了
- def addPeople(mp3_file, video_file):
- my_clip = VideoFileClip(video_file)
- audio_background = AudioFileClip(mp3_file)
- final_audio = CompositeAudioClip([my_clip.audio, audio_background])
- final_clip = my_clip.set_audio(final_audio)
- final_clip.write_videofile('final.mp4')
以上就是这个项目的几乎全部代码说明
报错了报错了!!求帮助!
subTitle ('text.txt', 'out.mp4') # 给视频添加字幕Traceback (most recent call last):
File "F:/python/python/ 程序 /funney/run.py", line 220, in <module>
File "F:/python/python/ 程序 /funney/run.py", line 166, in subTitle
txt = (TextClip(sentence, fontsize=fontsize, align='center', color=color, font='SimHei')File "F:pythonpython 程序 funneyvidio_create_venvlibsite-packagesmoviepyvideoVideoClip.py", line 1146, in init
raise IOError(error)OSError: MoviePy Error: creation of None failed because of the following error:
建议你看下这篇文章,我记得我当时也报了这个错的
https://blog.csdn.net/kd_2015/article/details/80157713
请认真看,这篇文章写的非常详细,一定要按照他的步骤一步一步做