Set the framerate of converted output file using ffmpeg-python - python

I have the following line to convert a .mp4 file to a .gif file using ffmpeg-python:
ffmpeg.input('test.mp4').trim(start=0, duration=3).output('output.gif').run()
It works well, but I wanted to reduce the frame rate. At this link, I could not find a way to do this. Does somebody know how to do it ?

ffmpeg
.input('test.mp4')
.trim(start=0, duration=3)
.filter('fps', fps=25, round='up')
.output('output.mp4')
.run()

Related

Using MoviePy to fix unfinalized .flv video

While recording my screen with OBS capture, I accumulated large quantity of videos that had been subject to a forced system shutdown, leaving them unfinalized. The videos were created using an .flv format, so when I play them in VLC Player they play flawlessly, however they are missing an end time (video length). Instead, the videos show the running time as they play, but maintain the 00:00 end time, despite the actual video playing for several minutes.
From my understanding, unlike .mp4 formatting, .flv formatted video should be able to be recovered if it has not been finalized (as in the case of my footage stopped by unexpected shutdowns). Since I have a large quantity of unfinalized, I need an automated solution to fix them.
Using MoviePy write_videofile
I attempted to fix the videos by using the MoviePy write_videofile command in the python shell with the directory set to the directory of the bad video:
from moviepy.editor import * #no error
vid = VideoFileClip("oldVideoName.flv") #no error
vid.write_videofile("corrected.mp4") #IndexError
The final line created breifly created a file "correctedTEMP_MPY_wvf_snd.mp3"(only 1KB, unplayable in Audacity), shorty before throwing an exception. I recieved a massive traceback with the final teir reading:
File "\Python37-32\lib\site-packages\moviepy\audio\io\readers.py", line 168, in get_frame
"Accessing time t=%.02f-%.02f seconds, "%(tt[0], tt[-1])+
IndexError: index 0 is out of bounds for axis 0 with size 0
I assumed that this was caused by a problem with an audio reader not accepting the supposed 00:00 timestamp as the length of the video.
Using MoviePy subclip
I attempted to see if there was a way that I could manually feed MoviePy the start and end timestamps, using the subclip method. I know the video is at least 4 seconds long, so I used that as a control test:
clip = vid.subclip("00:00:00", "00:00:05") #no error
clip.write_videofile("corrected.mp4") #OSError
The write_videofile method again threw an exception:
File "\Python37-32\lib\site-packages\moviepy\audio\io\readers.py", line 169, in get_frame
"with clip duration=%d seconds, "%self.duration)
OSError: Error in file oldVideoName.flv,
Accessing time t=0.00-0.04 seconds, with clip duration=0 seconds,
Even if this method were to work, I would need to find a way to automate the process of discovering the video end time.
Using OpenCV CAP_PROP_FRAME_COUNT
One possible solution to finding the end time (video length) is to use cv2, per this post.
import cv2 #no error
vid=cv2.VideoCapture("oldVideoName.flv") #no error
vid.get(cv2.CAP_PROP_FRAME_COUNT) #returns -5.534023222112865e+17
I was not expecting to receive a negative float for this value. Further tests reveal to me that this float does not correspond at all with the length of the video, as all unfinalized videos return the same float for this request. (Normal videos do return their length for this method call) This is useful to iterate over a directory identifying unfinalized videos.
Is using MoviePy to correct a large quantity of unfinalized videos a viable or even possible solution? Is it better to use cv2 (Python OpenCV) for solving this problem?
I was able to fix the video files using yamdi, an open source metadata injector for FLV files. After downloading and installing yamdi, I can use the following command to repair an .flv file named oldVideoName.flv:
yamdi -i oldVideoName.flv -o corrected.flv
The command leaves oldVideoName.flv untouched, and saves a repaired file as corrected.flv.

Obtaining scene change frames with corresponding timestamps and scene scored saved in a text file

I have to use FFmpeg to detect shot changes in a video, an also save the timestamps and scores of the detected shot changes? How can i do this with a single command?
EDIT
I jumped to my use case directly, as it was solved directly using FFmpeg, without the need of raw frames.
The best and perfect solution I came across after reading tonnes of Q/A:
Simply use the command:
ffmpeg inputvideo.mp4 -filter_complex "select='gt(scene,0.3)',metadata=print:file=time.txt" -vsync vfr img%03d.png
This will save just the relevant information in the time.txt file like below:
frame:0 pts:108859 pts_time:1.20954
lavfi.scene_score=0.436456
frame:1 pts:285285 pts_time:3.16983
lavfi.scene_score=0.444537
frame:2 pts:487987 pts_time:5.42208
lavfi.scene_score=0.494256
frame:3 pts:904654 pts_time:10.0517
lavfi.scene_score=0.462327
frame:4 pts:2533781 pts_time:28.1531
lavfi.scene_score=0.460413
frame:5 pts:2668916 pts_time:29.6546
lavfi.scene_score=0.432326

Using ffmpeg with Python3 subprocess to convert multiple PNGs to a video

I am using Python3, subprocess and ffmpeg to convert multiple PNG images into a single video.
I have 400 PNG numbered as "00001.png".
This call for one single specific image to a a one-frame-video works:
subprocess.call(["ffmpeg","-y","-r","24","-i", "00300.png","-vcodec","mpeg4", "-qscale","5", "-r", "24", "video.mp4"])
However, when I try some methods seen online for calling all of my images formated as "#####.png" using "%05d.png" as below, it does not work anymore:
subprocess.call(["ffmpeg","-y","-r","24","-i", "%05d.png","-vcodec","mpeg4", "-qscale","5", "-r", "24", "video.mp4"])
I receive the error : "%05d.png: No such file or directory".
I have the feelling that the above syntax is proper to Python2 and not working on my python3 but can't find the correct python3 syntax anywhere.
Thanks in advance for your help
Well, I finally found the answer, and as expected it is disappointingly simple: The numbering has to start with 1 (00001 in my case), while in fact my files ranged from 00002.png to 00301.png.
Worked as soon as I added an initial 00001.png file.

No audio when adding Mp3 to VideoFileClip MoviePy

I'm trying to add an mp3 audio file to a video clip that I'm creating out of images with MoviePy. When the script runs it creates the mp4 file and plays successfully, however there's no audio. I'm not really sure why and can't seem to find a ton of documentation around this in general. MoviePy is pretty new to me so any help would be appreciated - thank-you!
def make_video(images):
image_clips = []
for img in images:
if not os.path.exists(img):
raise FileNotFoundError(img)
ic = ImageClip(img).set_duration(3)
image_clips.append(ic)
video = concatenate(image_clips, method="compose")
video.set_audio(AudioFileClip("audio.mp3"))
video.write_videofile("mp4_with_audio.mp4", fps=60, codec="mpeg4")
This worked for me:
clip.write_videofile(out_path,
codec='libx264',
audio_codec='aac',
temp_audiofile='temp-audio.m4a',
remove_temp=True
)
Found it here: https://github.com/Zulko/moviepy/issues/51
Admittedly, this question is old but comes high in search results for the problem. I had the same issue and think the solution can be clarified.
The line:
video.set_audio(AudioFileClip("audio.mp3"))
actually does not change the audio track of the "video" object, but returns a copy of the object with the new AudioFileClip attached to it.
That means that the method:
video.write_videofile("mp4_with_audio.mp4", fps=60, codec="mpeg4")
does not write the final file with the new audio track, since the "video" object remains unchanged.
Changing the script as per the below solved the issue for me.
video_with_new_audio = video.set_audio(AudioFileClip("audio.mp3"))
video_with_new_audio.write_videofile("mp4_with_audio.mp4", fps=60, codec="mpeg4")
See also the docs
Check the video mp4_with_audio.mp4 with VLC media player, i also have same issue with quick player.
I run into this problem too. I found a solution, try
video = video.set_audio(AudioFileClip("audio.mp3"))
I was doing something similar and found that moviepy 1.0.1 did not call ffmpeg with the right arguments to combine the video and audio for mp4 video. 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. This is a similar question: Output video has no sound
Since you are working with mp3, you may need to have ffmpeg convert to aac, so this code does that.
This link helped me with ffmpeg:https://superuser.com/questions/277642/how-to-merge-audio-and-video-file-in-ffmpeg
video_with_new_audio = video.set_audio(AudioFileClip("audio.mp3"))
video_with_new_audio.write_videofile("temp_moviepy.mp4", temp_audiofile="tempaudio.m4a",codec="libx264",remove_temp=False,audio_codec='aac')
import subprocess as sp
command = ['ffmpeg',
'-y', #approve output file overwite
'-i', "temp_moviepy.mp4",
'-i', "tempaudio.m4a",
'-c:v', 'copy',
'-c:a', 'aac', #to convert mp3 to aac
'-shortest',
"mp4_with_audio.mp4" ]
with open(ffmpeg_log, 'w') as f:
process = sp.Popen(command, stderr=f)
Use this:
video.write_videofile("output.mp4", fps=30, audio_codec="aac", audio_bitrate="192k")

having cv2.imread reading images from file objects or memory-stream-like data (here non-extracted tar)

I have a .tar file containing several hundreds of pictures (.png). I need to process them via opencv.
I am wondering whether - for efficiency reasons - it is possible to process them without passing by the disc. In other, words I want to read the pictures from the memory stream related to the tar file.
Consider for instance
import tarfile
import cv2
tar0 = tarfile.open('mytar.tar')
im = cv2.imread( tar0.extractfile('fname.png').read() )
The last line doesn't work as imread expects a file name rather than a stream.
Consider that this way of reading directly from the tar stream can be achieved e.g. for text (see e.g. this SO question).
Any suggestion to open the stream with the correct png encoding?
Untarring to ramdisk is of course an option, although I was looking for something more cachable.
Thanks to the suggestion of #abarry and this SO answer I managed to find the answer.
Consider the following
def get_np_array_from_tar_object(tar_extractfl):
'''converts a buffer from a tar file in np.array'''
return np.asarray(
bytearray(tar_extractfl.read())
, dtype=np.uint8)
tar0 = tarfile.open('mytar.tar')
im0 = cv2.imdecode(
get_np_array_from_tar_object(tar0.extractfile('fname.png'))
, 0 )
Perhaps use imdecode with a buffer coming out of the tar file? I haven't tried it but seems promising.

Categories

Resources