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

segfault when using h264_v4l2m2m encoder + ffmpeg 4.14 #977

Closed
jasaw opened this issue Jul 29, 2019 · 10 comments
Closed

segfault when using h264_v4l2m2m encoder + ffmpeg 4.14 #977

jasaw opened this issue Jul 29, 2019 · 10 comments

Comments

@jasaw
Copy link
Contributor

jasaw commented Jul 29, 2019

  1. Reviewed guide and contributing documents? (Yes/No): Yes
  2. version [x.y.z, hash, other]: motion-release-4.2.2
  3. installed as a package or compiled from sources [deb, rpm, git, other]: compiled from source
  4. standalone or part of third party [motion, MotionEyeOS, other]: standalone motion
  5. video stream source [V4L (card or USB), net cam (mjpeg, rtsp, other), mmal]: v4l
  6. hardware [x86, ARM, other]: ARM (Odroid XU4)
  7. operating system [16.04, Stretch, etc, FreeBSD, other]: Armbian kernel 4.14.127-odroidxu4

Motion is able to configure the encoder's parameters like bitrate etc, but as soon as the first frame is sent over to ffmpeg, I get a segfault. I've added more debug prints in ffmpeg library to see where it actually segfaults. Here's the log:

[1:ml1] [NTC] [ALL] motion_detected: Motion detected - starting event 1
[1:ml1] [INF] [ENC] ffmpeg_encode_video: avcodec_send_frame encode
ffmpeg: avcodec_send_frame called
ffmpeg: calling codec->send_frame
v4l2_m2m_enc: v4l2_send_frame called
v4l2_m2m_enc: calling ff_v4l2_context_enqueue_frame
ff_v4l2_context_enqueue_frame called
v4l2_getfree_v4l2buf
calling ff_v4l2_buffer_avframe_to_buf
ff_v4l2_buffer_avframe_to_buf called, out ptr: 0xb5506f70
num_planes = 2
0: v4l2_bufref_to_buf
0: frame ptr = 0xb55064d0
0: &frame->buf[0] = 0xb55065c8
Segmentation fault

This is the function after I've added debug print.

int ff_v4l2_buffer_avframe_to_buf(const AVFrame *frame, V4L2Buffer* out)
{
    printf("ff_v4l2_buffer_avframe_to_buf called, out ptr: %p\n", out);
    printf("num_planes = %d\n", out->num_planes);
    int i, ret;

    for(i = 0; i < out->num_planes; i++) {
        printf("%d: v4l2_bufref_to_buf\n", i);
        printf("%d: frame ptr = %p\n", i, frame);
        printf("%d: &frame->buf[%d] = %p\n", i, i, &frame->buf[i]);
        printf("%d: frame->buf[%d]->size = %d\n", i, i, frame->buf[i]->size);
        printf("%d: frame->buf[%d]->data ptr = %p\n", i, i, frame->buf[i]->data);
        printf("%d: frame->buf[%d]->data[0] = 0x%x\n", i, i, frame->buf[i]->data[0]);
        ret = v4l2_bufref_to_buf(out, i, frame->buf[i]->data, frame->buf[i]->size, frame->buf[i]);
        if (ret)
            return ret;
    }

    v4l2_set_pts(out, frame->pts);

    return 0;
}

Not sure whether it's motion's bug or ffmpeg's bug, hoping you guys might be able to give me some pointers.

@tosiara
Copy link
Member

tosiara commented Aug 11, 2019

The function ff_v4l2_buffer_avframe_to_buf is not from motion codebase

@jasaw
Copy link
Contributor Author

jasaw commented Aug 12, 2019

Yes, the ff_v4l2_buffer_avframe_to_buf is from ffmpeg, but it's not liking the data that motion gave it. If you look carefully, it's segfaulting when dereferencing the size member variable frame->buf[i]->size. If I'm not mistaken, frame->buf comes from motion.

@Mr-DaveDev
Copy link
Contributor

I was just looking at this. Unfortunately, it does not appear that I have a device that this will work upon. I specified a codec of mp4:h264_v4l2m2m and it throws a ffmpeg_avcodec_log: Could not find a valid device I've never tried it before so I'm not sure if this is the precise codec format required to trigger it or whether other hardware is required.

From your debug, it seems to be getting an invalid size and I'd recommend validating the size and data buffer parameters inside of Motion as it is called. Effectively to see whether Motion provided the values in the frame and they were lost along the way to the decoder in FFmpeg or whether Motion got a bad frame, sent it along and other encoders ignored it but this one seg faulted.

If it is the latter, then we can see about specifying the size and other parameters better or skipping invalid input frames. If instead it is being lost instead of the FFmpeg processing, that would be a different course.

A debug print similar to the one provided but also upon the frame sent from Motion should illuminate the situation.

@jasaw
Copy link
Contributor Author

jasaw commented Aug 12, 2019

Turns out that the encoder expects ffmpeg->picture->buf to be allocated and set correctly, but motion left it as a NULL pointer. Motion writes the image pointer into ffmpeg->picture->data instead. ffmpeg eventually tries to get the size by derefencing the buf[0], which is a null pointer, and got a segfault. Debug output below.

ffmpeg->picture->buf looks like a referenced counted version of ffmpeg->picture->data?

Post my_frame_alloc ffmpeg->picture ptr: 0xb550b0f0
Post my_frame_alloc ffmpeg->picture->buf ptr: 0xb550b1e8
[1:ml1] [INF] [ENC] ffmpeg_avcodec_log: Setting default whitelist 'file,crypto'
[1:ml1] [NTC] [EVT] event_newfile: File of type 8 saved to: /root/motioneye/2019-08-12/15-25-18.mp4
[1:ml1] [NTC] [ALL] motion_detected: Motion detected - starting event 1
[1:ml1] [INF] [ENC] ffmpeg_encode_video: avcodec_send_frame encode
Pre avcodec_send_frame ffmpeg->picture ptr: 0xb550b0f0
Pre avcodec_send_frame ffmpeg->picture->buf ptr: 0xb550b1e8
Pre avcodec_send_frame ffmpeg->picture->buf[0] ptr: (nil)
ffmpeg: avcodec_send_frame called
ffmpeg: calling codec->send_frame
v4l2_m2m_enc: v4l2_send_frame called
v4l2_m2m_enc: calling ff_v4l2_context_enqueue_frame
ff_v4l2_context_enqueue_frame called
v4l2_getfree_v4l2buf
calling ff_v4l2_buffer_avframe_to_buf
ff_v4l2_buffer_avframe_to_buf called, out ptr: 0xb5506f70
num_planes = 2
0: v4l2_bufref_to_buf
0: frame ptr = 0xb550b0f0
0: &frame->buf[0] = 0xb550b1e8
0: frame->buf[0] = (nil)
Segmentation fault

@jasaw
Copy link
Contributor Author

jasaw commented Aug 12, 2019

I added av_frame_get_buffer(ffmpeg->picture, 8) after setting ffmpeg->width and ffmpeg->height, and that got me slightly further. It segfaults on buf[1] instead.

ffmpeg: avcodec_send_frame called
ffmpeg: calling codec->send_frame
v4l2_m2m_enc: v4l2_send_frame called
v4l2_m2m_enc: calling ff_v4l2_context_enqueue_frame
ff_v4l2_context_enqueue_frame called
v4l2_getfree_v4l2buf
calling ff_v4l2_buffer_avframe_to_buf
ff_v4l2_buffer_avframe_to_buf called, out ptr: 0xb5506f70
num_planes = 2
0: v4l2_bufref_to_buf
0: frame ptr = 0xb550b0f0
0: &frame->buf[0] = 0xb550b1e8
0: frame->buf[0] = 0xb5504e10
0: frame->buf[0]->size = 1059968
0: frame->buf[0]->data ptr = 0xb04d9020
0: frame->buf[0]->data[0] = 0x0
v4l2_bufref_to_buf called, plane 0 >= out->numplanes 2
out->plane_info[plane].length = 691456
bytesused = 691456
memcpy out->plane_info[plane].mm_addr: 0xb18ce000
1: v4l2_bufref_to_buf
1: frame ptr = 0xb550b0f0
1: &frame->buf[1] = 0xb550b1ec
1: frame->buf[1] = (nil)
Segmentation fault

@Mr-DaveDev
Copy link
Contributor

Did you really mean ffmpeg->picture->width and ffmpeg->picture->height rather than ffmpeg->width and ffmpeg->height ?

Try adding your av_frame_get_buffer call to the bottom of ffmpeg_set_picture ? That seems to be where it would belong since the height/width/format are set there on the avframe.

May also want to revise the second parameter to 0 rather than 8 and let FFmpeg decide the alignment.

@jasaw
Copy link
Contributor Author

jasaw commented Aug 16, 2019

Turns out that this encoder only accepts multi-planar buffer, and the ffmpeg v4l2 code only looks for the image Y channel in ffmpeg->picture->buf[0] and UV channel in ffmpeg->picture->buf[1]. Motion does not set ffmpeg->picture->buf does it?

As a test, after ffmpeg_set_picture, I used av_buffer_alloc to allocate image buffer to both ffmpeg->picture->buf[0] and ffmpeg->picture->buf[1], then set ffmpeg->picture->data[n] pointers to point to buf[0] and buf[1]. In ffmpeg_put_image, I did a memcpy and YUV to NV21 format conversion, instead of pointing ffmpeg->picture->data[n] to our source image. This managed to get the h264_v4l2m2m encoder to work correctly.

Now I have a couple of questions:

  • Should motion detect which format is supported by the selected encoder, rather than assuming YUV format? Or is this ffmpeg's job?
  • What's the correct thing to do for motion to support this encoder? If possible, other encoders in the future?

@Mr-Dave
Copy link
Member

Mr-Dave commented Aug 17, 2019

Motion does not set ffmpeg->picture->buf does it?

No. We set the pointers in the data struct.

Should motion detect which format is supported by the selected encoder, rather than assuming YUV format? Or is this ffmpeg's job?

Is it even possible for Motion to do this? I do not recall seeing in any of the FFmpeg examples a demonstration of this being done. If they don't have it in an example, then I'd say that it would be upon FFmpeg to do the appropriate conversions.

What's the correct thing to do for motion to support this encoder? If possible, other encoders in the future?

From what you've described, it seems like a small function to work around it for this encoder would be needed in Motion. The larger issue regarding what to do with all the other encoders is tougher since we do not know what each encoder is expecting. (Hence why it would seem to be needed in FFmpeg as part of their implementation for each encoder)

@jasaw
Copy link
Contributor Author

jasaw commented Aug 20, 2019

@Mr-Dave You are right that FFmpeg does not provide an API to check which formats are supported by an encoder.

I have created PR #991 to handle h264_v4l2m2m encoder differently in various places.

@Mr-Dave
Copy link
Member

Mr-Dave commented Sep 20, 2019

Closed via #991

@Mr-Dave Mr-Dave closed this as completed Sep 20, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants