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

Corrupted H264 files when switching between ring buffers #704

Open
rossGardiner opened this issue Sep 8, 2021 · 3 comments
Open

Corrupted H264 files when switching between ring buffers #704

rossGardiner opened this issue Sep 8, 2021 · 3 comments

Comments

@rossGardiner
Copy link

rossGardiner commented Sep 8, 2021

Hi,

I'm writing an application which saves h264 and rgb streams when motion is detected. My strategy to get around slow RPi IO is to implement two buffers and switch between them. See my program snippet below:

def write_video(stream,filename, frametype=picamera.PiVideoFrameType.frame):
    # Write the entire content of the circular buffer to disk. No need to
    # lock the stream here as we're definitely not writing to it
    # simultaneously
    with io.open(filename, 'ab') as output:
        for frame in stream.frames:
            if frame.frame_type == frametype:
                stream.seek(frame.position)
                break
        while True:
            buf = stream.read1()
            if not buf:
                break
            output.write(buf)
    # Wipe the circular stream once we're done
    stream.seek(0)
    stream.clear()

#camera set up stuff
iobuf_h264_1 = PiCameraIO(camera, seconds=seconds, splitter_port=1)
iobuf_h264_2 = PiCameraIO(camera, seconds=seconds, splitter_port=1)

iobuf_imgs_1 = PiCameraIO(camera, seconds=(seconds), splitter_port=0)
iobuf_imgs_2 = PiCameraIO(camera, seconds=(seconds), splitter_port=0)

camera.start_recording(iobuf_h264_1, format='h264', splitter_port=1, motion_output=motion_detector)
camera.start_recording(iobuf_imgs_1,  format='rgb', splitter_port=0,  resize=(416, 416))
try:
        while filenum < 10:
            
            if motion_detector.is_motion:
                #this is a motion event
                print('motion detected!')
                start = time.time()
                first_flag = True
                while motion_detector.is_motion:
                    camera.split_recording(iobuf_h264_2, splitter_port=1)
                    camera.split_recording(iobuf_imgs_2, splitter_port=0)
                    write_video(iobuf_h264_1, 'motion'+str(filenum)+'.h264', picamera.PiVideoFrameType.sps_header)
                    write_video(iobuf_imgs_1, 'motion'+str(filenum)+'.dat')
                    camera.wait_recording(3)
                    camera.split_recording(iobuf_h264_1, splitter_port=1)
                    camera.split_recording(iobuf_imgs_1, splitter_port=0)
                    write_video(iobuf_h264_2, 'motion'+str(filenum)+'.h264', picamera.PiVideoFrameType.sps_header)
                    write_video(iobuf_imgs_2, 'motion'+str(filenum)+'.dat')
                    camera.wait_recording(3)
                    first_flag = False
                end = time.time()
                filenum += 1
                print('motion ended! lasted {}s'.format(end-start))

When I view H264 files which I've saved, they are corrupted and only play to the sections where they have been joined together before vlc player gives up and starts again. Is there some problem with the way I'm appending the video to disk? I followed a PiCamera example.

Many thanks for any advice.

@rossGardiner
Copy link
Author

rossGardiner commented Sep 9, 2021

Update: I suspect the steam corruption is due to large gaps in frames caused by latency of the split_recording function.
I measured the split_recording latency for h264 stream. It can grow to be as large as 1.5 seconds. I'm recording at 1080p 25fps 17Mbps.

How is this possible? I'm only changing the location in memory the stream writes to - right? Why does split_recording occasionally take absolutely ages to complete?

@Esser50K
Copy link

Esser50K commented Oct 8, 2021

As far as I know split_recording will ask the encoder to produce a new keyframe.
This does not mean the encoder will immediately generate one, although 1.5seconds seems like a lot.

There also was an issue with the ringbuffer not being able to hold an entire frame when it was too big.
Supposedly this was fixed but there isn't a new release of it. So maybe try installing directly from the repo

@Esser50K
Copy link

Esser50K commented Oct 8, 2021

or just try using picameraX -> https://github.com/labthings/picamerax

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

2 participants