candroid-ndkgdkgdkpixbuf

Undefined Reference to gdk_pixbuf_save & gdk_pixbuf_new_from_data


I have the following code it is a modification of code found here. In conjunction with this tutorial here.

static void * snapshot_function(void *userdata){
    CustomData *data = (CustomData *) userdata;
    gint width, height;
    GstSample *sample;
    GError *error = NULL;
    GdkPixbuf *pixbuf;
    gint64 duration, position;
    GstStateChangeReturn ret;
    gboolean res;
    GstMapInfo map;

    /* Build pipeline */
    data->pipeline_snapshot = gst_parse_launch(pipeline_description_snapshot, &error);

    if (error) {
        gchar *message =
                g_strdup_printf("Unable to build pipeline: %s", error->message);
        g_clear_error(&error);
        set_ui_message(message, data);
        g_free(message);
        return NULL;
    }

    if (error != NULL) {
        g_print ("could not construct pipeline: %s\n", error->message);
        g_error_free (error);
        exit (-1);
    }

    /* get sink */
    data->snapshot_sink = gst_bin_get_by_name (GST_BIN (data->pipeline_snapshot), "sink");

    /* set to PAUSED to make the first frame arrive in the sink */
    ret = gst_element_set_state (data->pipeline_snapshot, GST_STATE_PAUSED);
    switch (ret) {
        case GST_STATE_CHANGE_FAILURE:
            g_print ("failed to play the file\n");
            exit (-1);
        case GST_STATE_CHANGE_NO_PREROLL:
            /* for live sources, we need to set the pipeline to PLAYING before we can
             * receive a buffer. We don't do that yet */
            g_print ("live sources not supported yet\n");
            exit (-1);
        default:
            break;
    }

    /* This can block for up to 5 seconds. If your machine is really overloaded,
 * it might time out before the pipeline prerolled and we generate an error. A
 * better way is to run a mainloop and catch errors there. */
    ret = gst_element_get_state (data->pipeline_snapshot, NULL, NULL, 5 * GST_SECOND);
    if (ret == GST_STATE_CHANGE_FAILURE) {
        g_print ("failed to play the file\n");
        exit (-1);
    }

    /* get the duration */
    gst_element_query_duration (data->pipeline_snapshot, GST_FORMAT_TIME, &duration);

    if (duration != -1)
        /* we have a duration, seek to 5% */
        position = duration * 5 / 100;
    else
        /* no duration, seek to 1 second, this could EOS */
        position = 1 * GST_SECOND;

    /* seek to the a position in the file. Most files have a black first frame so
     * by seeking to somewhere else we have a bigger chance of getting something
     * more interesting. An optimisation would be to detect black images and then
     * seek a little more */
    gst_element_seek_simple (data->pipeline_snapshot, GST_FORMAT_TIME,
                             GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_FLUSH, position);

    /* get the preroll buffer from appsink, this block untils appsink really
     * prerolls */
    g_signal_emit_by_name (data->snapshot_sink, "pull-preroll", &sample, NULL);
    gst_object_unref (data->snapshot_sink);

    /* if we have a buffer now, convert it to a pixbuf. It's possible that we
     * don't have a buffer because we went EOS right away or had an error. */
    if (sample) {
        GstBuffer *buffer;
        GdkPixbuf *pixbuf;
        GstCaps *caps;
        GstStructure *s;

        /* get the snapshot buffer format now. We set the caps on the appsink so
         * that it can only be an rgb buffer. The only thing we have not specified
         * on the caps is the height, which is dependent on the pixel-aspect-ratio
         * of the source material */
        caps = gst_sample_get_caps (sample);
        if (!caps) {
            g_print ("could not get snapshot format\n");
            exit (-1);
        }
        s = gst_caps_get_structure (caps, 0);

        /* we need to get the final caps on the buffer to get the size */
        res = gst_structure_get_int (s, "width", &width);
        res |= gst_structure_get_int (s, "height", &height);
        if (!res) {
            g_print ("could not get snapshot dimension\n");
            exit (-1);
        }

        /* create pixmap from buffer and save, gstreamer video buffers have a stride
         * that is rounded up to the nearest multiple of 4 */
        buffer = gst_sample_get_buffer (sample);
        gst_buffer_map (buffer, &map, GST_MAP_READ);
        pixbuf = gdk_pixbuf_new_from_data (map.data,GDK_COLORSPACE_RGB, FALSE, 8, width, height, GST_ROUND_UP_4 (width * 3), NULL, NULL);

        /* save the pixbuf */
        gdk_pixbuf_save (pixbuf, "snapshot.png", "png", &error, NULL);
        gst_buffer_unmap (buffer, &map);
    } else {
        g_print ("could not make snapshot");
    }

    /* cleanup and exit */
    gst_element_set_state (data->pipeline_snapshot, GST_STATE_NULL);
    gst_object_unref (data->pipeline_snapshot);

    exit (0);
}

However, when compiling I get the following linker error:

C:/Users/user1/AndroidStudioProjects/Project/app/jni/tutorial-3.c:344: undefined reference to `gdk_pixbuf_new_from_data'

C:/Users/user1/AndroidStudioProjects/Project/app/jni/tutorial-3.c:347: undefined reference to `gdk_pixbuf_save' clang++: error: linker command failed with exit code 1 (use -v to see invocation)

I have #include <gdk-pixbuf-2.0/gdk-pixbuf/gdk-pixbuf.h> at the top of my file, does anyone know what I could potentially be doing wrong?


Solution

  • You have problems with linking, so you need to look at your build script, it seems that you forgot to link the library. For example, if you use cmake, it will look like this:

    target_link_libraries(tutorial-3 gdk_pixbuf-2.0)