When I concatenate videos in Moviepy I get no sound in the output file, I try using various parameters but no clue.
This is my code:
import moviepy.editor as mp
import os
dir_path = os.path.dirname(os.path.realpath(__file__))
clip1 = mp.VideoFileClip("V1.mp4")
clip2 = mp.VideoFileClip(dir_path+"\\V2.mp4")
clip3 = mp.VideoFileClip(dir_path+"\\V3.mp4")
output_movie = 'new_movie1.mp4'
final_clip = mp.concatenate_videoclips([clip1,clip2,clip3])
final_clip.write_videofile(output_movie, remove_temp=False, bitrate="5000k",audio=True, audio_codec="aac",codec='mpeg4')
I tried codec="libx264"
I solved this by adding a temporary audio file path. Just change your final line of code to this:
final_clip.write_videofile(output_movie, temp_audiofile='temp-audio.m4a', remove_temp=True, codec="libx264", audio_codec="aac")
You're specifying where MoviePy can store its temp audio file. Also, change the parameter remove_temp to True so the temp file will be cleaned up automatically.
I solved this through a workaround using ffmpeg directly. It uses the temp audio file and video file from moviepy to create a final file. I found that moviepy 1.0.1 does not call ffmpeg with the right arguments to combine the video and audio for mp4 video. This link helped me with ffmpeg:https://superuser.com/questions/277642/how-to-merge-audio-and-video-file-in-ffmpeg
final_clip.write_videofile(moviepy_outfile, temp_audiofile=temp_audiofile,codec="libx264",remove_temp=False,audio_codec='aac')
import subprocess as sp
command = ['ffmpeg',
'-y', #approve output file overwite
'-i', str(moviepy_outfile),
'-i', str(temp_audiofile),
'-c:v', 'copy',
'-c:a', 'copy',
'-shortest',
str(output_movie) ]
with open(ffmpeg_log, 'w') as f:
process = sp.Popen(command, stderr=f)
Related
I have a .m3u8 link which I'm trying to download as an .mp3 file.
import m3u8
import subprocess
import requests
link = 'https://cs9-9v4.vkuseraudio.net/s/v1/ac/NeMmHNX2Iyt08MZ4z5fELAMybgSNR6T1xYEcBEv5Kdsenci3KHOAC-1fKapAV9vxwVOBIik40I4DwfrN-a_jtjILYVcx3mLTNCzKo1UF-UhbKOztLrboF9NEn1jzZs1Jl0ijfmccog6aAcB4PcdnrxPzXY7WCMVWtUjWKOgHad5a-g0/index.m3u8'
m3u8_parsed = m3u8.load(link)
with open('track.ts', 'wb') as f:
for segment in m3u8_parsed.segments:
r = requests.get(segment.absolute_uri)
f.write(r.content)
subprocess.run(['ffmpeg', '-i', 'track.ts', 'track.mp3'])
The result is that the track.ts file takes up 6MB storage but doesn't have sound when I try to play it with the VLC Player, while track.mp3 is basically an empty file taking up a couple kilobytes.
You only need FFmpeg to retrieve the audio:
import subprocess
link = 'https://cs9-9v4.vkuseraudio.net/s/v1/ac/NeMmHNX2Iyt08MZ4z5fELAMybgSNR6T1xYEcBEv5Kdsenci3KHOAC-1fKapAV9vxwVOBIik40I4DwfrN-a_jtjILYVcx3mLTNCzKo1UF-UhbKOztLrboF9NEn1jzZs1Jl0ijfmccog6aAcB4PcdnrxPzXY7WCMVWtUjWKOgHad5a-g0/index.m3u8'
subprocess.run(['ffmpeg', '-i', link, 'track.mp3'])
This resulted in a playable mp3 file when I tried it.
I am trying to combine a .mp4 file with a .wav file. I am rendering my mp4 with cv2 videowriter, and I don't think it has anyway of incorporating audio with it. I have tried moviepy.editor, and ffmpeg. moviepy.editor kept messing up the video file and ffmpeg repeatedly kept giving me an error that it couldn't edit existing files in-place. Combining .mp4 with another audio file type is also fine, but if so it would be nice to also answer how to convert midi files to the file type you answered with. Thanks for the help!
moviepy.editor workflow:
video = mpe.VideoFileClip(mp4_path)
os.system(f"timidity {midi_path} -Ow -o {wav_path)}") # Convert .mid to .wav
video = video.set_audio(mpe.AudioFileClip(wav_path))
video.write_videofile(mp4_path, fps=fps)
ffmpeg workflow:
video = ffmpeg.input(mp4_path)
os.system(f"timidity {midi_path} -Ow -o {wav_path)}") # Convert .mid to .wav
audio = ffmpeg.input(wav_path)
video = ffmpeg.output(video, audio, path, vcodec='copy', acodec='aac', strict='experimental')
ffmpeg.run(video)
I tested both modules and for moviepy I get correct output video with audio even if I use the same name as output. So I don't know what can mess with output.
For ffmpeg I had to use different name for output file to resolve problem with couldn't edit existing files in-place
I had to also use object.video and object.audio to replace audio in output file.
video = ffmpeg.input(video_path).video # get only video channel
audio = ffmpeg.input(audio_path).audio # get only audio channel
My testing code
def test_moviepy(video_path, audio_path, output_path='output-moviepy.mp4', fps=24):
import moviepy.editor as mpe
print('--- moviepy ---')
video = mpe.VideoFileClip(video_path)
video = video.set_audio(mpe.AudioFileClip(audio_path))
video.write_videofile(output_path, fps=fps)
def test_ffmpeg(video_path, audio_path, output_path='output-ffmpeg.mp4', fps=24):
import ffmpeg
print('--- ffmpeg ---')
video = ffmpeg.input(video_path).video # get only video channel
audio = ffmpeg.input(audio_path).audio # get only audio channel
output = ffmpeg.output(video, audio, output_path, vcodec='copy', acodec='aac', strict='experimental')
ffmpeg.run(output)
# --- main ---
video_path = 'movie.mp4'
audio_path = 'sound.wav'
output_path = 'output.mp4'
test_moviepy(video_path, audio_path)#, output_path)
test_ffmpeg(video_path, audio_path)#, output_path)
EDIT:
After installing python module graphviz and program graphviz I could run
ffmpeg.view(output, filename='output-ffmpeg.png')
to get image
I created script which one downloading video and sound from Youtube, and after that merging sound and video with ffmpeg, i wondering is another way to make same result but in faster way? Because this script takes about 7 min ~ depends on Video quality and duration. My code bellow:
from pytube import YouTube
import sys
import ffmpeg
import os
class Downloader(YouTube):
def __init__(self, link):
self.link = YouTube(link)
self.hq = []
self.best_video = []
self.best_sound = []
def stream_objects(self):
q = [self.hq.append(x) for x in self.link.streams.all()]
self.best_video.append(str(self.hq[1]).split()[1].split('\"')[1])
self.best_sound.append(str(self.hq[-1]).split()[1].split('\"')[1])
return self.best_video, self.best_sound
def downloady(self):
vid = self.link.streams.get_by_itag(str(self.best_video).strip("['']"))
audio = self.link.streams.get_by_itag(str(self.best_sound).strip("['']"))
self.vid_title = (f"{vid.title}"+".mp4")
vid.download(filename='video')
audio.download(filename='audio')
print('Downloaded, Now Starting Merge \n\n\n\n\n')
print(f'{self.vid_title}'+'\n')
def merge(self):
ffmpeg.output(ffmpeg.input('video.mp4'), ffmpeg.input('audio.webm'), self.vid_title).run()
os.remove('video.mp4')
os.remove('audio.webm')
if __name__=='__main__':
a = Downloader(link = sys.argv[1])
a.stream_objects()
a.downloady()
a.merge()
OKE UPDATE:
Now code looks like that..Second problem is slow downloading mp4 files from YouTube server, i have 10Gb/s internet. Good connection with YT servers, but why so poor downloading ? ? ? :)
from pytube import YouTube
import sys
import ffmpeg
import os
import subprocess
class Downloader(YouTube):
def __init__(self, link):
self.link = YouTube(link)
self.hq = []
def stream_objects(self):
self.best = self.link.streams.filter(file_extension='mp4')
q = [self.hq.append(x) for x in self.best.all()]
self.best_vid_itag = str(self.best.all()[1]).split()[1].split('\"')[1]
self.best_audio_itag = str(self.best.all()[-1]).split()[1].split('\"')[1]
def downloader(self):
vid = self.link.streams.get_by_itag(self.best_vid_itag)
aud = self.link.streams.get_by_itag(self.best_audio_itag)
print('Donwloading Video file...\n')
vid.download(filename='video')
print('Video file downloaded... Now Trying download Audio file..\n')
aud.download(filename='audio')
print('Audio file downloaded... Now Trying to merge audio and video files...\n')
def merger(self):
lin = str(self.link.title).rstrip()
lin2 = (lin+'.mp4')
subprocess.run(f'ffmpeg -i video.mp4 -i audio.mp4 -c copy "{lin2}"', shell=True)
os.remove('video.mp4')
os.remove('audio.mp4')
print('Done....\n')
if __name__=='__main__':
a = Downloader(link = sys.argv[1])
a.stream_objects()
a.downloader()
a.merger()
First of all you download a video file and audio file with different encoding
In your case it is mp4 and webm
You should for example download an mp4 video and m4a audio
Or a webm video and a webm audio
Then it comes to ffmpeg, you should pass a parameter “-c copy”
Example for ffmpeg comand line:
ffmpeg -i myvideo.mp4 -i myaudio.m4a -c copy output.mp4
Here is a link to a python project on github use same technique
https://github.com/pyIDM/pyIDM
Check video.py file
Further explanation:
When you use “-c copy” parameter, ffmpeg will just copy the audio track and merge it with video provided that both audio and video has same codec container, this process take less than 2 seconds
Otherwise it will process every frame in video and every bit in audio then convert them to a desired format, which takes very long time
I want to:
Download audio files from Youtube
which I have done with pytube, however, it is formatted in mp4 even though I set only_audio to True.
then turn the audio files to numpy arrays
There are libraries that work on mp3, for example, pydub, but not mp4. When I tried moviepy, it failed because there is no video and therefore no framerate. I don't want to download the video because it will take much longer.
note that I want the audio, not the video.
How can:
download audio from youtube, and turn it into numpy arrays?
Thanks for any helps :)
EDIT
Thanks to the comments, I've managed to turn the mp4 into mp3 using ffmpeg
However, when I tried to turn it into numpy arrays using the code from this question, which looks like this:
def read(f, normalized=False):
"""MP3 to numpy array"""
a = pydub.AudioSegment.from_mp3(f)
y = np.array(a.get_array_of_samples())
if a.channels == 2:
y = y.reshape((-1, 2))
if normalized:
return a.frame_rate, np.float32(y) / 2**15
else:
return a.frame_rate, y
it raised this error:
Traceback (most recent call last):
File "C:\Users\myname\Google Drive\Python\Projects\Music\Downloads\Music Read.py", line 63, in <module>
print(read(x,True))
......
File "C:\Users\myname\AppData\Local\Programs\Python\Python36\lib\subprocess.py", line 1017, in _execute_child
startupinfo)
FileNotFoundError: [WinError 2] The system cannot find the file specified
This is weird because as demonstrated below, the path should work perfectly
for f in os.listdir(path):
if (f.endswith(".mp3")):
print(f)
x = 'C:/Users/myname/Google Drive/Python/Projects/Music/Downloads/{}'.format(f)
print(os.path.exists(x))
print(open(x))
print(read(x,True))
outputs:
test-Copy.mp3
True
c:/users/myname/google drive/python/projects/music/downloads/test-copy.mp3
<_io.TextIOWrapper name='c:/users/myname/google drive/python/projects/music/downloads/test-copy.mp3' mode='r' encoding='cp1252'>
Also, when I input a file path that actually doesn't exist, it outputs a different error:
......
File "C:\Users\myname\AppData\Local\Programs\Python\Python36\lib\site-packages\pydub\utils.py", line 57, in _fd_or_path_or_tempfile
fd = open(fd, mode=mode)
FileNotFoundError: [Errno 2] No such file or directory: 'c:/users/myname/google drive/python/projects/music/downloads/hi'
How can use the code from this question to turn the mp3 into numpy arrays, if I can't, how else?
btw I'm running on Win10 with python 3.6
I really hope I have made myself clear enough, and again thanks in advance for any bits of advice :)
This is weird answering my own question but:
I got around the pydub issue by using this code:
def decode (fname):
# If you are on Windows use full path to ffmpeg.exe
cmd = ["C:/Users/allen/Google Drive/Python/Tools/ffmpeg-20190604-d3f236b-win64-static/bin/ffmpeg.exe", "-i", fname, "-f", "wav", "-"]
# If you are on W add argument creationflags=0x8000000 to prevent another console window jumping out
p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
data = p.communicate()[0]
return np.fromstring(data[data.find(data)+4:], np.int16)
I have a video file named 'video.mp4'. I am trying to seperate a section of audio from the video and save it as a wav file that can be used with other Python modules. I want to do this with MoviePy.
I send parameters to the write_audiofile function, specifying the filename, fps, nbyte, and codec.
Following the MoviePy AudioClip docs, I specified the codec as ‘pcm_s32le’ for a 32-bit wav file.
from moviepy.editor import *
sound = AudioFileClip("video.mp4")
newsound = sound.subclip("00:00:13","00:00:15") #audio from 13 to 15 seconds
newsound.write_audiofile("sound.wav", 44100, 2, 2000,"pcm_s32le")
This code generates a .wav file, named 'sound.wav'.
Opening the audio file in Audacity
The resulting file, sound.wav, can be opened in Audacity, however I run into problems when I try to use it as a wav file with other Python modules.
Playing the sound file in pygame
import pygame
pygame.mixer.init()
sound=pygame.mixer.Sound("sound.wav")
The third line gives the following error:
pygame.error: Unable to open file 'sound.wav'
Determining type of sound file using sndhdr.what()
import sndhdr
sndhdr.what("sound.wav")
The sndhdr method returned none
. According to the docs, when this happens, the method failed to determine the type of sound data stored in the file.
Reading the file with Google Speech Recognition
import speech_recognition as sr
r = sr.Recognizer()
audio = "sound.wav"
with sr.AudioFile(audio) as source:
audio = r.record(source)
text= r.recognize_google(audio)
print(text)
This code stops execution on the second to last line:
ValueError: Audio file could not be read as PCM WAV, AIFF/AIFF-C, or Native FLAC; check if file is corrupted or in another format
Why does the audio file open in Audacity, if sndhdr.what() can not recognize it as an audio file type?
How can I properly export a MoviePy AudioClip as a wav file?
I had the same issue with no codec specified or with codec = 'pcms32le', the one that worked for me was pcm_s16le.
Note that I am using "fr-FR" language, you should probably adapt to yur needs.
here is the entire code :
# Python code to convert video to audio
import moviepy.editor as mp
import speech_recognition as sr
# Insert Local Video File Path
clip = mp.VideoFileClip("/tmp/data/test.mp4")
# Insert Local Audio File Path
clip.audio.write_audiofile("/tmp/data/test.wav",codec='pcm_s16le')
# initialize the recognizer
r = sr.Recognizer()
# open the file
with sr.AudioFile("/tmp/data/test.wav") as source:
# listen for the data (load audio to memory)
audio_data = r.record(source)
# recognize (convert from speech to text)
text = r.recognize_google(audio_data, language = "fr-FR")
print(text)
I had the same issue. I was trying to get a mp4 file from URL, then convert It into wav file and call Google Speech Recognition over It. Instead I used pydub to handle conversion and it worked! Here's a sample of the code:
import requests
import io
import speech_recognition as sr
from pydub import AudioSegment
# This function translate speech to text
def speech_to_text(file):
recognizer = sr.Recognizer()
audio = sr.AudioFile(file)
with audio as source:
speech = recognizer.record(source)
try:
# Call recognizer with audio and language
text = recognizer.recognize_google(speech, language='pt-BR')
print("Você disse: " + text)
return text
# If recognizer don't understand
except:
print("Não entendi")
def mp4_to_wav(file):
audio = AudioSegment.from_file(file, format="mp4")
audio.export("audio.wav", format="wav")
return audio
def mp4_to_wav_mem(file):
audio = AudioSegment.from_file_using_temporary_files(file, 'mp4')
file = io.BytesIO()
file = audio.export(file, format="wav")
file.seek(0)
return file
url = ''
r = requests.get(url, stream=True)
file = io.BytesIO(r.content)
file = mp4_to_wav_mem(file)
speech_to_text(file)
Note that I wrote two functions: mp4_to_wav and mp4_to_wav_mem. The only difference is mp4_to_wav_mem handle all files in memory and mp4_to_wav generates .wav file.
I read the docs of MoviePy and found that the parameter nbyte should be consistent with codec. nbyte is for the Sample width (set to 2 for 16-bit sound, 4 for 32-bit sound). Hence, it better set nbyte=4, when you set codec=pcm_s32le.
i think this is the right method:
import os
from moviepy.editor import AudioFileClip
PATH= "files/"
fileName = "nameOfYourFile.mp4"
newFileName = "nameOfTheNewFile"
Ext = "wav"
AudioFileClip(os.path.join(PATH, f"{fileName}")).write_audiofile(os.path.join(PATH, f"{newFileName}.{Ext}"))
I think this approach is very easy to understand.
from moviepy.editor import *
input_file = "../Database/myvoice.mp4"
output_file = "../Database/myvoice.wav"
sound = AudioFileClip(input_file)
sound.write_audiofile(output_file, 44100, 2, 2000,"pcm_s32le")