/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
   Copyright (C) 2015 Francois Gouget

   Compile with something like:
   gcc -o mjpeg-timestamp mjpeg-timestamp.c -pthread -I/usr/include/gstreamer-1.0 -I/usr/lib/x86_64-linux-gnu/gstreamer-1.0/include -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -lgstapp-1.0 -lgstbase-1.0 -lgstreamer-1.0 -lgobject-2.0 -lglib-2.0

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, see <<A HREF="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</A>>.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <gst/gst.h>
#include <gst/app/gstappsrc.h>
#include <gst/app/gstappsink.h>


/* ---------- Video characteristics ---------- */

int width = 320;
int height = 200;
int bpp = 32;
int framerate = 30;

gchar* options = "";


/* ---------- GStreamer pipeline ---------- */

GstElement *pipeline = NULL;
GstAppSrc *appsrc = NULL;
GstAppSink *appsink = NULL;

static void construct_pipeline(void)
{
    gchar *encoder;
    if (strstr(options, "vp8")) {
        encoder = "vp8enc lag-in-frames=0";
    } else if (strstr(options, "threads")) {
        encoder = "avenc_mjpeg";
    } else {
        encoder = "avenc_mjpeg max-threads=1";
    }

    GError *err = NULL;
    gchar *desc = g_strdup_printf("appsrc name=src format=2 do-timestamp=true ! videoconvert ! %s ! appsink name=sink", encoder);
    printf("%s\n", desc);
    pipeline = gst_parse_launch_full(desc, NULL, GST_PARSE_FLAG_FATAL_ERRORS, &err);
    if (!pipeline || err) {
        fprintf(stderr, "GStreamer error: %s\n", err->message);
        exit(1);
    }
    appsrc = GST_APP_SRC(gst_bin_get_by_name(GST_BIN(pipeline), "src"));
    appsink = GST_APP_SINK(gst_bin_get_by_name(GST_BIN(pipeline), "sink"));

    /* Set the source caps */
    GstCaps *src_caps = gst_caps_new_simple(
        "video/x-raw",
        "format", G_TYPE_STRING, "BGRA",
        "width", G_TYPE_INT, width,
        "height", G_TYPE_INT, height,
        "framerate", GST_TYPE_FRACTION, framerate, 1,
        NULL);
    g_object_set(G_OBJECT(appsrc), "caps", src_caps, NULL);

    /* Remove the clock... or not */
    if (strstr(options, "noclock")) {
        /* See https://bugzilla.gnome.org/show_bug.cgi?id=753257 */
        printf("removing the pipeline clock\n");
        gst_pipeline_use_clock(GST_PIPELINE(pipeline), NULL);
    }

    /* Start playing */
    fprintf(stderr, "setting state to PLAYING\n");
    if (gst_element_set_state(pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
        fprintf(stderr, "GStreamer error: unable to set the pipeline to the playing state\n");
        exit(1);
    }
}

int main(int argc, char **argv)
{
    int size = width * height * bpp / 8;
    gchar* bitmap = malloc(size);
    memset(bitmap, 0, size);

    if (argc > 1)
        options = argv[1];
    if (strstr(options, "help")) {
        printf("Usage: mjpeg-timestamp jpeg[,noclock][,threads]\n");
        printf("       mjpeg-timestamp vp8[,noclock]\n");
        printf("       mjpeg-timestamp help\n");
        exit(0);
    }

    gst_init(NULL, NULL);
    construct_pipeline();

    int f;
    for (f = 0; f < 40; f++) {
        printf("encoding frame %d\n", f);

        /* Push the bitmap */
        GstBuffer *buffer = gst_buffer_new_allocate(NULL, size, NULL);
        gst_buffer_fill(buffer, 0, bitmap, size);
        GST_BUFFER_OFFSET(buffer) = f;

        printf("  pushing...\n");
        GstFlowReturn ret = gst_app_src_push_buffer(appsrc, buffer);
        if (ret != GST_FLOW_OK) {
            fprintf(stderr, "GStreamer error: unable to push source buffer (%d)\n", ret);
            exit(1);
        }

        /* Pull the compressed buffer */
        printf("  pulling...\n");
        GstSample *sample = gst_app_sink_pull_sample(appsink);
        if (!sample) {
            fprintf(stderr, "sample is NULL!\n");
            exit(1);
        }
        gst_sample_unref(sample); /* and immediately throw it away */
    }

    printf("all good\n");
    return 0;
}
