def test_fix_preroll_buffering_with_trim(self): """ We can fix buffering occurred from preroll by using trim filter. """ ff = self.ffmpeg ff < self.preroll ff < self.source output = outputs.output_file('original.mp4', codecs.VideoCodec("libx264")) original = outputs.output_file('original.mp4', codecs.VideoCodec("libx264")) preroll_stream = self.preroll.streams[0] source_stream = self.source.streams[0] concat = preroll_stream | filters.Concat(VIDEO) source_stream | concat split = concat | filters.Split(VIDEO) split > output pd = preroll_stream.meta.duration sd = source_stream.meta.duration trim = split | filters.Trim(VIDEO, start=pd, end=pd + sd) trim | filters.SetPTS(VIDEO) > original ff > original ff > output ff.check_buffering()
def test_handle_codec_copy_with_other_filters(self): """ vcodec=copy with separate transcoded output.""" ff = self.ffmpeg ff < self.source cv0 = codecs.VideoCodec('copy') ca0 = codecs.AudioCodec('copy') ff > outputs.output_file('/tmp/copy.flv', cv0, ca0) cv1 = codecs.VideoCodec('libx264') ca1 = codecs.AudioCodec('aac') self.source | filters.Scale(640, 360) > cv1 self.source > ca1 ff > outputs.output_file('/tmp/out.flv', cv1, ca1) self.assert_ffmpeg_args( '-i', 'source.mp4', '-filter_complex', '[0:v]scale=w=640:h=360[vout0]', '-map', '0:v', '-c:v', 'copy', '-map', '0:a', '-c:a', 'copy', '/tmp/copy.flv', '-map', '[vout0]', '-c:v', 'libx264', '-map', '0:a', '-c:a', 'aac', '/tmp/out.flv')
def test_shortcut_outputs_with_codec(self): """ Check ff > output shortcut if codecs list specified.""" ff = FFMPEG(input=inputs.input_file("input.mp4")) scaled = ff.video | filters.Scale(width=1280, height=720) with self.assertRaises(RuntimeError): codec = codecs.VideoCodec("libx264") out = ff > outputs.output_file("output.mp4", codec) # at this moment codec is connected to ffmpeg input stream directly # so scaled video stream could not be connected to output scaled > out codec = codecs.VideoCodec("libx264") out = scaled > outputs.output_file("output.mp4", codec) ff > out
def setUp(self) -> None: super().setUp() self.video_metadata = video_meta_data(width=1920, height=1080, dar=1.777777778, par=1.0, duration=300.0, frame_rate=10.0, frame_count=3000) self.source_audio_duration = 200.0 self.source_sampling_rate = 48000 self.source_samples_count = (self.source_audio_duration * self.source_sampling_rate) self.source_audio_bitrate = 128000 self.audio_metadata = audio_meta_data( duration=self.source_audio_duration, sampling_rate=self.source_sampling_rate, samples_count=self.source_samples_count, bit_rate=self.source_audio_bitrate, ) self.target_audio_bitrate = 64000 self.source = inputs.Input( input_file='input.mp4', streams=(inputs.Stream(VIDEO, meta=self.video_metadata), inputs.Stream(AUDIO, meta=self.audio_metadata))) self.output = outputs.output_file( 'output.mp4', codecs.VideoCodec('libx264'), FdkAAC(bitrate=self.target_audio_bitrate)) self.input_list = inputs.InputList((self.source, )) self.output_list = outputs.OutputList((self.output, )) self.fc = FilterComplex(self.input_list, self.output_list)
def test_reuse_input_files(self): """ Reuse input files multiple times.""" ff = self.ffmpeg ff < self.source v = self.source.streams[0] a = self.source.streams[1] ff > self.output cv1 = codecs.VideoCodec('copy') ca1 = codecs.AudioCodec('copy') out1 = outputs.output_file('/tmp/out1.flv', cv1, ca1) v > cv1 a > ca1 ff > out1 self.assert_ffmpeg_args( '-i', 'source.mp4', '-map', '0:v', '-c:v', 'libx264', '-b:v', '3600000', '-map', '0:a', '-c:a', 'aac', '-b:a', '192000', 'output.mp4', '-map', '0:v', '-c:v', 'copy', '-map', '0:a', '-c:a', 'copy', '/tmp/out1.flv', )
def test_no_audio_if_no_codecs_found(self): """ If no audio codecs specified, set -an flag for an output.""" ff = self.ffmpeg ff < self.source output = outputs.output_file('out.mp4', codecs.VideoCodec('libx264')) ff.video | filters.Scale(640, 360) > output ff > output self.assert_ffmpeg_args('-i', 'source.mp4', '-filter_complex', '[0:v]scale=w=640:h=360[vout0]', '-map', '[vout0]', '-c:v', 'libx264', '-an', 'out.mp4')
def test_tee_muxer(self): """ tee muxer args.""" ff = FFMPEG('/tmp/input.mp4') cv0 = codecs.VideoCodec('libx264') ca0 = codecs.AudioCodec('aac') out0 = HLSMuxer('http://ya.ru/1.m3u8', segment_size=2) out1 = HLSMuxer('http://ya.ru/2.m3u8', manifest_size=5) ff.add_output(TeeMuxer(out0, out1), cv0, ca0) expected = [ 'ffmpeg', '-i', '/tmp/input.mp4', '-map', '0:v', '-c:v', 'libx264', '-map', '0:a', '-c:a', 'aac', '-f', 'tee', '[f=hls:hls_time=2]http://ya.ru/1.m3u8|' '[f=hls:hls_list_size=5]http://ya.ru/2.m3u8' ] self.assertEqual(ff.get_args(), ensure_binary(expected))
def test_handle_codec_copy(self): """ vcodec=copy connects source directly to muxer.""" ff = self.ffmpeg ff < self.source cv0 = codecs.VideoCodec('copy') ca0 = codecs.AudioCodec('aac', bitrate=128000) ff.audio | Volume(20) > ca0 ff > outputs.output_file('/tmp/out.flv', cv0, ca0) self.assert_ffmpeg_args( '-i', 'source.mp4', '-filter_complex', '[0:a]volume=20.00[aout0]', '-map', '0:v', '-c:v', 'copy', '-map', '[aout0]', '-c:a', 'aac', '-b:a', '128000', '/tmp/out.flv' )
def setUp(self) -> None: super().setUp() self.video_metadata = video_meta_data(width=1920, height=1080, dar=1.777777778, par=1.0, duration=300.0, frame_rate=10.0, frame_count=3000) self.audio_metadata = audio_meta_data(duration=200.0, sampling_rate=48000, samples_count=200 * 48000) self.source = inputs.Input( input_file='input.mp4', streams=(inputs.Stream(VIDEO, meta=self.video_metadata), inputs.Stream(AUDIO, meta=self.audio_metadata))) self.output = outputs.output_file('output.mp4', codecs.VideoCodec('libx264'), codecs.AudioCodec('libfdk_aac')) self.input_list = inputs.InputList((self.source, )) self.output_list = outputs.OutputList((self.output, )) self.fc = FilterComplex(self.input_list, self.output_list)
def test_detect_trim_buffering(self): """ When trim and concat filters are used for editing timeline, buffering may occur if order of scenes in output file does not match order of same scenes in input file. """ cases = [ (False, [1.0, 2.0], [2.0, 3.0]), (True, [2.0, 3.0], [1.0, 2.0]), (True, [2.0, 3.0], [2.0, 4.0]), ] for case in cases: with self.subTest(case): raises, first, second = case ff = FFMPEG() s1 = inputs.Stream(VIDEO, self.source.streams[0].meta) s2 = inputs.Stream(VIDEO, self.source.streams[1].meta) ff < inputs.input_file('input.mp4', s1, s2) split = ff.video | filters.Split(VIDEO) t1 = split | filters.Trim(VIDEO, *first) p1 = t1 | filters.SetPTS(VIDEO) t2 = split | filters.Trim(VIDEO, *second) p2 = t2 | filters.SetPTS(VIDEO) concat = p1 | filters.Concat(VIDEO) output = outputs.output_file('output.mp4', codecs.VideoCodec('libx264')) p2 | concat > output ff > output try: ff.check_buffering() except BufferError as e: self.assertTrue(raises, e) else: self.assertFalse(raises)
def test_fix_trim_buffering(self): """ Trim buffering could be fixed with multiple source file deconding. """ ff = FFMPEG() v1 = inputs.Stream(VIDEO, self.source.streams[0].meta) a1 = inputs.Stream(AUDIO, self.source.streams[1].meta) v2 = inputs.Stream(VIDEO, self.source.streams[0].meta) a2 = inputs.Stream(AUDIO, self.source.streams[1].meta) in1 = ff < inputs.input_file('input.mp4', v1, a1) in2 = ff < inputs.input_file('input.mp4', v2, a2) p1 = in1.video | filters.Trim(VIDEO, 2.0, 3.0) | filters.SetPTS(VIDEO) p2 = in2.video | filters.Trim(VIDEO, 1.0, 2.0) | filters.SetPTS(VIDEO) output = outputs.output_file('output.mp4', codecs.VideoCodec('libx264')) concat = p1 | filters.Concat(VIDEO) p2 | concat > output ff > output ff.check_buffering()