FFmpeg将.mp3输出保存到变量中 [英] FFmpeg save .mp3 output into a variable

查看:64
本文介绍了FFmpeg将.mp3输出保存到变量中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用程序中,我想修改各种mp3,然后将它们混合在一起.我知道我可以使用FFmpeg中的单个命令行来完成此操作,但是由于我需要在每个样本上使用各种过滤器,并且其中有五个,因此最终可能会很混乱.我的想法是分别编辑每个样本,将它们保存到变量中,最后将它们混合.这是我的代码:

In my application I want to modify various mp3 and then mix them together. I know I could do it with a single command line in FFmpeg but It can end up very messy since I need to use various filter on each sample and I have five of them. My idea is to edit each sample individually, save them into a variable and finally mix them. This is my code:

import subprocess    

def create_samp():
    sample= subprocess.run(["ffmpeg", "-y", "-i", "https://freesound.org/data/previews/186/186942_2594536-hq.mp3", \
                           "-filter_complex", "adelay=15000|15000", "-codec:v", "copy", "-f", "mp3","-"], stdout=subprocess.PIPE)         
    return(sample)    

def record(samp):
    subprocess.run(["ffmpeg", "-y", "-i", "https://cdns-preview-b.dzcdn.net/stream/c-b0b684fe962f93dc43f1f7ea493683a1-3.mp3", \
                    "-i", samp.stdout, "-f", "-mp3", "copy", "output.mp3"])

samp = create_samp()
record(samp)

我的问题是我必须对 stdout 进行编码.我已经尝试过'utf-8',但收到此错误:

My issue is that I have to encode the stdout. I've tried 'utf-8' but got this error:

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 45: invalid start byte

带有''utf-16':

With `'utf-16':

UnicodeDecodeError: 'utf-16-le' codec can't decode bytes in position 239454-239455: illegal encoding

为什么要解决此问题?我的方法正确吗?

Why is the way to fix this issue? Is my approach the right one?

由于@Rotem,我得以成功完成自己想做的事情.但是现在我面临另一个问题,因为我想混合多达5种声音,所以我尝试以一种懒惰/简单的方式实现它:

Thanks to @Rotem I succeed to do what I wanted to. But now I am facing an other issue, since I want to mix up to 5 sounds, I tried to implement it the lazy/easy way:

import subprocess

def create_samp_2():
    sample= subprocess.run(["ffmpeg", "-i", "https://freesound.org/data/previews/186/186942_2594536-hq.mp3", \
                            "-af", "adelay=15000|15000", "-f", "mp3", "pipe:"], stdout=subprocess.PIPE).stdout
    return(sample)

def create_samp():

    sample= subprocess.run(["ffmpeg", "-i", "https://freesound.org/data/previews/370/370934_6399962-lq.ogg", \
                            "-af", "adelay=1000|1000", "-f", "mp3", "pipe:"], stdout=subprocess.PIPE).stdout
    return(sample)


def record(samp, samp_2):        
    process = subprocess.Popen(["ffmpeg", "-y", '-f', 'mp3', \
                                "-i", "https://cdns-preview-b.dzcdn.net/stream/c-b0b684fe962f93dc43f1f7ea493683a1-3.mp3", \
                                "-i", "pipe:", \
                                "-i", "pipe:", \
                                "-filter_complex", "amix=inputs=3:duration=longest", "output.mp3"], stdin=subprocess.PIPE)

    process.stdin.write(samp) 
    process.stdin.write(samp_2)        
    process.stdin.close()  
    process.wait()

samp = create_samp()
samp_2 = create_samp_2()
record(samp, samp_2)

令人惊讶的是,我的两个声音在正确的时间开始播放,但是第二个声音混乱了.因此,这不是正确的方法.

Surprisingly it works, my two sounds start at the right time, but the second sound is messed up. So it's not the right way to do it.

然后我尝试按照这种方式建议命名管道:

Then I tried named pipes as suggested this way:

"pipe1:"

但是我得到这个错误:

pipe1:: Protocol not found
Did you mean file:pipe1:?

阅读命名管道wiki时,我必须使用 mkfifo()创建它们.

Reading named pipe wiki it is stated that I have to create them with mkfifo().

所以我尝试了:

import os
pipe1 = "pipe1"

def create_pipe1():
    os.mkfifo(pipe1)

But now I have this error: pipe1:: Protocol not found
Did you mean file:pipe1:?

推荐答案

您的方法正确,但需要修复.

Your approach the right, but fixes are needed.

修复 create_samp():

  • 您不需要"-codec:v","copy" 参数,因为没有视频流.
  • You don't need "-codec:v", "copy" arguments because there is no video stream.

修复记录(样本):

  • 您不能使用-i" "samp.stdout" ,因为 samp.stdout 是字节数组(Python子流程模块将其用作字符串).
  • 使用-i","pipe:" 从stdin管道接收第二个音频.
  • 由于要混合两个音频流,因此必须使用-filter_complex" 参数和 amix 音频过滤器或 amerge 音频过滤器,如此处所述.
  • samp 写入stdin管道,然后关闭stdin管道.
  • You can't use "-i" "samp.stdout", because samp.stdout is a bytes array (Python subprocess module uses it as a string).
  • Use "-i", "pipe:" for receiving the the second audio from stdin pipe.
  • Since you want to mix two audio streams you have to use "-filter_complex" argument, and amix audio filter or amerge audio filter, as described here.
  • Write samp to stdin pipe, and close stdin pipe.

这是代码:

import subprocess

def create_samp():
    # Read audio stream from https://freesound.org/data/previews/186/186942_2594536-hq.mp3
    # Apply adelay audio filter.
    # Encode the audio in mp3 format.
    # FFmpeg output is passed to stdout pipe, and stored in sample bytes array.
    sample= subprocess.run(["ffmpeg", "-i", "https://freesound.org/data/previews/186/186942_2594536-hq.mp3", \
                            "-af", "adelay=15000|15000", "-f", "mp3", "pipe:"], stdout=subprocess.PIPE).stdout
    return(sample)


def record(samp):
    # Open FFmpeg as sub-process
    # Use two audio input streams:
    # 1. WEB address
    # 2. PIPE (the input is going to be written stdin pipe).
    # Merge the two audio streams using amix audio filter.
    # Store the result to output file: output.mp3
    process = subprocess.Popen(["ffmpeg", "-y", '-f', 'mp3', \
                                "-i", "https://cdns-preview-b.dzcdn.net/stream/c-b0b684fe962f93dc43f1f7ea493683a1-3.mp3", \
                                "-i", "pipe:", \
                                "-filter_complex", "amix=inputs=2:duration=longest", "output.mp3"], stdin=subprocess.PIPE)

    process.stdin.write(samp)  # Write samp (bytes array containing mp3 data).
    process.stdin.close()  # Close stdin pipe.
    process.wait()  # Wait for FFmpeg sub-process to finish

samp = create_samp()
record(samp)

听起来不错...

使用命名管道(仅Linux):

当有两个以上的输入流(需要从内存缓冲区中传递)时,需要使用命名管道.
使用命名管道根本不是一件容易的事...

Named pipes are required when there are more than two input streams (that need to be piped from memory buffers).
Using named pipes is not trivial at all...

从FFmpeg的角度来看,命名管道就像(不可搜索的)输入文件.

From FFmpeg point of view named pipes are like (non-seekable) input files.

在Python中使用命名管道(在Linux中):
假设 pipe1 是命名管道"的名称,(例如 pipe1 ="audio_pipe1" ).

Using named pipes in Python (in Linux):
Assume pipe1 is the name of the "named pipe" (e.g pipe1 = "audio_pipe1").

  1. 创建一个命名管道":

  1. Create a "named pipe":

os.mkfifo(pipe1)

  • 以只写"方式打开管道;文件:

  • Open the pipe as "write only" file:

    fd_pipe = os.open(pipe_name, os.O_WRONLY)  # fd_pipe1 is a file descriptor (an integer)
    

  • 将数据分成小块写入管道.
    在大多数Linux系统中,管道的默认缓冲区大小为64KB.
    由于数据大于65536字节,因此我们需要将数据分小块写入管道.
    我决定使用1024字节的任意大小.
    管道写入操作是阻塞"操作.操作.
    我通过使用"writer"解决了这个问题.线程:

    def writer(data, pipe_name, chunk_size):
        # Open the pipes as opening files (open for "open for writing only").
        fd_pipe = os.open(pipe_name, os.O_WRONLY)  # fd_pipe1 is a file descriptor (an integer)
    
        for i in range(0, len(data), chunk_size):
            # Write to named pipe as writing to a file (but write the data in small chunks).
            os.write(fd_pipe, data[i:chunk_size+i])  # Write 1024 bytes of data to fd_pipe
    

  • 关闭管道:

  • Close the pipe:

    os.close(fd_pipe)
    

  • 删除(取消链接)命名管道:

  • Remove (unlink) the named pipe:

    os.unlink(pipe1)
    


  • 这是先前的示例,使用两个命名管道:


    Here is the previous sample, using two named pipes:

    import subprocess
    import os
    from threading import Thread
    
    
    def create_samp():
        # Read audio stream from https://freesound.org/data/previews/186/186942_2594536-hq.mp3
        # Apply adelay audio filter.
        # Encode the audio in mp3 format.
        # FFmpeg output is passed to stdout pipe, and stored in sample bytes array.
        sample1 = subprocess.run(["ffmpeg", "-i", "https://freesound.org/data/previews/186/186942_2594536-hq.mp3",
                                  "-af", "adelay=15000|15000", "-f", "mp3", "pipe:"], stdout=subprocess.PIPE).stdout
    
        # Read second audio sample from https://cdns-preview-b.dzcdn.net/stream/c-b0b684fe962f93dc43f1f7ea493683a1-3.mp3
        sample2 = subprocess.run(["ffmpeg", "-i", "https://cdns-preview-b.dzcdn.net/stream/c-b0b684fe962f93dc43f1f7ea493683a1-3.mp3",
                                  "-f", "mp3", "pipe:"], stdout=subprocess.PIPE).stdout
    
        return sample1, sample2
    
    
    def writer(data, pipe_name, chunk_size):
        # Open the pipes as opening files (open for "open for writing only").
        fd_pipe = os.open(pipe_name, os.O_WRONLY)  # fd_pipe1 is a file descriptor (an integer)
    
        for i in range(0, len(data), chunk_size):
            # Write to named pipe as writing to a file (but write the data in small chunks).
            os.write(fd_pipe, data[i:chunk_size+i])  # Write 1024 bytes of data to fd_pipe
    
        # Closing the pipes as closing files.
        os.close(fd_pipe)
    
    
    def record(samp1, samp2):
        # Names of the "Named pipes"
        pipe1 = "audio_pipe1"
        pipe2 = "audio_pipe2"
    
        # Create "named pipes".
        os.mkfifo(pipe1)
        os.mkfifo(pipe2)
    
        # Open FFmpeg as sub-process
        # Use two audio input streams:
        # 1. Named pipe: "audio_pipe1"
        # 2. Named pipe: "audio_pipe2"
        # Merge the two audio streams using amix audio filter.
        # Store the result to output file: output.mp3
        process = subprocess.Popen(["ffmpeg", "-y", '-f', 'mp3',
                                    "-i", pipe1,
                                    "-i", pipe2,
                                    "-filter_complex", "amix=inputs=2:duration=longest", "output.mp3"],
                                    stdin=subprocess.PIPE)
    
        # Initialize two "writer" threads (each writer writes data to named pipe in chunks of 1024 bytes).
        thread1 = Thread(target=writer, args=(samp1, pipe1, 1024))  # thread1 writes samp1 to pipe1
        thread2 = Thread(target=writer, args=(samp2, pipe2, 1024))  # thread2 writes samp2 to pipe2
    
        # Start the two threads
        thread1.start()
        thread2.start()
    
        # Wait for the two writer threads to finish
        thread1.join()
        thread2.join()
    
        process.wait()  # Wait for FFmpeg sub-process to finish
    
        # Remove the "named pipes".
        os.unlink(pipe1)
        os.unlink(pipe2)
    
    
    sampl1, sampl2 = create_samp()
    record(sampl1, sampl2)
    


    该代码已在Ubuntu 18.04(在虚拟机中)中进行了测试.


    The code was tested in Ubuntu 18.04 (in a virtual machine).

    这篇关于FFmpeg将.mp3输出保存到变量中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆