Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Record frames to video #24

Closed
robsmall opened this issue Sep 8, 2018 · 6 comments
Closed

Record frames to video #24

robsmall opened this issue Sep 8, 2018 · 6 comments

Comments

@robsmall
Copy link

robsmall commented Sep 8, 2018

First off, I just wanted to thank you for creating this project!

My repo is a bit different as I needed to be able to stream to Android clients and wanted to play around with flask-RESTful as seen here: https://github.com/robsmall/flask-raspi-video-streamer. I have been playing around with this repo and am looking to record the frames to a video file on disk while keeping a similar architecture and continuing to stream the video.

I was looking at opencv and pillow to achieve this but have been unable to do so. I was curious if you have any pointers around how to write the frame objects to a video file on disk, while still streaming.

Thanks in advance!

@miguelgrinberg
Copy link
Owner

There is a complete implementation of a video file writer for Python in this article: https://www.learnopencv.com/read-write-and-display-a-video-using-opencv-cpp-python/.

@robsmall
Copy link
Author

robsmall commented Sep 10, 2018

Thanks for the quick response and share! I was hoping to avoid needing to use openCV as well as picamera to generate the data to write to the file so I could use the same stream that is sent over the network. I was looking to achieve this by using a custom StreamingOutput from the picamera's recording like so:

class StreamingOutput(object):
    def __init__(self):
        self.frame = None
        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()
            self.frame = self.buffer.getvalue()
            self.buffer.seek(0)
        return self.buffer.write(buf)

    def flush(self):
        print "\n\nflush!!!!\n\n"
        try:
            fourcc = cv2.cv.CV_FOURCC(*'MJPG')
            out = cv2.VideoWriter("output.avi", fourcc, 40.0, (1640, 922))

            arr = np.fromstring(self.buffer.getvalue(), dtype=np.uint8)
            out.write(arr)

            # Release everything if job is finished
            out.release()
            cv2.destroyAllWindows()
        except Exception as e:
            print e


class Camera(BaseCamera):
    camera = None
    output = None

    @staticmethod
    def start_recording():
        """ Start recording from the camera object. """
        Camera.camera.start_recording(Camera.output, format='mjpeg')

    @staticmethod
    def stop_recording():
        """ Stop the Camera object from recording. """
        Camera.camera.stop_recording()

    @staticmethod
    def frames():
        with picamera.PiCamera(
                sensor_mode=5, resolution="1640x922", framerate=40) as camera:
            Camera.camera = camera
            Camera.output = StreamingOutput()

            Camera.start_recording()
            
            while True:
                frame = Camera.output.frame
                yield frame

The issue is when I open the output avi file (output.avi), it is empty. I also tried writing the frames themselves but to no avail. Curious if you have any pointers here.

@miguelgrinberg
Copy link
Owner

The code that you are using is not written by me, it is actually based on code that I wrote, but was modified in a way that I don't fully understand. You now took that code and modified it even more, so I can't really tell you for sure what's wrong. What seems wrong to me though, is that the flush() function is opening the video file. My guess is that flush() is called several times during the recording, and each time it is called, you are creating a new video file, overwriting anything you may have written to it before that.

@robsmall
Copy link
Author

Thanks for the insight! flush() is called at the end of the output (according to the picamera docs) from the picamera. I was using this to PoC writing the output to a file by terminating the feed and looking at the file after recording for a while. Unfortunately, in reading up some more it looks like there are quite a few issues here.

Sorry for asking about modified broken code, I wanted to flag this as a question not an issue to see if you had any insight into writing the output from a stream to a file but in hindsight this is more of a question about writing picamera output to a file on a pi, not about the code in this repo. Will close.

@robsmall
Copy link
Author

Well... That was very broken. The issue was that I wasn't thinking clearly and missed the fact that I was truncating the buffer so it was only writing a single frame. The solution is to write out each frame and then release the buffer at the end:

class StreamingOutput(object):
    def __init__(self):
        self.frame = None
        self.buffer = io.BytesIO()

        four_cc = cv2.cv.CV_FOURCC(*'MJPG')
        now = datetime.datetime.now()
        self.out = cv2.VideoWriter(
            "output_{}.avi".format(now.strftime("%Y-%m-%d_%H:%M:%S")),
            four_cc,
            30,
            (1640, 922))

    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()
            self.frame = self.buffer.getvalue()
            self.buffer.seek(0)

            arr = cv2.imdecode(np.fromstring(buf, dtype=np.uint8), 1)
            self.out.write(arr)

            return self.buffer.write(buf)

    def flush(self):
        # Release everything since the job is finished
        self.out.release()
        cv2.destroyAllWindows()

@LMJayasundara
Copy link

Hello

Is there any way to do this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants