Python запуск ffmpeg с Popen вызывает утечку памяти - PullRequest
0 голосов
/ 13 января 2020

Я использую ffmpeg в Python3 для запуска команды :

    process = subprocess.Popen(
        command,
        stdout=open(os.devnull, 'wb'),
        stdin=subprocess.PIPE,
        stderr=subprocess.PIPE)

    # Write data to STDIN.
    try:
        process.stdin.write(data.astype('<f4').tostring())
    except IOError:
        raise IOError(f'FFMPEG error: {process.stderr.read()}')

    # Clean process.
    process.stdin.close()
    if process.stderr is not None:
        process.stderr.close()
    process.wait()

, где data представляет волновой файл, следовательно, он добавляется к stdin. Этот код имеет утечку памяти. Память не освобождается, фактически, если мы выполняем трассировку памяти:

    is_tracing = tracemalloc.is_tracing()
    if not is_tracing:
        nframe = 6
        tracemalloc.start(nframe)
    current_mem, peak_mem = tracemalloc.get_traced_memory()
    overhead = tracemalloc.get_tracemalloc_memory()
    summary = "traced memory: %d KiB  peak: %d KiB  overhead: %d KiB" % (
        int(current_mem // 1024), int(peak_mem // 1024), int(overhead // 1024)
    )
    print( "before save", summary )

    process = subprocess.Popen(
        command,
        stdout=open(os.devnull, 'wb'),
        stdin=subprocess.PIPE,
        stderr=subprocess.PIPE)

    try:
        process.stdin.write(data.astype('<f4').tostring())
    except IOError:
        raise IOError(f'FFMPEG error: {process.stderr.read()}')

    process.stdin.close()
    if process.stderr is not None:
        process.stderr.close()
    process.wait()

   current_mem, peak_mem = tracemalloc.get_traced_memory()
    overhead = tracemalloc.get_tracemalloc_memory()
    summary = "traced memory: %d KiB  peak: %d KiB  overhead: %d KiB" % (
        int(current_mem // 1024), int(peak_mem // 1024), int(overhead // 1024)
    )
    print( "after save", summary )

и выполняем ее больше раз, это даст

after save traced memory: 18 KiB  peak: 1419 KiB  overhead: 10 KiB
before save traced memory: 27459 KiB  peak: 28152 KiB  overhead: 28293 KiB
after save traced memory: 27384 KiB  peak: 28872 KiB  overhead: 28267 KiB
before save traced memory: 52707 KiB  peak: 53400 KiB  overhead: 53132 KiB
after save traced memory: 52653 KiB  peak: 54120 KiB  overhead: 53109 KiB

В частности, выполненный ffmpeg command был

     command = (
        self._get_command_builder()
        .flag('-y')
        .opt('-loglevel', 'error')
        .opt('-f', 'f32le')
        .opt('-ar', sample_rate)
        .opt('-ac', data.shape[1])
        .opt('-i', '-')
        .flag('-vn')
        .opt('-acodec', codec)
        .opt('-ar', sample_rate)
        .opt('-strict', '-2')
        .opt('-ab', bitrate)
        .flag(path)
        .command())

Какая переменная утечка памяти? Я пытался очистить data и грипп sh стандартный ввод без какого-либо успеха. Я также пытался запустить в threading.Thread как:

class MyClass(threading.Thread):
    def __init__(self, *args):
        self.stdout = None
        self.stderr = None
        self.args = args
        threading.Thread.__init__(self)

    def run(self):
        command = self.args[ 0 ]
        data = self.args[ 1 ]
        process = subprocess.Popen(
            command,
            stdout=open(os.devnull, 'wb'),
            stdin=subprocess.PIPE,
            stderr=subprocess.PIPE)
        try:
            process.stdin.write(data.astype('<f4').tostring())
        except IOError:
            raise IOError(f'FFMPEG error: {process.stderr.read()}')
        self.stdout, self.stderr = process.communicate()

, а затем

myclass = MyClass(command, data)
myclass.start()
myclass.join()
...