I am a noob in audio processing using python. I have got the following python code from internet to perform live audio recording. Whenever, I record using the following code, there is a block of zeros in the beginning of the recorded audio file. The recording doesn't start immediately and it adds zeros until the recording starts.
I want the recording to happen immediately i run code without any zeros in the beginning. Any help is appreciated.
import argparse
import tempfile
import queue
import sys
def int_or_str(text):
"""Helper function for argument parsing."""
try:
return int(text)
except ValueError:
return text
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
'-l', '--list-devices', action='store_true',
help='show list of audio devices and exit')
parser.add_argument(
'-d', '--device', type=int_or_str,
help='input device (numeric ID or substring)')
parser.add_argument(
'-r', '--samplerate', type=int, help='sampling rate')
parser.add_argument(
'-c', '--channels', type=int, default=1, help='number of input channels')
parser.add_argument(
'filename', nargs='?', metavar='FILENAME',
help='audio file to store recording to')
parser.add_argument(
'-t', '--subtype', type=str, help='sound file subtype (e.g. "PCM_24")')
args = parser.parse_args()
try:
import sounddevice as sd
import soundfile as sf
import numpy # Make sure NumPy is loaded before it is used in the callback
assert numpy # avoid "imported but unused" message (W0611)
if args.list_devices:
print(sd.query_devices())
parser.exit(0)
if args.samplerate is None:
device_info = sd.query_devices(args.device, 'input')
# soundfile expects an int, sounddevice provides a float:
args.samplerate = int(device_info['default_samplerate'])
if args.filename is None:
args.filename = tempfile.mktemp(prefix='delme_rec_unlimited_',
suffix='.wav', dir='')
q = queue.Queue()
def callback(indata, frames, time, status):
"""This is called (from a separate thread) for each audio block."""
if status:
print(status, file=sys.stderr)
q.put(indata.copy())
# Make sure the file is opened before recording anything:
with sf.SoundFile(args.filename, mode='x', samplerate=args.samplerate,
channels=args.channels, subtype=args.subtype) as file:
with sd.InputStream(samplerate=args.samplerate, device=args.device,
channels=args.channels, callback=callback):
print('#' * 80)
print('press Ctrl+C to stop the recording')
print('#' * 80)
while True:
file.write(q.get())
except KeyboardInterrupt:
print('\nRecording finished: ' + repr(args.filename))
parser.exit(0)
except Exception as e:
parser.exit(type(e).__name__ + ': ' + str(e))
Related
I wrote this code using the realSense SDK and as well as the sounddevice libary to record audio. I am having trouble with running them both at the same time I am unsure if this is the correct way to write threads in python.
The errors:
The error I get is input overflow but I am unsure what this means and how I can make this work. The audio is initiated but never saved. The program never enters the final statement for the audio.
import argparse
from concurrent.futures import thread
from multiprocessing.dummy import Process
import tempfile
import queue
import sys
import cv2
import time
import sounddevice as sd
import soundfile as sf
import pyrealsense2 as rs
import numpy as np
from pathlib import Path
#audio
def int_or_str(text):
"""Helper function for argument parsing."""
try:
return int(text)
except ValueError:
return text
print("hello ---")
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument(
'-l', '--list-devices', action='store_true',
help='show list of audio devices and exit')
args, remaining = parser.parse_known_args()
if args.list_devices:
print(sd.query_devices())
parser.exit(0)
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter,
parents=[parser])
parser.add_argument(
'filename', nargs='?', metavar='FILENAME',
help='audio file to store recording to')
parser.add_argument(
'-d', '--device', type=int_or_str,
help='input device (numeric ID or substring)')
parser.add_argument(
'-r', '--samplerate', type=int, help='sampling rate')
parser.add_argument(
'-c', '--channels', type=int, default=1, help='number of input channels')
parser.add_argument(
'-t', '--subtype', type=str, help='sound file subtype (e.g. "PCM_24")')
args = parser.parse_args(remaining)
q = queue.Queue()
def callback(indata, frames, time, status):
"""This is called (from a separate thread) for each audio block."""
if status:
print(status, file=sys.stderr)
q.put(indata.copy())
def recordAudio():
try:
if args.samplerate is None:
#TODO: this will change to be microphones and other devices
device_info = sd.query_devices(args.device, 'input')
# soundfile expects an int, sounddevice provides a float:
args.samplerate = int(device_info['default_samplerate'])
if args.filename is None:
args.filename = tempfile.mktemp(prefix='delme_rec_unlimited_',
suffix='.wav', dir='')
# Make sure the file is opened before recording anything:
with sf.SoundFile(args.filename, mode='x', samplerate=args.samplerate,
channels=args.channels, subtype=args.subtype) as file:
with sd.InputStream(samplerate=args.samplerate, device=args.device,
channels=args.channels, callback=callback):
print('#')
print('press Ctrl+C to stop the recording')
print('#')
while True:
file.write(q.get())
if cv2.waitKey(1) & 0xFF == ord('q'):
print("q was pressed in audio")
break
except KeyboardInterrupt:
print('\nRecording finished: ' + repr(args.filename))
parser.exit(0)
except Exception as e:
print("exeption e was found ",e)
parser.exit(type(e).__name__ + ': ' + str(e))
finally:
# Stop recording
print('\nRecording finished: ' + repr(args.filename))
print("audioRecording has stoped")
#video
#part of the displayvideo
def recordVideo():
print("Video Recording has initialized")
print("#")
# Configure depth and color streams
pipeline = rs.pipeline()
config = rs.config()
#create a device
#device1 = rs.device
#create a path
recording_path = Path('C:','/','appli','/','Documents','/','test recordings')
print(recording_path)
#create a recorder
#rec1 = rs.recorder(recording_path,device1)
# Get device product line for setting a supporting resolution
pipeline_wrapper = rs.pipeline_wrapper(pipeline)
pipeline_profile = config.resolve(pipeline_wrapper)
device = pipeline_profile.get_device()
device_product_line = str(device.get_info(rs.camera_info.product_line))
found_rgb = False
for s in device.sensors:
if s.get_info(rs.camera_info.name) == 'RGB Camera':
found_rgb = True
break
if not found_rgb:
print("The demo requires Depth camera with Color sensor")
exit(0)
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
if device_product_line == 'L500':
config.enable_stream(rs.stream.color, 960, 540, rs.format.bgr8, 30)
else:
config.enable_stream(rs.stream.color, 640, 480, rs.format.bgr8, 30)
#rs.recorder()
# Start streaming
config.enable_record_to_file('test2.bag')
profile = pipeline.start(config)
recorder = profile.get_device().as_recorder() # Obtain the streaming device and cast it to recorder type
try:
while True:
#current_time = time.time()
#elapsed_time = current_time - start_time
# Wait for a coherent pair of frames: depth and color
frames = pipeline.wait_for_frames()
depth_frame = frames.get_depth_frame()
color_frame = frames.get_color_frame()
if not depth_frame or not color_frame:
continue
# Convert images to numpy arrays
depth_image = np.asanyarray(depth_frame.get_data())
color_image = np.asanyarray(color_frame.get_data())
# Apply colormap on depth image (image must be converted to 8-bit per pixel first)
depth_colormap = cv2.applyColorMap(cv2.convertScaleAbs(depth_image, alpha=0.03), cv2.COLORMAP_JET)
depth_colormap_dim = depth_colormap.shape
color_colormap_dim = color_image.shape
# If depth and color resolutions are different, resize color image to match depth image for display
if depth_colormap_dim != color_colormap_dim:
resized_color_image = cv2.resize(color_image, dsize=(depth_colormap_dim[1], depth_colormap_dim[0]), interpolation=cv2.INTER_AREA)
images = np.hstack((resized_color_image, depth_colormap))
else:
images = np.hstack((color_image, depth_colormap))
# Show images
cv2.namedWindow('RealSense', cv2.WINDOW_AUTOSIZE)
cv2.imshow('RealSense', images)
if cv2.waitKey(1) & 0xFF == ord('q'):
print("q was pressed in Video")
break
finally:
# Stop recording
recorder.pause() # Pause recording while continuing to stream
pipeline.stop()
pipeline = rs.pipeline()
pipeline.start()
pipeline.stop()
print("Video Recording Has stoped")
if __name__ == "__main__":
thread1 = Process(target = recordAudio)
thread2 = Process(target = recordVideo)
thread1.start()
thread2.start()
im making a type of "aux switch" and I decided that i was going to use python module sounddevice to do that and I use it BUT i cant seem to get it to use the sound device that i want it too. every time that i try to change it for some reason it dose not work
import argparse
import sounddevice as sd
import numpy # Make sure NumPy is loaded before it is used in the callback
assert numpy # avoid "imported but unused" message (W0611)
print(sd.query_devices())
sd.check_input_settings(device= "VoiceMeeter Aux Output (VB-Audio VoiceMeeter AUX VAIO), Windows DirectSound")
sd.check_output_settings(device= "CABLE Input (VB-Audio Virtual Cable), Windows DirectSound ")
def int_or_str(text):
"""Helper function for argument parsing."""
try:
return int(text)
except ValueError:
return text
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument(
'-l', '--list-devices', action='store_true',
help='show list of audio devices and exit')
args, remaining = parser.parse_known_args()
if args.list_devices:
print(sd.query_devices())
parser.exit(0)
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter,
parents=[parser])
parser.add_argument(
'-i', '--input-device', type=int_or_str,
help='')
parser.add_argument(
'-o', '--output-device', type=int_or_str,
help='output device (numeric ID or substring)')
parser.add_argument(
'-c', '--channels', type=int, default=2,
help='number of channels')
parser.add_argument('--dtype', help='audio data type')
parser.add_argument('--samplerate', type=float, help='sampling rate')
parser.add_argument('--blocksize', type=int, help='block size')
parser.add_argument('--latency', type=float, help='latency in seconds')
args = parser.parse_args(remaining)
def callback(indata, outdata, frames, time, status):
if status:
print(status)
outdata[:] = indata
try:
with sd.Stream(device=(args.input_device, args.output_device),
samplerate=args.samplerate, blocksize=args.blocksize,
dtype=args.dtype, latency=args.latency,
channels=args.channels, callback=callback):
print('#' * 80)
print('press Return to quit')
print('#' * 80)
input()
except KeyboardInterrupt:
parser.exit('')
except Exception as e:
parser.exit(type(e).__name__ + ': ' + str(e))
import argparse
import sounddevice as sd
import numpy # Make sure NumPy is loaded before it is used in the callback
assert numpy # avoid "imported but unused" message (W0611)
print(sd.query_devices())
inpdev = "VoiceMeeter Aux Output (VB-Audio VoiceMeeter AUX VAIO), Windows DirectSound"
outdev = "CABLE Input (VB-Audio Virtual Cable), Windows DirectSound "
def int_or_str(text):
"""Helper function for argument parsing."""
try:
return int(text)
except ValueError:
return text
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument(
'-l', '--list-devices', action='store_true',
help='show list of audio devices and exit')
args, remaining = parser.parse_known_args()
if args.list_devices:
print(sd.query_devices())
parser.exit(0)
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter,
parents=[parser])
parser.add_argument(
'-i', '--input-device', type=int_or_str,
help='')
parser.add_argument(
'-o', '--output-device', type=int_or_str,
help='output device (numeric ID or substring)')
parser.add_argument(
'-c', '--channels', type=int, default=2,
help='number of channels')
parser.add_argument('--dtype', help='audio data type')
parser.add_argument('--samplerate', type=float, help='sampling rate')
parser.add_argument('--blocksize', type=int, help='block size')
parser.add_argument('--latency', type=float, help='latency in seconds')
args = parser.parse_args(remaining)
def callback(indata, outdata, frames, time, status):
if status:
print(status)
outdata[:] = indata
try:
with sd.Stream(device=(inpdev, outdev),
samplerate=args.samplerate, blocksize=args.blocksize,
dtype=args.dtype, latency=args.latency,
channels=args.channels, callback=callback):
print('#' * 80)
print('press Return to quit')
print('#' * 80)
input()
except KeyboardInterrupt:
parser.exit('')
except Exception as e:
parser.exit(type(e).__name__ + ': ' + str(e))
I have been writing a code to make an app that can stream mic input to speaker output it works perfectly on PyCharm or any IDE but as soon as a successful installation happens and you do first start up it crashes, IDK why no solution so far has worked this was my first python app. Can anyone help
I used google colab to run builddozer but its not working. Can anyone tell me what is wrong here?
import argparse
import kivy
from kivymd.app import MDApp
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
import sounddevice as sd
import numpy # Make sure NumPy is loaded before it is used in the callback
assert numpy # avoid "imported but unused" message (W0611)
def int_or_str(text):
"""Helper function for argument parsing."""
try:
return int(text)
except ValueError:
return text
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument(
'-l', '--list-devices', action='store_true',
help='show list of audio devices and exit')
args, remaining = parser.parse_known_args()
if args.list_devices:
print(sd.query_devices())
parser.exit(0)
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter,
parents=[parser])
parser.add_argument(
'-i', '--input-device', type=int_or_str,
help='input device (numeric ID or substring)')
parser.add_argument(
'-o', '--output-device', type=int_or_str,
help='output device (numeric ID or substring)')
parser.add_argument(
'-c', '--channels', type=int, default=2,
help='number of channels')
parser.add_argument('--dtype', help='audio data type')
parser.add_argument('--samplerate', type=float, help='sampling rate')
parser.add_argument('--blocksize', type=int, help='block size')
parser.add_argument('--latency', type=float, help='latency in seconds')
args = parser.parse_args(remaining)
class THAGrid(GridLayout):
def __init__(self,**kwargs):
super(THAGrid, self).__init__()
self.cols = 2
self.add_widget(Label(text="Press To Start"))
self.press = Button(text = "Press to Hear")
self.press.bind(on_press=self.hear)
self.add_widget(self.press)
def hear(self, instance, ):
def callback(indata, outdata, frames, time, status):
if status:
print(status)
outdata[:] = indata
try:
with sd.Stream(device=(args.input_device, args.output_device),
samplerate=args.samplerate, blocksize=args.blocksize,
dtype=args.dtype, latency=args.latency,
channels=args.channels, callback=callback):
print('#' * 80)
print('press Return to quit')
print('#' * 80)
input()
except KeyboardInterrupt:
parser.exit('')
except Exception as e:
parser.exit(type(e).__name__ + ': ' + str(e))
class TheHealingTech(MDApp):
def build(self):
return THAGrid()
if __name__ == "__main__":
TheHealingTech().run()
Try using kivy 2.0.0 and kivymd==0.104.2. Install them in plugins trough settings in pycharm. And your buildozer.spec should be like this:
requirements = python3,kivy==2.0.0,kivymd==0.104.2,pillow,sdl2_ttf==2.0.15,argparse,numpy
my python file would read two files from command line using argparse.
I want to use 'try...except...' to handle incorrect user input, like non-exist file and incorrect command:
for example, I use '-f' to open file, then '-g' is incorrect command.
my codes are:
> parser = argparse.ArgumentParser(description = "Handle with input
files")
parser.add_argument('-m', dest = "model", type=argparse.FileType('r'))
parser.add_argument('-t', dest = "test", type=argparse.FileType('r'))
parser.add_argument('-d', dest = "permute",type=argparse.FileType('r'))
args = parser.parse_args()
# Parse file names from command line
model_file = open(args.model.name)
test_file = open(args.test.name)
decoy_file = open(args.permute.name)
I try to implement 'try...except' in my code but failed. Here is what I did:
> parser = argparse.ArgumentParser(description = "Handle with input
files")
parser.add_argument('-m', dest = "model", type=argparse.FileType('r'))
parser.add_argument('-t', dest = "test", type=argparse.FileType('r'))
parser.add_argument('-d', dest = "permute",type=argparse.FileType('r'))
args = parser.parse_args()
# Parse file names from command line
try:
model_file = open(args.model.name)
test_file = open(args.test.name)
decoy_file = open(args.permute.name)
except IOError:
print('Wrong file name')
I would suggest adding something like this to make sure all arguments are there. Also argparse.FileType('r') will already open the file (meaning you can do something like args.model.readlines()).
if not all([args.model, args.test, args.permute]):
print("All Arguments are required")
exit(1)
Firstly the error happend at args = parser.parse_args() when it parse your command param.
As we can see from argparse's origin code:
...
def parse_args(self, args=None, namespace=None):
args, argv = self.parse_known_args(args, namespace)
if argv:
msg = _('unrecognized arguments: %s')
self.error(msg % ' '.join(argv))
return args
...
def error(self, message):
"""error(message: string)
Prints a usage message incorporating the message to stderr and
exits.
If you override this in a subclass, it should not return -- it
should either exit or raise an exception.
"""
self.print_usage(_sys.stderr)
args = {'prog': self.prog, 'message': message}
self.exit(2, _('%(prog)s: error: %(message)s\n') % args)
# exit here ,that's why can not catch the exception
So when you input incorrectly, like what you mentioned above, like non-exist file or incorrect command , it will call error function in the end, and exit the thread, that's why you cannot catch a exception by try .. except synax.
If you want to catch the exception, you can inherit the main class ArgumentParser, and rewrite the method error. you can take my codes as reference:
import argparse
import sys as _sys
from argparse import ArgumentParser
class ArgumentParserSub(ArgumentParser):
def error(self, message):
self.print_usage(_sys.stderr)
args = {'prog': self.prog, 'message': message}
raise Exception(('error: %(message)s\n') % args)
parser = ArgumentParserSub(description = "Handle with input files")
parser.add_argument('-m', dest = "model", type=argparse.FileType('r'))
parser.add_argument('-t', dest = "test", type=argparse.FileType('r'))
parser.add_argument('-d', dest = "permute",type=argparse.FileType('r'))
try:
args = parser.parse_args()
# Parse file names from command line
model_file = open(args.model.name)
test_file = open(args.test.name)
decoy_file = open(args.permute.name)
except Exception as e:
print(e)
print('Wrong file name')
If you don't want the parser to catch and exit bad files, open the files yourself. Just ask the parser for names, not open files.
parser = argparse.ArgumentParser(description = "Handle with input files")
parser.add_argument('-m', dest = "model") # no type
parser.add_argument('-t', dest = "test")
parser.add_argument('-d', dest = "permute")
args = parser.parse_args()
# print(args) # good idea when debugging
# Open file names from command line
try:
model_file = open(args.model)
test_file = open(args.test)
decoy_file = open(args.permute)
except IOError:
print('Wrong file name')
Python 3.6
This program:
launches ffmpeg as a subprocess
waits for a socket connection
receives PNG images on the socket
sends the PNG images to ffmpeg
stdin
The problem is step 4. I can't work out how to send the received PNG image from the coroutine to the stdin of the ffmpeg subprocess. Can anyone please point me in the right direction to send the PNG image to the stdin of the ffmpeg subprocess?
EDIT: to clarify - there's nothing wrong with this code, it receives the PNGs fine over the socket. I just don't have any idea how to send the PNGs on into the stdin of ffmpeg. I've done quite alot of Python but asyncio is new to me and how things tie together is a mystery.
thanks!
import asyncio
import argparse, sys
import sys
import base64
from struct import unpack
parser = argparse.ArgumentParser()
parser.add_argument('--port', help='ffmpeg listen port')
parser.add_argument('--outputfilename', help='ffmpeg output filename')
args = parser.parse_args()
if not args.port:
print("port is required")
sys.exit(1)
if not args.outputfilename:
print("outputfilename is required")
sys.exit(1)
async def _read_stream(stream, cb):
while True:
line = await stream.readline()
if line:
cb(line)
else:
break
async def _stream_subprocess(cmd, stdout_cb, stderr_cb):
process = await asyncio.create_subprocess_exec(
*cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
stdin=asyncio.subprocess.PIPE,
)
await asyncio.wait([
_read_stream(process.stdout, stdout_cb),
_read_stream(process.stderr, stderr_cb)
])
return await process.wait()
def process_stderr(line):
# ffmpeg finishes processing and writes the output file when its input is closed
# thus the completion message will come out of stderr only when the socket or stdin or whatever is closed
line = line.decode()
print(line)
if "Output" in line:
if args.outputfilename in line:
print('finished!!!!')
sys.exit(0)
def process_stdout(line):
print("STDOUT: %s" % line)
def spawn_ffmpeg(listenport, outputfilename, framerate=30, format='webm'):
outputdirectory = "sftp://username:password#10.0.0.196/var/www/static/"
input_type = "pipe:0" #stdin
params = \
f"ffmpeg " \
f"-loglevel 56 " \
f"-y -framerate {framerate} " \
f"-f image2pipe " \
f"-i {input_type} " \
f"-c:v libvpx-vp9 " \
f"-b:v 1024k " \
f"-q:v 0 " \
f"-pix_fmt yuva420p " \
f"{outputdirectory}{outputfilename} "
return params
async def socket_png_receiver(reader, writer):
while True:
# first the client sends the length of the data to us
lengthbuf = await reader.read(4)
length, = unpack('!I', lengthbuf)
if length == 0:
print("length was 0, finish") # a zero length PNG says that there are no more frames
break
# then we read the PNG
data = await reader.read(length)
data = data.decode() # from bytes to string
png_bytes = base64.b64decode(data) # from base64 to bytes
# next line was just a guess, so I have commented it out.
#await proc.communicate(png_bytes)
print("Got PNG, length", length)
return
loop = asyncio.get_event_loop()
command = spawn_ffmpeg("24897", args.outputfilename)
ffmpeg_process = _stream_subprocess(
command.split(),
process_stdout,
process_stderr,
)
#coro = asyncio.start_server(socket_png_receiver, '0.0.0.0', args.port, ffmpeg_process, loop=loop)
coro = asyncio.start_server(socket_png_receiver, '0.0.0.0', args.port, loop=loop)
several_futures = asyncio.gather(ffmpeg_process, coro)
server = loop.run_until_complete(several_futures)
server.close()
loop.close()
Here are the changes suggested by #user4815162342
import asyncio
import argparse, sys
import sys
import base64
from struct import unpack
parser = argparse.ArgumentParser()
parser.add_argument('--port', help='ffmpeg listen port')
parser.add_argument('--outputfilename', help='ffmpeg output filename')
args = parser.parse_args()
if not args.port:
print("port is required")
sys.exit(1)
if not args.outputfilename:
print("outputfilename is required")
sys.exit(1)
if not args.outputfilename.endswith('.webm'):
print("outputfilename must end with '.webm'")
sys.exit(1)
async def _read_stream(stream, cb):
while True:
line = await stream.readline()
if line:
cb(line)
else:
break
async def _stream_subprocess(cmd, stdout_cb, stderr_cb):
global process
process = await asyncio.create_subprocess_exec(
*cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
stdin=asyncio.subprocess.PIPE,
)
await asyncio.wait([
_read_stream(process.stdout, stdout_cb),
_read_stream(process.stderr, stderr_cb)
])
return await process.wait()
def process_stderr(line):
# ffmpeg finishes processing and writes the output file when its input is closed
# thus the completion message will come out of stderr only when the socket or stdin or whatever is closed
line = line.decode()
print(line)
if "Output" in line:
if args.outputfilename in line:
print('finished!!!!')
sys.exit(0)
def process_stdout(line):
print("STDOUT: %s" % line)
def spawn_ffmpeg(listenport, outputfilename, framerate=30, format='webm'):
outputdirectory = "sftp://username:password#10.0.0.196/var/www/static/"
input_type = "pipe:0" # stdin
params = \
f"ffmpeg " \
f"-loglevel 56 " \
f"-y " \
f"-framerate {framerate} " \
f"-i {input_type} " \
f"{outputdirectory}{outputfilename} "
print(params)
return params
async def socket_png_receiver(reader, writer):
while True:
# first the client sends the length of the data to us
lengthbuf = await reader.readexactly(4)
length, = unpack('!I', lengthbuf)
if length == 0:
print("length was 0, finish") # a zero length PNG says that there are no more frames
break
# then we read the PNG
print("Got PNG, length", length)
data = await reader.readexactly(length)
print(data)
png_bytes = base64.b64decode(data) # from base64 to bytes
process.stdin.write(png_bytes)
return
loop = asyncio.get_event_loop()
command = spawn_ffmpeg("24897", args.outputfilename)
ffmpeg_process = _stream_subprocess(
command.split(),
process_stdout,
process_stderr,
)
coro = asyncio.start_server(socket_png_receiver, '0.0.0.0', args.port, loop=loop)
several_futures = asyncio.gather(ffmpeg_process, coro)
server = loop.run_until_complete(several_futures)
server.close()
loop.close()
There are several issues with the code:
await reader.read(length) should be await reader.readexactly(length) because the argument to StreamReader.read is the maximum number of bytes to read, and it can return fewer.
proc.communicate(png_bytes) should be changed to proc.stdin.write(png_bytes). The call to communicate() is incorrect here because you want to continue talking to the program, while communicate() waits for all the streams to close.
The instance of process returned by asyncio.create_subprocess_exec(...) must be made available to socket_png_receiver, e.g. by making the process variable global using global process. (It would be better to use a class and assign to self.process, but that is beyond the scope of this answer.)
Some potential issues:
There is no need to decode data from bytes to string, base64.b64decode can accept bytes just fine.
spawn_ffmpeg() doesn't appear to use its listenport parameter.