Memory OOM when using v4l2h264enc
I'm using the v4l2h264enc element to encode the video stream from a ADV7282-M CSI2 Video Decoder, which I'm passing via an appsrc element. In certain scenarios (e.g. disconnecting the Video Source to the Video Decoder) I encounter the issue that my Application runs OOM.
This starts with the encoder not outputting any data anymore and the Application taking more and more Memory. Like if a Worker Thread stops working, and it queues the Input Data till OOM.
I was able to reproduce this issue with feeding in random Data from /dev/urandom
via an appsrc. I guess something similar happens when disconnecting the Video Source to the Video Decoder as it tries to lock to the Video Signal and outputs whatever it receives At this moment.
An example Application to reproduce this is attached to this issue.
I was able to reproduce this issue on:
- Raspberry Pi CM4 - Debian 11 - GStreamer 1.18.4 - Linux 5.15.24
- Raspberry Pi 3 - ArchLinux ARM - GStreamer 1.20.0 - Linux 5.15.27-1-rpi-ARCH
- i.MX8MM - Debian 11 - GStreamer 1.16.2 - Linux 5.17.0-rc1
Code:
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <gst/gst.h>
#include <gst/app/gstappsrc.h>
#include <gst/app/gstappsink.h>
#include <gst/gstbuffer.h>
#include <gst/gstmemory.h>
#ifdef __arm__
#define IMAGE_WIDTH 720
#define IMAGE_HEIGHT 576
#endif
#define VIDEO_GST_DEBUG "*:3"
GstElement *pPipeline = NULL;
GstElement *pAppSrc = NULL;
void init_pipeline()
{
if (pPipeline != NULL)
return;
unsigned int major, minor, micro, nano;
/* initialize gstreamer */
setenv("GST_DEBUG", VIDEO_GST_DEBUG, 5);
gst_init(NULL, NULL);
gst_version(&major, &minor, µ, &nano);
gst_update_registry();
printf("Gstreamer initialized. \n");
printf("Gstreamer version: %u %u %u %u \n", major, minor, micro, nano);
#ifdef __arm__
char *pipelineStr = "appsrc name=appsrc ! video/x-raw, interlace-mode=interleaved, framerate=25/1, format=UYVY, width=720, height=576 ! "
"deinterlace method=linear ! videocrop top=16 left=16 right=16 bottom=16 ! videorate ! videoscale ! "
"video/x-raw, framerate=25/1, width=640, height=480 ! videoconvert ! "
"queue ! v4l2h264enc output-io-mode=2 extra-controls=\"controls,h264_profile=0,video_bitrate=1000000,h264_i_frame_period=10,repeat_sequence_header=1;\" ! "
"video/x-h264, level=(string)3, profile=baseline, stream-format=byte-stream ! filesink location=out.h264";
pPipeline = gst_parse_launch(pipelineStr, NULL);
printf("Pipeline: %s \n", pipelineStr);
#endif
pAppSrc = gst_bin_get_by_name(GST_BIN(pPipeline), "appsrc");
// ASSERT(pAppSrc != NULL);
g_object_set(pAppSrc, "format", GST_FORMAT_TIME, NULL);
if (pPipeline == NULL)
{
printf("Failed to init pipeline \n");
exit(-1);
}
/* Start playing */
gst_element_set_state(pPipeline, GST_STATE_PLAYING);
}
FILE *myfile = NULL;
void write_gst_data(char *buffer, size_t length)
{
GstBuffer *gstbuffer;
GstFlowReturn ret;
GstMapInfo map;
/* copy frame to gst buffer */
gstbuffer = gst_buffer_new_and_alloc(length);
gst_buffer_map(gstbuffer, &map, GST_MAP_WRITE);
memcpy(map.data, buffer, length);
gst_buffer_unmap(gstbuffer, &map);
/* push data to appsrc */
ret = gst_app_src_push_buffer(GST_APP_SRC(pAppSrc), gstbuffer);
if (ret != GST_FLOW_OK)
{
printf("-EINVAL GST_FLOW! \n");
}
}
int main(int argc, char *argv[])
{
init_pipeline();
while (true)
{
char *uyvybuffer = (char *)malloc(IMAGE_WIDTH * IMAGE_HEIGHT * 2);
FILE *fp;
fp = fopen("/dev/urandom", "r");
fread(uyvybuffer, 1, IMAGE_WIDTH * IMAGE_HEIGHT * 2, fp);
fclose(fp);
write_gst_data(uyvybuffer, IMAGE_WIDTH * IMAGE_HEIGHT * 2);
free(uyvybuffer);
// 25FPS -> 40ms
usleep(40 * 1000);
}
}
Build:
gcc -g main.c -I. -o decoder `pkg-config --cflags --libs gstreamer-1.0` `pkg-config --cflags --libs gstreamer-app-1.0`
For reference: https://github.com/raspberrypi/linux/issues/4906 https://forums.raspberrypi.com/viewtopic.php?p=1977561#p1977561