#include <gst/gst.h>
#include <stdlib.h>
#include <stdio.h>
// to compile
// gcc -Wall -g  ./gst_dynamic_padding_expt.c -g $(pkg-config --cflags --libs gstreamer-1.0) -lgstapp-1.0
#include <gst/gst.h>
#include <glib.h>

#include <gst/app/gstappsrc.h>

#define APPSRC_PLUGIN_MAX_BYTES	      400000



static void transode_pipeline_appsrc_needs_data (GstElement * elt, guint unused_size, char *name)
 {
	 
	 guint8 inBuf[4096];
	 static FILE *inFp = NULL;
	 if(inFp  == NULL)
	    inFp = fopen(name, "rb");
	 if(!feof(inFp))
	 {
		 int inSize = fread(inBuf, sizeof(char), sizeof(inBuf), inFp);
		 int status = Write_data_to_transcode_session (elt, (gpointer) (inBuf), inSize);
	 }
	  
 } 
 

static gboolean
bus_call (GstBus     *bus,
          GstMessage *msg,
          gpointer    data)
{
  GMainLoop *loop = (GMainLoop *) data;

  switch (GST_MESSAGE_TYPE (msg)) {

    case GST_MESSAGE_EOS:
      g_print ("End of stream\n");
      g_main_loop_quit (loop);
      break;

    case GST_MESSAGE_ERROR: {
      gchar  *debug;
      GError *error;

      gst_message_parse_error (msg, &error, &debug);
      g_free (debug);

      g_printerr ("Error: %s\n", error->message);
      g_error_free (error);

      g_main_loop_quit (loop);
      break;
    }
    default:
      break;
  }

  return TRUE;
}


static void
on_pad_added (GstElement *element,
              GstPad     *pad,
              gpointer    data)
{
  GstPad *sinkpad;
  GstElement *decoder = (GstElement *) data;

  /* We can now link this pad with the vorbis-decoder sink pad */
  g_print ("Dynamic pad created, linking demuxer/decoder\n");

  sinkpad = gst_element_get_static_pad (decoder, "sink");

  gst_pad_link (pad, sinkpad);

  gst_object_unref (sinkpad);
}

 gint Write_data_to_transcode_session (GstElement *source, gpointer buffer, guint size)
 {
   GstBuffer *appsrc_buffer = NULL;
   GstFlowReturn retVal;
   gpointer data;
   if (buffer != NULL)
   {
	   GstMapInfo info;
      appsrc_buffer = gst_buffer_new_and_alloc (size);
      gst_buffer_map(appsrc_buffer, &info, GST_MAP_WRITE);
      data = info.data;
      memcpy(data, buffer, size);
      gst_buffer_unmap(appsrc_buffer, &info);
      
       /* Get app source in the transcode pipeline */
      retVal = gst_app_src_push_buffer (GST_APP_SRC (source), appsrc_buffer);
      if (G_UNLIKELY(retVal != GST_FLOW_OK))
      {
	  g_print ("Can't push the buffer to a transcode pipeline\n");
	  return -1;
      }	  
      
  }
  else
  {
      g_print ("Pushed EOS into a transcode engine\n");
      /* Push EOS into the transcode pipeline */
      gst_app_src_end_of_stream (GST_APP_SRC (source));
  }
   
   return TRUE;
 }
 




int
main (int   argc,
      char *argv[])
{
  GMainLoop *loop;
  GstCaps *appSrcCaps = NULL;

  GstElement *pipeline, *source, *demuxer, *decoder, *sink;
  GstBus *bus;
  guint bus_watch_id;

  /* Initialisation */
  gst_init (&argc, &argv);

  loop = g_main_loop_new (NULL, FALSE);

    
  /* Check input arguments */
  if (argc != 3) {
    g_printerr ("Usage: %s <input file> <output file>\n", argv[0]);
    return -1;
  }


  /* Create gstreamer elements */
  pipeline = gst_pipeline_new ("video-player");
  source   = gst_element_factory_make ("appsrc",       "app-source");
  demuxer  = gst_element_factory_make ("flvdemux",      "flv-demuxer");
  decoder  = gst_element_factory_make ("avdec_h264",     "h264-decoder");
  sink     = gst_element_factory_make ("filesink", "file-sink");

  if (!pipeline || !source || !demuxer || !decoder || !sink) {
    g_printerr ("One element could not be created. Exiting.\n");
    return -1;
  }

  /* Set up the pipeline */

  /* we set the input filename to the source element */
  //g_object_set (G_OBJECT (source), "location", argv[1], NULL);
  g_object_set (G_OBJECT (sink), "location", argv[2], NULL);

  /* we add a message handler */
  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
  bus_watch_id = gst_bus_add_watch (bus, bus_call, loop);
  gst_object_unref (bus);

  appSrcCaps = gst_caps_from_string("video/x-flv");
  gst_app_src_set_caps(GST_APP_SRC(source), appSrcCaps);
  gst_caps_unref (appSrcCaps);
  g_object_set ( G_OBJECT (source), "max-bytes", APPSRC_PLUGIN_MAX_BYTES, NULL);
  g_signal_connect (source, "need-data", G_CALLBACK (transode_pipeline_appsrc_needs_data), argv[1]);
  

  /* we add all elements into the pipeline */
  /* file-source | ogg-demuxer | vorbis-decoder | converter | alsa-output */
  gst_bin_add_many (GST_BIN (pipeline),
                    source, demuxer, decoder, sink, NULL);

  
  /* we link the elements together */
  /* file-source -> ogg-demuxer ~> vorbis-decoder -> converter -> alsa-output */
  gst_element_link (source, demuxer);
  gst_element_link (decoder, sink);
  g_signal_connect (demuxer, "pad-added", G_CALLBACK (on_pad_added), decoder);

  /* note that the demuxer will be linked to the decoder dynamically.
     The reason is that Ogg may contain various streams (for example
     audio and video). The source pad(s) will be created at run time,
     by the demuxer when it detects the amount and nature of streams.
     Therefore we connect a callback function which will be executed
     when the "pad-added" is emitted.*/


  /* Set the pipeline to "playing" state*/
  g_print ("Now playing: %s\n", argv[1]);
  gst_element_set_state (pipeline, GST_STATE_PLAYING);


  /* Iterate */
  g_print ("Running...\n");
  g_main_loop_run (loop);
  


  /* Out of the main loop, clean up nicely */
  g_print ("Returned, stopping playback\n");
  gst_element_set_state (pipeline, GST_STATE_NULL);

  g_print ("Deleting pipeline\n");
  gst_object_unref (GST_OBJECT (pipeline));
  g_source_remove (bus_watch_id);
  g_main_loop_unref (loop);

  return 0;
}
