@@ -57,6 +57,21 @@ def to_av_frame_rate(fps):
5757 return av .utils .Fraction (num , denom )
5858
5959
60+ def convert_audio (input_path : Path , output_path : Path , codec_name : str ):
61+ with (
62+ av .open (input_path ) as input_audio ,
63+ av .open (output_path , "w" ) as output_audio ,
64+ ):
65+ input_audio_stream = input_audio .streams .audio [0 ]
66+ output_audio_stream = output_audio .add_stream (codec_name )
67+ for frame in input_audio .decode (input_audio_stream ):
68+ for packet in output_audio_stream .encode (frame ):
69+ output_audio .mux (packet )
70+
71+ for packet in output_audio_stream .encode ():
72+ output_audio .mux (packet )
73+
74+
6075class SceneFileWriter :
6176 """
6277 SceneFileWriter is the object that actually writes the animations
@@ -350,19 +365,7 @@ def add_sound(
350365 # we need to pass delete=False to work on Windows
351366 # TODO: figure out a way to cache the wav file generated (benchmark needed)
352367 wav_file_path = NamedTemporaryFile (suffix = ".wav" , delete = False )
353- with (
354- av .open (file_path ) as input_container ,
355- av .open (wav_file_path , "w" , format = "wav" ) as output_container ,
356- ):
357- for audio_stream in input_container .streams .audio :
358- output_stream = output_container .add_stream ("pcm_s16le" )
359- for frame in input_container .decode (audio_stream ):
360- for packet in output_stream .encode (frame ):
361- output_container .mux (packet )
362-
363- for packet in output_stream .encode ():
364- output_container .mux (packet )
365-
368+ convert_audio (file_path , wav_file_path , "pcm_s16le" )
366369 new_segment = AudioSegment .from_file (wav_file_path .name )
367370 logger .info (f"Automatically converted { file_path } to .wav" )
368371 wav_file_path .close ()
@@ -748,21 +751,17 @@ def combine_to_movie(self):
748751 # but tries to call ffmpeg via its CLI -- which we want
749752 # to avoid. This is why we need to do the conversion
750753 # manually.
751- if config .format == "webm" :
752- with (
753- av .open (sound_file_path ) as wav_audio ,
754- av .open (sound_file_path .with_suffix (".ogg" ), "w" ) as opus_audio ,
755- ):
756- wav_audio_stream = wav_audio .streams .audio [0 ]
757- opus_audio_stream = opus_audio .add_stream ("libvorbis" )
758- for frame in wav_audio .decode (wav_audio_stream ):
759- for packet in opus_audio_stream .encode (frame ):
760- opus_audio .mux (packet )
761-
762- for packet in opus_audio_stream .encode ():
763- opus_audio .mux (packet )
764-
765- sound_file_path = sound_file_path .with_suffix (".ogg" )
754+ out_suffix = movie_file_path .suffix .lower ()
755+ if config .format == "webm" or out_suffix == ".webm" :
756+ ogg_sound_file_path = sound_file_path .with_suffix (".ogg" )
757+ convert_audio (sound_file_path , ogg_sound_file_path , "libvorbis" )
758+ sound_file_path = ogg_sound_file_path
759+ elif config .format == "mp4" or out_suffix == ".mp4" :
760+ # Similarly, pyav may reject wav audio in an .mp4 file;
761+ # convert to AAC.
762+ aac_sound_file_path = sound_file_path .with_suffix (".aac" )
763+ convert_audio (sound_file_path , aac_sound_file_path , "aac" )
764+ sound_file_path = aac_sound_file_path
766765
767766 temp_file_path = movie_file_path .with_name (
768767 f"{ movie_file_path .stem } _temp{ movie_file_path .suffix } "
0 commit comments