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)
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
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?
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:"
But I get this error:
pipe1:: Protocol not found
Did you mean file:pipe1:?
Reading named pipe wiki it is stated that I have to create them with mkfifo()
.
So I tried:
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.
Fixing create_samp()
:
"-codec:v", "copy"
arguments because there is no video stream. Fixing record(samp)
:
"-i"
"samp.stdout"
, because samp.stdout
is a bytes array (Python subprocess module uses it as a string)."-i", "pipe:"
for receiving the the second audio from stdin pipe."-filter_complex"
argument, and amix
audio filter or amerge
audio filter, as described here .samp
to stdin pipe, and close stdin pipe.Here is the code:
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)
Sounds great...
Named pipes implementation moved to the following post.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.