Skip to content

Commit

Permalink
Merge pull request #2346 from zalgo3/use-audio-codec-for-existing-aud…
Browse files Browse the repository at this point in the history
…iofile

Use audio codec for existing audiofile
  • Loading branch information
OsaAjani authored Jan 26, 2025
2 parents 5810a64 + 13c56ac commit 8d271ea
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 6 deletions.
4 changes: 4 additions & 0 deletions moviepy/video/VideoClip.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,9 @@ def write_videofile(
write_logfile=write_logfile,
logger=logger,
)
# The audio is already encoded,
# so there is no need to encode it during video export
audio_codec = "copy"

ffmpeg_write_video(
self,
Expand All @@ -396,6 +399,7 @@ def write_videofile(
preset=preset,
write_logfile=write_logfile,
audiofile=audiofile,
audio_codec=audio_codec,
threads=threads,
ffmpeg_params=ffmpeg_params,
logger=logger,
Expand Down
18 changes: 14 additions & 4 deletions moviepy/video/io/ffmpeg_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class FFMPEG_VideoWriter:
audiofile : str, optional
The name of an audio file that will be incorporated to the video.
audio_codec : str, optional
FFMPEG audio codec. If None, ``"copy"`` codec is used.
preset : str, optional
Sets the time that FFMPEG will take to compress the video. The slower,
the better the compression rate. Possibilities are: ``"ultrafast"``,
Expand Down Expand Up @@ -81,6 +84,7 @@ def __init__(
fps,
codec="libx264",
audiofile=None,
audio_codec=None,
preset="medium",
bitrate=None,
with_mask=False,
Expand All @@ -94,6 +98,7 @@ def __init__(
self.logfile = logfile
self.filename = filename
self.codec = codec
self.audio_codec = audio_codec
self.ext = self.filename.split(".")[-1]

pixel_format = "rgba" if with_mask else "rgb24"
Expand All @@ -119,7 +124,9 @@ def __init__(
"-",
]
if audiofile is not None:
cmd.extend(["-i", audiofile, "-acodec", "copy"])
if audio_codec is None:
audio_codec = "copy"
cmd.extend(["-i", audiofile, "-acodec", audio_codec])

if codec == "h264_nvenc":
cmd.extend(["-c:v", codec])
Expand Down Expand Up @@ -175,13 +182,14 @@ def write_frame(self, img_array):
f"writing file {self.filename}:\n\n {ffmpeg_error}"
)

if "Unknown encoder" in ffmpeg_error:
if "Unknown encoder" in ffmpeg_error or "Unknown decoder" in ffmpeg_error:
error += (
"\n\nThe video export failed because FFMPEG didn't find the "
f"specified codec for video encoding {self.codec}. "
"specified codec for video or audio. "
"Please install this codec or change the codec when calling "
"write_videofile.\nFor instance:\n"
" >>> clip.write_videofile('myvid.webm', codec='libvpx')"
" >>> clip.write_videofile('myvid.webm', audio='myaudio.mp3', "
"codec='libvpx', audio_codec='aac')"
)

elif "incorrect codec parameters ?" in ffmpeg_error:
Expand Down Expand Up @@ -240,6 +248,7 @@ def ffmpeg_write_video(
preset="medium",
write_logfile=False,
audiofile=None,
audio_codec=None,
threads=None,
ffmpeg_params=None,
logger="bar",
Expand Down Expand Up @@ -269,6 +278,7 @@ def ffmpeg_write_video(
with_mask=has_mask,
logfile=logfile,
audiofile=audiofile,
audio_codec=audio_codec,
threads=threads,
ffmpeg_params=ffmpeg_params,
pixel_format=pixel_format,
Expand Down
22 changes: 20 additions & 2 deletions tests/test_VideoClip.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def test_write_frame_errors(util, video):
clip.write_videofile(location, codec="nonexistent-codec")
assert (
"The video export failed because FFMPEG didn't find the specified"
" codec for video encoding nonexistent-codec" in str(e.value)
" codec for video or audio" in str(e.value)
), e.value

autogenerated_location = "unlogged-writeTEMP_MPY_wvf_snd.mp3"
Expand All @@ -84,7 +84,7 @@ def test_write_frame_errors_with_redirected_logs(util, video):
clip.write_videofile(location, codec="nonexistent-codec", write_logfile=True)
assert (
"The video export failed because FFMPEG didn't find the specified"
" codec for video encoding nonexistent-codec" in str(e.value)
" codec for video or audio" in str(e.value)
)

autogenerated_location_mp3 = "logged-writeTEMP_MPY_wvf_snd.mp3"
Expand All @@ -106,6 +106,24 @@ def test_write_videofiles_with_temp_audiofile_path(util):
assert any(file.startswith("temp_audiofile_path") for file in contents_of_temp_dir)


def test_write_videofiles_audio_codec_error(util, video):
"""Checks error cases return helpful messages."""
clip = video()
location = os.path.join(util.TMP_DIR, "unlogged-write.mp4")
with pytest.raises(IOError) as e:
clip.write_videofile(
location, audio="media/crunching.mp3", audio_codec="nonexistent-codec"
)
assert (
"The video export failed because FFMPEG didn't find the specified"
" codec for video or audio" in str(e.value)
), e.value

autogenerated_location = "unlogged-writeTEMP_MPY_wvf_snd.mp3"
if os.path.exists(autogenerated_location):
os.remove(autogenerated_location)


@pytest.mark.parametrize("mask_color", (0, 0.5, 0.8, 1))
@pytest.mark.parametrize(
"with_mask",
Expand Down

0 comments on commit 8d271ea

Please sign in to comment.