2

I'm not too sure why my C program is leaking, it has something to do with the set_image_scaled() function I'm pretty sure and it has to do with me resizing an image. Basically once I take a picture with gphoto2, I save that file to a jpg, then I open that picture in GTK and resize it down with a pixbuf and then set the image widgets on the display accordingly. How can I fix this? I am lost. Been playing around with it for hours now. I am on raspberry pi on raspbian. Model 3b+.

This is the error it gives me:

(guiphoto:2344): GdkPixbuf-CRITICAL **: 13:57:26.656: gdk_pixbuf_scale_simple: assertion 'GDK_IS_PIXBUF (src)' failed
**
ERROR:guiphoto.c:94:set_image_scaled: assertion failed (err == NULL): Failed to load image “/media/pi/SD CARD/cam-1_1_b_back.jpg”: Insufficient memory to open TIFF file (gdk-pixbuf-error-quark, 1)
Aborted

The problematic code that keeps running into a memory leak is in the cam_main function, specifically:

if (front == 1)
    set_image_scaled(front_cams[i], buf);
else
    set_image_scaled(back_cams[i], buf);

So I believe it has to do with this code right here and the static variables at the top of my program:

void set_image_scaled(GtkWidget* img, const char* path) {
    pixbuf = gdk_pixbuf_new_from_file(path, &err);
    pxbscaled = gdk_pixbuf_scale_simple(pixbuf, 500, 300, GDK_INTERP_BILINEAR);
    gtk_image_set_from_pixbuf(GTK_IMAGE(img), pxbscaled);
    g_assert_no_error(err);
}

Here is my full code:

#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdarg.h>
#include <gphoto2/gphoto2-camera.h>
#include <gphoto2/gphoto2-context.h>
#include <wiringPi.h>
#include <gtk/gtk.h>
#include <pthread.h>

//program
static int running = 1;
//libgphoto2
static CameraList* list;
static Camera** cams;
static GPContext* context;
static const char *name, *value;
static int ret, count;
static int pic = 0;
static int front = 1;
//GTK
static GtkWidget *window, *hbox, *hbox2, *vbox, *button1;
static GtkWidget *front_cams[3];
static GtkWidget *back_cams[3];
static GdkPixbuf *pxbscaled;
static GdkPixbuf *pixbuf;
static GError* err = NULL;

static void button_clicked(GtkWidget *widget, gpointer data) {
    gtk_button_set_label(GTK_BUTTON(button1), "click me 2"); //change button text
}

void *main_gtk() {
    //INIT GTK
    gtk_init(NULL, NULL);

    //SETUP WINDOW
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    //button1=gtk_button_new_with_label("Click me");
    //gtk_button_set_label(GTK_BUTTON(button1), "click me 1");
    g_signal_connect(window,"delete-event", G_CALLBACK(gtk_main_quit), NULL);
    //g_signal_connect(button1, "clicked", G_CALLBACK(button_clicked), NULL);

    //SET WINDOW SIZE AND TITLE
    //gtk_widget_set_size_request(window, 600, 400);
    gtk_window_set_title(GTK_WINDOW(window), "CaptureGui");

    //RESIZE IMAGES
    //image
    front_cams[0] = gtk_image_new();
    front_cams[1] = gtk_image_new();
    front_cams[2] = gtk_image_new();
    back_cams[0] = gtk_image_new();
    back_cams[1] = gtk_image_new();
    back_cams[2] = gtk_image_new();

    //g_print("1");

    //set_image_scaled(front_cams[0], "/media/pi/SD CARD/cam-1_1_a_front.jpg");
    //set_image_scaled(front_cams[1], "/media/pi/SD CARD/cam-1_1_a_front.jpg");
    //set_image_scaled(front_cams[2], "/media/pi/SD CARD/cam-1_1_b_back.jpg");

    //PACK
    hbox = gtk_hbox_new(0, 0);
    gtk_box_pack_start(GTK_BOX(hbox), front_cams[0], 0, 0, 0);
    gtk_box_pack_start(GTK_BOX(hbox), front_cams[1], 0, 0, 0);
    gtk_box_pack_start(GTK_BOX(hbox), front_cams[2], 0, 0, 0);
    gtk_box_pack_start(GTK_BOX(hbox), back_cams[0], 0, 0, 0);
    gtk_box_pack_start(GTK_BOX(hbox), back_cams[1], 0, 0, 0);
    gtk_box_pack_start(GTK_BOX(hbox), back_cams[2], 0, 0, 0);
    gtk_container_add(GTK_CONTAINER(window), hbox);

    //ADD ELEMENTS TO GUI
    //gtk_container_add(GTK_CONTAINER(window), image1);
    //gtk_container_add(GTK_CONTAINER(window), image2);
    //image2 = gtk_image_new_from_file("/media/pi/SD CARD/cam-1_0_b_back.jpg");
    //gtk_container_add(GTK_CONTAINER(window), image2);

    //SHOW GUI
    gtk_widget_show_all(window);
    gtk_main();
}

void set_image_scaled(GtkWidget *img, const char *path) {
    pixbuf = gdk_pixbuf_new_from_file(path, &err);
    pxbscaled = gdk_pixbuf_scale_simple(pixbuf, 500, 300, GDK_INTERP_BILINEAR);
    gtk_image_set_from_pixbuf(GTK_IMAGE(img), pxbscaled);
    g_assert_no_error(err);
}

void *cam_main() {
    while (running == 1) {
        if (digitalRead(4) == 0) {
            printf("taking pics of %s item %i\n", (front == 1) ? "front" : "back", pic);
            for (int i = 0; i < count; i++) {
                int fd, retval;
                CameraFile *file;
                CameraFilePath cfPath;
                strcpy(cfPath.folder, "/");
                strcpy(cfPath.name, "foo1.jpg");
                printf("Capturing cam%i...\n", i + 1);
                int res = gp_camera_capture(cams[i], GP_CAPTURE_IMAGE, &cfPath, context);
                //printf(gp_port_result_as_string(res));
                printf("capture result: %i\n", res);
                //Camera won't take pic if busy and will continue to program end
                char buf[256];
                snprintf(buf, sizeof(buf), "/media/pi/SD CARD/cam-%i_%i_%s.jpg", i + 1, pic, (front == 1) ? "a_front" : "b_back"); //a_ to make front come before back otherwise systems will order incorrectly
                fd = open(buf, O_CREAT | O_WRONLY, 0644);
                retval = gp_file_new_from_fd(&file, fd);
                retval = gp_camera_file_get(cams[i], cfPath.folder, cfPath.name, GP_FILE_TYPE_NORMAL, file, context);
                retval = gp_camera_file_delete(cams[i], cfPath.folder, cfPath.name, context);
                gp_file_free(file);

                if (front == 1)
                    set_image_scaled(front_cams[i], buf);
                else
                    set_image_scaled(back_cams[i], buf);
            }
            if (front == 1)
                front = 0;
            else {
                front = 1;
                pic += 1;
            }
            printf("pics taken...\n");
        }
    }
}

int main(int argc, char **argv) {
    //Kill any processes using cams
    system("pkill -f gphoto2");

    //main_gtk();

    //Wiring pi init
    wiringPiSetupGpio();

    //Init
    context = gp_context_new();

    detect_cams();

    pthread_t logic_thread_handle, gui_thread_handle;
    pthread_create(&logic_thread_handle, NULL, cam_main, NULL);
    pthread_create(&gui_thread_handle, NULL, main_gtk, NULL);
    pthread_join(gui_thread_handle, 0);
    pthread_join(logic_thread_handle, 0);

    //Deinit
    for (int i = 0; i < count; i++) {
        gp_camera_exit(cams[i], context);
        gp_camera_free(cams[i]);
    }
    return 0;
}

void detect_cams() {
    //Detecting all cameras and loading them into mem
        //Detecting all cameras
    ret = gp_list_new(&list);
    if (ret < GP_OK) return 1;
    gp_list_reset(list);
    count = gp_camera_autodetect(list, context);
    if (count < 1) {
        printf("No cameras detected.\n");
        return 1;
    }

    //Open all cameras
    printf("Number of cameras: %d\n", count);
    cams = calloc(sizeof (Camera*), count);
    for (int i = 0; i < count; i++) {
        gp_list_get_name(list, i, &name);
        gp_list_get_value(list, i, &value);
        ret = open_cam(&cams[i], name, value, context);
        if (ret < GP_OK)
            fprintf(stderr, "Camera %s on port %s failed to open\n", name, value);
    }
}

int open_cam(Camera ** camera, const char *model, const char *port, GPContext *context) {
    GPPortInfoList      *portinfolist = NULL;
    CameraAbilitiesList *abilities = NULL;
    int     ret, m, p;
    CameraAbilities a;
    GPPortInfo  pi;

    ret = gp_camera_new (camera);
    if (ret < GP_OK) return ret;

    if (!abilities) {
        /* Load all the camera drivers we have... */
        ret = gp_abilities_list_new (&abilities);
        if (ret < GP_OK) return ret;
        ret = gp_abilities_list_load (abilities, context);
        if (ret < GP_OK) return ret;
    }

    /* First lookup the model / driver */
        m = gp_abilities_list_lookup_model (abilities, model);
    if (m < GP_OK) return ret;
        ret = gp_abilities_list_get_abilities (abilities, m, &a);
    if (ret < GP_OK) return ret;
        ret = gp_camera_set_abilities (*camera, a);
    if (ret < GP_OK) return ret;

    if (!portinfolist) {
        /* Load all the port drivers we have... */
        ret = gp_port_info_list_new (&portinfolist);
        if (ret < GP_OK) return ret;
        ret = gp_port_info_list_load (portinfolist);
        if (ret < 0) return ret;
        ret = gp_port_info_list_count (portinfolist);
        if (ret < 0) return ret;
    }

    /* Then associate the camera with the specified port */
    p = gp_port_info_list_lookup_path (portinfolist, port);
    switch (p) {
      case GP_ERROR_UNKNOWN_PORT:
        fprintf(stderr, "The port you specified "
               "('%s') can not be found. Please "
               "specify one of the ports found by "
               "'gphoto2 --list-ports' and make "
               "sure the spelling is correct "
               "(i.e. with prefix 'serial:' or 'usb:').",
               port);
        break;
      default:
        break;
    }
    if (p < GP_OK) return p;

    ret = gp_port_info_list_get_info (portinfolist, p, &pi);
    if (ret < GP_OK) return ret;
    ret = gp_camera_set_port_info (*camera, pi);
    if (ret < GP_OK) return ret;
    return GP_OK;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
username3630
  • 111
  • 1
  • 7
  • The error message doesn't indicate a memory leak. What makes you think that a memory leak is causing the problem? – user3386109 Mar 20 '20 at 18:39
  • @user3386109 It says "insufficient memory can't open picture" also if you google exited with 137 it says its a memory error – username3630 Mar 20 '20 at 18:41
  • 1
    Having insufficient memory is different than having a memory leak. How big is the image? How much memory does the raspberry pi have? – user3386109 Mar 20 '20 at 18:43
  • BTW, if you search this page for 137, it only appears twice -- here in this comment and in your comment. So if you have more information about the problem, you should share that. – user3386109 Mar 20 '20 at 18:45
  • @user3386109 the images are all the same size, 1920x1080, it will load perfectly fine the first 7 images, but after 7 images or so it throws this error. I am basically rewriting over the past image that was taken to display the new image but the error seems to say it doesnt have enough memory, which means something is eating all my memory when I keep calling this function, which means memory must be leaking. The function only writes the new JPG data to the existing PixBuf and then Image. I am not allocating any new memory. – username3630 Mar 20 '20 at 18:46
  • Yes my bad it says the program exited with error 137 – username3630 Mar 20 '20 at 18:46
  • On a desktop system, I would recommend using [valgrind](https://stackoverflow.com/tags/valgrind/info) to pinpoint the error. Not sure if you can run valgrind on a raspberry pi. – user3386109 Mar 20 '20 at 18:48

1 Answers1

2

Probably a lot of memory is occupied by unreleased GdkPixbufs.

Try this:

void set_image_scaled(GtkWidget *img, const char *path) {
    pixbuf = gdk_pixbuf_new_from_file(path, &err);
    g_assert_no_error(err);
    pxbscaled = gdk_pixbuf_scale_simple(pixbuf, 500, 300, GDK_INTERP_BILINEAR);
    gtk_image_set_from_pixbuf(GTK_IMAGE(img), pxbscaled);
    g_object_unref(pixbuf);
    g_object_unref(pxbscaled);
}

Reference Counting and Memory Mangement

Damian
  • 261
  • 1
  • 2