Почему запись в файл работает, а запись в обработчик запросов HTTP-сервера не работает? - PullRequest
1 голос
/ 23 января 2020

Я использую плату Raspberry Pi Picamera для сбора данных, используя следующий код:

with picamera.PiCamera(
    sensor_mode=4,
    resolution='1640x1232',
    framerate=30
    ) as camera:

    camera.rotation = 180
    camera.start_recording(StreamingOutput(), format='mjpeg')

try:
    server = StreamingServer(('', 8000), StreamingHandler)
    server.serve_forever()
finally:
    camera.stop_recording()


class StreamingOutput:
    def __init__(self):
        self.frame = None
        self.condition = threading.Condition()
        self._buffer = io.BytesIO()

    def write(self, buf):
        if buf.startswith(b'\xff\xd8'):
            # New frame, copy the existing buffer's content and notify all
            # clients it's available
            self._buffer.truncate()
            with self.condition:
                self.frame = self._buffer.getvalue()
                self.condition.notify_all()
            self._buffer.seek(0)


class StreamingServer(socketserver.ThreadingMixIn, server.HTTPServer):
    allow_reuse_address = True
    daemon_threads = True


class StreamingHandler(server.BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/capture.jpg':
            with self._output.condition:
                self._output.condition.wait()
                frame = self._output.frame

            # This works.
            with open("frame.jpg", 'wb') as f:
                f.write(frame)

            # This produces a truncated image.
            self.send_response(200)
            self.send_header('Content-Type', 'image/jpeg')
            self.send_header('Content-Length', len(frame))
            self.end_headers()
            self.wfile.write(frame)

Это самая ужасная вещь: хотя изображение будет сохраняться на диск просто отлично (frame.jpg полностью хорошо), оно получит усеченное изображение, если будет проходить через HTTP-сервер. Вот скриншот:

truncated image

Я пробовал несколько разных вещей, и я зашел в тупик. Есть идеи?

Ответы [ 2 ]

1 голос
/ 23 января 2020

Вот рабочая версия:

def do_capture(self):
    with self._output.condition:
        self._output.condition.wait()
        frame = self._output.frame

    self.send_response(200)
    self.send_header('Content-Type', 'image/jpeg')
    self.send_header('Content-Length', len(frame))
    self.end_headers()
    fb = io.BytesIO(frame)
    shutil.copyfileobj(fb, self.wfile)

Обтекание frame в потоке BytesIO работает в сочетании с shutil, который ожидает два файловых объекта. Вместе он создает функциональный поток.

1 голос
/ 23 января 2020

Я предполагаю, что у вас проблема с памятью. Когда вы записываете на диск, содержимое передается в потоковом режиме, но я ожидаю, что при использовании wfile.write(frame) вам придется почти удвоить объем используемой памяти, поскольку вы не разбиваете данные на части (вместо frame и чанка frame). в памяти в любой момент времени у вас есть две копии. Я попытался бы использовать shutil, чтобы увидеть, если это исправит проблему, выполнив shutil.copyfileobj(frame, self.wfile). Это только предположение, но, надеюсь, это решит вашу проблему! shutil docs

...