Log In
New Account
Home My Page Project Cloud Code Snippets Project Openings

Browse | Submit A New Snippet | Create A Package


Car Video Recorder

GNU General Public License
Car Video Recorder for NOKIA N900
This is an function testing code.
Media framework depending on gstreamer.
Extra depend package : gstreamer0.10-plugins-base-subtitles.

1.Record H.264 video to /home/user/MyDocs/r.avi
2.Speed from GPS
3.Record with speed


Versions Of This Snippet::

Chang Tai Chang
Snippet ID Download Version Date Posted Author Delete
13alpha2010-07-08 14:32Chang Tai Chang

Download a raw-text version of this code by clicking on "Download Version"


Latest Snippet Version: :alpha

/* carvideo.c
 * (C) 2010 Steve Chang <stevegigijoe@yahoo.com.tw>
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * GNU General Public License for more details.

#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <gst/gst.h>
#include <gst/interfaces/xoverlay.h>

#include <hildon/hildon-banner.h>
#include <hildon/hildon-program.h>

/* Define sources and sinks according to
 * running environment
 * NOTE: If you want to run the application
 * in ARM scratchbox, you have to change these*/
#ifdef __arm__
/* The device by default supports only
 * vl4l2src for camera and xvimagesink
 * for screen */
#define VIDEO_SRC "v4l2camsrc"
#define VIDEO_SINK "xvimagesink"
/* These are for the X86 SDK. Xephyr doesn't
 * support XVideo extension, so the application
 * must use ximagesink. The video source depends
 * on driver of your Video4Linux device so this
 * may have to be changed */
#define VIDEO_SRC "videotestsrc"
#define VIDEO_SINK "ximagesink"

/* Define structure for variables that
 * are needed thruout the application */
typedef struct
  HildonProgram *program;
  HildonWindow *window;

  GstElement *pipeline;
  GtkWidget *screen;
  guint buffer_cb_id;
} CarVideoApp;

static float speed = 0.0f;

/* Callback that gets called when user clicks the "Take photo" button */
static void record(GtkWidget *widget, CarVideoApp *app)
  static gboolean rec = FALSE;
  rec = rec ? FALSE : TRUE;
  /* Display a note to the user */
  if(rec) {
    GstElement *overlay = gst_bin_get_by_name(GST_BIN(app->pipeline), "overlay");
    g_object_set(G_OBJECT(overlay), "text", "0.00 km/h", NULL);
    speed = 0.0f;
    gst_element_set_state(app->pipeline, GST_STATE_PLAYING);
    hildon_banner_show_information(GTK_WIDGET(app->window), NULL, "Start Record");
  } else  {    
    gst_element_set_state(app->pipeline, GST_STATE_PAUSED);    
    hildon_banner_show_information(GTK_WIDGET(app->window), NULL, "Stop Record");

/* Callback that gets called whenever pipeline's message bus has
 * a message */
static void bus_callback(GstBus *bus, GstMessage *message, CarVideoApp *app)
  gchar *message_str;
  const gchar *message_name;
  GError *error;
  /* Report errors to the console */
    gst_message_parse_error(message, &error, &message_str);
    g_error("GST error: %s\n", message_str);
  /* Report warnings to the console */
    gst_message_parse_warning(message, &error, &message_str);
    g_warning("GST warning: %s\n", message_str);

  /* See if the message type is GST_MESSAGE_APPLICATION which means
   * thet the message is sent by the client code (this program) and
   * not by gstreamer. */
    /* Get name of the message's structure */
    message_name = gst_structure_get_name(gst_message_get_structure(message));
    /* The hildon banner must be shown in here, because the bus callback is
     * called in the main thread and calling GUI-functions in gstreamer threads
     * usually leads to problems with X-server */
#if 0    
    /* "photo-taken" message means that the photo was succefully taken
     * and saved and message is shown to user */
    if(!strcmp(message_name, "photo-taken"))
          NULL, "Photo taken");
    /* "photo-failed" means that the photo couldn't be captured or saved */ 
    if(!strcmp(message_name, "photo-failed"))
           "Saving photo failed");

/* Callback to be called when the screen-widget is exposed */
static gboolean expose_cb(GtkWidget * widget, GdkEventExpose * event, gpointer data)

  /* Tell the xvimagesink/ximagesink the x-window-id of the screen
   * widget in which the video is shown. After this the video
   * is shown in the correct widget */
  return FALSE;

/* Initialize the the Gstreamer pipeline. Below is a diagram
 * of the pipeline that will be created:
 *                   |Screen|  |Screen|
 *                 ->|queue |->|sink  |-> Display
 * |Camera|  |Tee|/
 * |src   |->|   |\  |Video|   |Video  |  |AVI  |  |file|
 *                 ->|queue|-> |encoder|->|muxer|->|sink| avi file
static gboolean initialize_pipeline(CarVideoApp *app,
    int *argc, char ***argv)
  GstElement *pipeline, *camera_src, *screen_sink, *video_encoder, *video_muxer, *file_sink;
  GstElement *screen_queue, *video_queue;
  GstElement *tee, *overlay;
  GstCaps *caps;
  GstBus *bus;

  /* Initialize Gstreamer */
  gst_init(argc, argv);
  /* Create pipeline and attach a callback to it's
   * message bus */
  pipeline = gst_pipeline_new("car-video");

  bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
  gst_bus_add_watch(bus, (GstBusFunc)bus_callback, app);
  /* Save pipeline to the CarVideoApp structure */
  app->pipeline = pipeline;
  /* Create elements */
  /* Camera video stream comes from a Video4Linux driver */
  camera_src = gst_element_factory_make(VIDEO_SRC, "camera_src");
  g_object_set(G_OBJECT(camera_src), "device", "/dev/video0", NULL);
  g_object_set(G_OBJECT(camera_src), "capture-mode", "2", NULL);

  overlay = gst_element_factory_make("textoverlay", "overlay");
  g_object_set(G_OBJECT(overlay), "font-desc", "Nokia Sans 30", NULL);
  g_object_set(G_OBJECT(overlay), "valign", "top", NULL);
  g_object_set(G_OBJECT(overlay), "halign", "left", NULL);
  g_object_set(G_OBJECT(overlay), "text", "Car Video", NULL);
  /* Tee that copies the stream to multiple outputs */
  tee = gst_element_factory_make("tee", "tee");
  /* Queue creates new thread for the stream */
  screen_queue = gst_element_factory_make("queue", "screen_queue");
  /* Sink that shows the image on screen. Xephyr doesn't support XVideo
   * extension, so it needs to use ximagesink, but the device uses
   * xvimagesink */
  screen_sink = gst_element_factory_make(VIDEO_SINK, "screen_sink");
  /* Creates separate thread for the stream from which the video
   * is captured */
  video_queue = gst_element_factory_make("queue", "video_queue");
  video_encoder = gst_element_factory_make("dsph264enc", "video_encoder");
  video_muxer = gst_element_factory_make("avimux", "video_muxer");
  file_sink = gst_element_factory_make("filesink", "file_sink");
  g_object_set(G_OBJECT(file_sink), "location", "/home/user/MyDocs/r.avi", NULL);
  /* Check that elements are correctly initialized */
  if(!(pipeline && camera_src && screen_sink && video_encoder && 
    video_muxer && file_sink && screen_queue && video_queue && tee && overlay))  {
    g_critical("Couldn't create pipeline elements");
    return FALSE;
  /* Add elements to the pipeline. This has to be done prior to
   * linking them */
  gst_bin_add_many(GST_BIN(pipeline), camera_src, overlay,
      tee, screen_queue, screen_sink, video_queue,
      video_encoder, video_muxer, file_sink, NULL);
  /* Specify what kind of video is wanted from the camera */
  caps = gst_caps_new_simple("video/x-raw-yuv",
      "format",  GST_TYPE_FOURCC, GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'),
      "width", G_TYPE_INT, 640,
      "height", G_TYPE_INT, 480,
      "bpp", G_TYPE_INT, 24,
      "framerate", GST_TYPE_FRACTION, 30, 1,

  /* Link the camera source and tee using capabilities
   * specified */
  if(!gst_element_link_filtered(camera_src, overlay, caps))
    return FALSE;

  if(!gst_element_link_many(overlay, tee, NULL))
    return FALSE;
  /* Connect Tee -> Screen Queue -> Screen Sink
   * This finalizes the initialization of the screen-part of the pipeline */
  if(!gst_element_link_many(tee, screen_queue, screen_sink, NULL))
    return FALSE;

  if(!gst_element_link_many(tee, video_queue, video_encoder, video_muxer, file_sink, NULL)) 
    return FALSE;

  /* As soon as screen is exposed, window ID will be advised to the sink */
  g_signal_connect(app->screen, "expose-event", G_CALLBACK(expose_cb), screen_sink);

//  gst_element_set_state(pipeline, GST_STATE_PLAYING);

  return TRUE;

/* Destroy the pipeline on exit */
static void destroy_pipeline(GtkWidget *widget, CarVideoApp *app)
  /* Free the pipeline. This automatically also unrefs all elements
   * added to the pipeline */
  gst_element_set_state(app->pipeline, GST_STATE_NULL);

#include <location/location-gps-device.h>
#include <location/location-gpsd-control.h>

static void gps_changed_cb(LocationGPSDevice *device, gpointer data)

  if(device->fix) {
    if(device->fix->fields & LOCATION_GPS_DEVICE_SPEED_SET && !isnan(device->fix->speed))
      speed = device->fix->speed;

static gboolean gps_poll(CarVideoApp *app)
  static float _speed = 0;
  printf("speed = %f\n", speed);
  if(speed != _speed) {
    char str[32];
    snprintf(str, 32, "%.2f km/h", speed);
    GstElement *overlay = gst_bin_get_by_name(GST_BIN(app->pipeline), "overlay");
    g_object_set(G_OBJECT(overlay), "text", str, NULL);
    _speed = speed;
  return TRUE;

int main(int argc, char **argv)
  CarVideoApp cvapp;
  GtkWidget *button, *hbox, *vbox_button, *vbox;

  /* Initialize GTK+ */
  gtk_init(&argc, &argv);

  /* Create HildonProgram and set application name */
  cvapp.program = HILDON_PROGRAM(hildon_program_get_instance());
  g_set_application_name("Car Video");

  /* Create the toplevel HildonWindow */
  cvapp.window = HILDON_WINDOW(hildon_window_new());

  /* Connect destroying of the main window to gtk_main_quit */
  g_signal_connect(G_OBJECT(cvapp.window), "delete_event",
      G_CALLBACK(gtk_main_quit), NULL);
  vbox = gtk_vbox_new(FALSE, 0);
  hbox = gtk_hbox_new(FALSE, 0);
  vbox_button = gtk_vbox_new(FALSE, 0);

  gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(hbox), vbox_button, FALSE, FALSE, 0);

  cvapp.screen = gtk_drawing_area_new();
  gtk_widget_set_size_request(cvapp.screen, 640, 480);
  gtk_box_pack_start(GTK_BOX(vbox), cvapp.screen, FALSE, FALSE, 0);

  button = gtk_button_new_with_label("Record");
  gtk_widget_set_size_request(button, 120, 240);
  gtk_box_pack_start(GTK_BOX(vbox_button), button, FALSE, FALSE, 0);
  g_signal_connect(G_OBJECT(button), "clicked",
       G_CALLBACK(record), &cvapp);

  gtk_container_add(GTK_CONTAINER(cvapp.window), hbox);

  /* Initialize the GTK pipeline */
  if(!initialize_pipeline(&cvapp, &argc, &argv))
        "Failed to initialize pipeline");

* GPS Function

  LocationGPSDControl *gps_control;
  LocationGPSDevice *gps_device;
  gps_control = location_gpsd_control_get_default();
  gps_device = g_object_new(LOCATION_TYPE_GPS_DEVICE, NULL);

    "preferred-method", LOCATION_METHOD_USER_SELECTED,
    "preferred-interval", LOCATION_INTERVAL_5S,

  g_signal_connect(gps_device, "changed", G_CALLBACK(gps_changed_cb), gps_control);

  g_timeout_add(5000, (GSourceFunc)gps_poll, &cvapp);
  printf("Car Video ...\n");

  g_signal_connect(G_OBJECT(cvapp.window), "destroy",
      G_CALLBACK(destroy_pipeline), &cvapp);

  /* Show the window and widgets it contains
   * and go to the main loop. */
  /* Free the gstreamer resources. Elements added
   * to the pipeline will be freed automatically */
  return 0;


Submit a new version

You can submit a new version of this snippet if you have modified it and you feel it is appropriate to share with others..

Terms of Use    Privacy Policy    Contribution Guidelines    Feedback

Powered By GForge Collaborative Development Environment