gnome-shell-3.6.3.1/src/shell-recorder.c

No issues found

   1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
   2 
   3 #include "config.h"
   4 
   5 #include <fcntl.h>
   6 #include <math.h>
   7 #include <stdio.h>
   8 #include <string.h>
   9 #include <unistd.h>
  10 
  11 #define GST_USE_UNSTABLE_API
  12 #include <gst/gst.h>
  13 
  14 #include "shell-recorder-src.h"
  15 #include "shell-recorder.h"
  16 #include "shell-screen-grabber.h"
  17 
  18 #include <clutter/x11/clutter-x11.h>
  19 #include <X11/extensions/Xfixes.h>
  20 
  21 typedef enum {
  22   RECORDER_STATE_CLOSED,
  23   RECORDER_STATE_RECORDING
  24 } RecorderState;
  25 
  26 typedef struct _RecorderPipeline RecorderPipeline;
  27 
  28 struct _ShellRecorderClass
  29 {
  30   GObjectClass parent_class;
  31 };
  32 
  33 struct _ShellRecorder {
  34   GObject parent;
  35 
  36   /* A "maximum" amount of memory to use for buffering. This is used
  37    * to alert the user that they are filling up memory rather than
  38    * any that actually affects recording. (In kB)
  39    */
  40   guint memory_target;
  41   guint memory_used; /* Current memory used. (In kB) */
  42 
  43   RecorderState state;
  44 
  45   ClutterStage *stage;
  46   int stage_width;
  47   int stage_height;
  48 
  49   ShellScreenGrabber *grabber;
  50 
  51   gboolean have_pointer;
  52   int pointer_x;
  53   int pointer_y;
  54 
  55   gboolean have_xfixes;
  56   int xfixes_event_base;
  57 
  58   CoglHandle recording_icon; /* icon shown while playing */
  59 
  60   cairo_surface_t *cursor_image;
  61   int cursor_hot_x;
  62   int cursor_hot_y;
  63 
  64   gboolean only_paint; /* Used to temporarily suppress recording */
  65 
  66   int framerate;
  67   char *pipeline_description;
  68   char *filename;
  69 
  70   /* We might have multiple pipelines that are finishing encoding
  71    * to go along with the current pipeline where we are recording.
  72    */
  73   RecorderPipeline *current_pipeline; /* current pipeline */
  74   GSList *pipelines; /* all pipelines */
  75 
  76   GstClockTime start_time; /* When we started recording */
  77   GstClockTime last_frame_time; /* Timestamp for the last frame */
  78 
  79   /* GSource IDs for different timeouts and idles */
  80   guint redraw_timeout;
  81   guint redraw_idle;
  82   guint update_memory_used_timeout;
  83   guint update_pointer_timeout;
  84   guint repaint_hook_id;
  85 };
  86 
  87 struct _RecorderPipeline
  88 {
  89   ShellRecorder *recorder;
  90   GstElement *pipeline;
  91   GstElement *src;
  92   int outfile;
  93 };
  94 
  95 static void recorder_set_stage    (ShellRecorder *recorder,
  96                                    ClutterStage  *stage);
  97 static void recorder_set_framerate (ShellRecorder *recorder,
  98                                     int framerate);
  99 static void recorder_set_pipeline (ShellRecorder *recorder,
 100                                    const char    *pipeline);
 101 static void recorder_set_filename (ShellRecorder *recorder,
 102                                    const char    *filename);
 103 
 104 static void recorder_pipeline_set_caps (RecorderPipeline *pipeline);
 105 static void recorder_pipeline_closed   (RecorderPipeline *pipeline);
 106 
 107 enum {
 108   PROP_0,
 109   PROP_STAGE,
 110   PROP_FRAMERATE,
 111   PROP_PIPELINE,
 112   PROP_FILENAME
 113 };
 114 
 115 G_DEFINE_TYPE(ShellRecorder, shell_recorder, G_TYPE_OBJECT);
 116 
 117 /* The default value of the target frame rate; we'll never record more
 118  * than this many frames per second, though we may record less if the
 119  * screen isn't being redrawn. 30 is a compromise between smoothness
 120  * and the size of the recording.
 121  */
 122 #define DEFAULT_FRAMES_PER_SECOND 30
 123 
 124 /* The time (in milliseconds) between querying the server for the cursor
 125  * position.
 126  */
 127 #define UPDATE_POINTER_TIME 100
 128 
 129 /* The time we wait (in milliseconds) before redrawing when the memory used
 130  * changes.
 131  */
 132 #define UPDATE_MEMORY_USED_DELAY 500
 133 
 134 /* Maximum time between frames, in milliseconds. If we don't send data
 135  * for a long period of time, then when we send the next frame, a lot
 136  * of work can be created for the encoder to do, so we want to force a
 137  * periodic redraw when nothing happen.
 138  */
 139 #define MAXIMUM_PAUSE_TIME 1000
 140 
 141 /* The default pipeline. videorate is used to give a constant stream of
 142  * frames to theora even if there is a pause because nothing is moving.
 143  * (Theora does have some support for frames at non-uniform times, but
 144  * things seem to break down if there are large gaps.)
 145  */
 146 #define DEFAULT_PIPELINE "vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux"
 147 
 148 /* If we can find the amount of memory on the machine, we use half
 149  * of that for memory_target, otherwise, we use this value, in kB.
 150  */
 151 #define DEFAULT_MEMORY_TARGET (512*1024)
 152 
 153 /* Create an emblem to show at the lower-left corner of the stage while
 154  * recording. The emblem is drawn *after* we record the frame so doesn't
 155  * show up in the frame.
 156  */
 157 static CoglHandle
 158 create_recording_icon (void)
 159 {
 160   cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 32, 32);
 161   cairo_t *cr;
 162   cairo_pattern_t *pat;
 163   CoglHandle texture;
 164 
 165   cr = cairo_create (surface);
 166 
 167   /* clear to transparent */
 168   cairo_save (cr);
 169   cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
 170   cairo_paint (cr);
 171   cairo_restore (cr);
 172 
 173   /* radial "glow" */
 174   pat = cairo_pattern_create_radial (16, 16, 6,
 175                                      16, 16, 14);
 176   cairo_pattern_add_color_stop_rgba (pat, 0.0,
 177                                      1, 0, 0, 1); /* opaque red */
 178   cairo_pattern_add_color_stop_rgba (pat, 1.0,
 179                                      1, 0, 0, 0); /* transparent red */
 180 
 181   cairo_set_source (cr, pat);
 182   cairo_paint (cr);
 183   cairo_pattern_destroy (pat);
 184 
 185   /* red circle */
 186   cairo_arc (cr, 16, 16, 8,
 187              0, 2 * M_PI);
 188   cairo_set_source_rgb (cr, 1, 0, 0);
 189   cairo_fill (cr);
 190 
 191   cairo_destroy (cr);
 192 
 193   texture = cogl_texture_new_from_data (32, 32,
 194                                         COGL_TEXTURE_NONE,
 195                                         CLUTTER_CAIRO_FORMAT_ARGB32,
 196                                         COGL_PIXEL_FORMAT_ANY,
 197                                         cairo_image_surface_get_stride (surface),
 198                                         cairo_image_surface_get_data (surface));
 199   cairo_surface_destroy (surface);
 200 
 201   return texture;
 202 }
 203 
 204 static guint
 205 get_memory_target (void)
 206 {
 207   FILE *f;
 208 
 209   /* Really simple "get amount of memory on the machine" if it
 210    * doesn't work, you just get the default memory target.
 211    */
 212   f = fopen("/proc/meminfo", "r");
 213   if (!f)
 214     return DEFAULT_MEMORY_TARGET;
 215 
 216   while (!feof(f))
 217     {
 218       gchar line_buffer[1024];
 219       guint mem_total;
 220       if (fscanf(f, "MemTotal: %u", &mem_total) == 1)
 221         {
 222           fclose(f);
 223           return mem_total / 2;
 224         }
 225       /* Skip to the next line and discard what we read */
 226       if (fgets(line_buffer, sizeof(line_buffer), f) == NULL)
 227         break;
 228     }
 229 
 230   fclose(f);
 231 
 232   return DEFAULT_MEMORY_TARGET;
 233 }
 234 
 235 /*
 236  * Used to force full stage redraws during recording to avoid artifacts
 237  *
 238  * Note: That this will cause the stage to be repainted on
 239  * every animation frame even if the frame wouldn't normally cause any new
 240  * drawing
 241  */
 242 static gboolean
 243 recorder_repaint_hook (gpointer data)
 244 {
 245   ClutterActor *stage = data;
 246   clutter_actor_queue_redraw (stage);
 247 
 248   return TRUE;
 249 }
 250 
 251 static void
 252 shell_recorder_init (ShellRecorder *recorder)
 253 {
 254   /* Calling gst_init() is a no-op if GStreamer was previously initialized */
 255   gst_init (NULL, NULL);
 256 
 257   shell_recorder_src_register ();
 258 
 259   recorder->recording_icon = create_recording_icon ();
 260   recorder->memory_target = get_memory_target();
 261 
 262   recorder->grabber = shell_screen_grabber_new ();
 263 
 264   recorder->state = RECORDER_STATE_CLOSED;
 265   recorder->framerate = DEFAULT_FRAMES_PER_SECOND;
 266 }
 267 
 268 static void
 269 shell_recorder_finalize (GObject  *object)
 270 {
 271   ShellRecorder *recorder = SHELL_RECORDER (object);
 272   GSList *l;
 273 
 274   for (l = recorder->pipelines; l; l = l->next)
 275     {
 276       RecorderPipeline *pipeline = l->data;
 277 
 278       /* Remove the back-reference. The pipeline will be freed
 279        * when it finishes. (Or when the process exits, but that's
 280        * out of our control.)
 281        */
 282       pipeline->recorder = NULL;
 283     }
 284 
 285   if (recorder->update_memory_used_timeout)
 286     g_source_remove (recorder->update_memory_used_timeout);
 287 
 288   if (recorder->cursor_image)
 289     cairo_surface_destroy (recorder->cursor_image);
 290 
 291   recorder_set_stage (recorder, NULL);
 292   recorder_set_pipeline (recorder, NULL);
 293   recorder_set_filename (recorder, NULL);
 294 
 295   g_object_unref (recorder->grabber);
 296 
 297   cogl_handle_unref (recorder->recording_icon);
 298 
 299   G_OBJECT_CLASS (shell_recorder_parent_class)->finalize (object);
 300 }
 301 
 302 static void
 303 recorder_on_stage_destroy (ClutterActor  *actor,
 304                            ShellRecorder *recorder)
 305 {
 306   recorder_set_stage (recorder, NULL);
 307 }
 308 
 309 /* Add together the memory used by all pipelines; both the
 310  * currently recording pipeline and pipelines finishing
 311  * recording asynchronously.
 312  */
 313 static void
 314 recorder_update_memory_used (ShellRecorder *recorder,
 315                              gboolean       repaint)
 316 {
 317   guint memory_used = 0;
 318   GSList *l;
 319 
 320   for (l = recorder->pipelines; l; l = l->next)
 321     {
 322       RecorderPipeline *pipeline = l->data;
 323       guint pipeline_memory_used;
 324 
 325       g_object_get (pipeline->src,
 326                     "memory-used", &pipeline_memory_used,
 327                     NULL);
 328       memory_used += pipeline_memory_used;
 329     }
 330 
 331   if (memory_used != recorder->memory_used)
 332     {
 333       recorder->memory_used = memory_used;
 334       if (repaint)
 335         {
 336           /* In other cases we just queue a redraw even if we only need
 337            * to repaint and not redraw a frame, but having changes in
 338            * memory usage cause frames to be painted and memory used
 339            * seems like a bad idea.
 340            */
 341           recorder->only_paint = TRUE;
 342           clutter_stage_ensure_redraw (recorder->stage);
 343           recorder->only_paint = FALSE;
 344         }
 345     }
 346 }
 347 
 348 /* Timeout used to avoid not drawing for more than MAXIMUM_PAUSE_TIME
 349  */
 350 static gboolean
 351 recorder_redraw_timeout (gpointer data)
 352 {
 353   ShellRecorder *recorder = data;
 354 
 355   recorder->redraw_timeout = 0;
 356   clutter_actor_queue_redraw (CLUTTER_ACTOR (recorder->stage));
 357 
 358   return FALSE;
 359 }
 360 
 361 static void
 362 recorder_add_redraw_timeout (ShellRecorder *recorder)
 363 {
 364   if (recorder->redraw_timeout == 0)
 365     {
 366       recorder->redraw_timeout = g_timeout_add (MAXIMUM_PAUSE_TIME,
 367                                                 recorder_redraw_timeout,
 368                                                 recorder);
 369     }
 370 }
 371 
 372 static void
 373 recorder_remove_redraw_timeout (ShellRecorder *recorder)
 374 {
 375   if (recorder->redraw_timeout != 0)
 376     {
 377       g_source_remove (recorder->redraw_timeout);
 378       recorder->redraw_timeout = 0;
 379     }
 380 }
 381 
 382 static void
 383 recorder_fetch_cursor_image (ShellRecorder *recorder)
 384 {
 385   XFixesCursorImage *cursor_image;
 386   guchar *data;
 387   int stride;
 388   int i, j;
 389 
 390   if (!recorder->have_xfixes)
 391     return;
 392 
 393   cursor_image = XFixesGetCursorImage (clutter_x11_get_default_display ());
 394   if (!cursor_image)
 395     return;
 396 
 397   recorder->cursor_hot_x = cursor_image->xhot;
 398   recorder->cursor_hot_y = cursor_image->yhot;
 399 
 400   recorder->cursor_image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
 401                                                        cursor_image->width,
 402                                                        cursor_image->height);
 403 
 404   /* The pixel data (in typical Xlib breakage) is longs even on
 405    * 64-bit platforms, so we have to data-convert there. For simplicity,
 406    * just do it always
 407    */
 408   data = cairo_image_surface_get_data (recorder->cursor_image);
 409   stride = cairo_image_surface_get_stride (recorder->cursor_image);
 410   for (i = 0; i < cursor_image->height; i++)
 411     for (j = 0; j < cursor_image->width; j++)
 412       *(guint32 *)(data + i * stride + 4 * j) = cursor_image->pixels[i * cursor_image->width + j];
 413 
 414   cairo_surface_mark_dirty (recorder->cursor_image);
 415 
 416   XFree (cursor_image);
 417 }
 418 
 419 /* Overlay the cursor image on the frame. We draw the cursor image
 420  * into the host-memory buffer after  we've captured the frame. An
 421  * alternate approach would be to turn off the cursor while recording
 422  * and draw the cursor ourselves with GL, but then we'd need to figure
 423  * out what the cursor looks like, or hard-code a non-system cursor.
 424  */
 425 static void
 426 recorder_draw_cursor (ShellRecorder *recorder,
 427                       GstBuffer     *buffer)
 428 {
 429   GstMapInfo info;
 430   cairo_surface_t *surface;
 431   cairo_t *cr;
 432 
 433   /* We don't show a cursor unless the hot spot is in the frame; this
 434    * means that sometimes we aren't going to draw a cursor even when
 435    * there is a little bit overlapping within the stage */
 436   if (recorder->pointer_x < 0 ||
 437       recorder->pointer_y < 0 ||
 438       recorder->pointer_x >= recorder->stage_width ||
 439       recorder->pointer_y >= recorder->stage_height)
 440     return;
 441 
 442   if (!recorder->cursor_image)
 443     recorder_fetch_cursor_image (recorder);
 444 
 445   if (!recorder->cursor_image)
 446     return;
 447 
 448   gst_buffer_map (buffer, &info, GST_MAP_WRITE);
 449   surface = cairo_image_surface_create_for_data (info.data,
 450                                                  CAIRO_FORMAT_ARGB32,
 451                                                  recorder->stage_width,
 452                                                  recorder->stage_height,
 453                                                  recorder->stage_width * 4);
 454 
 455   cr = cairo_create (surface);
 456   cairo_set_source_surface (cr,
 457                             recorder->cursor_image,
 458                             recorder->pointer_x - recorder->cursor_hot_x,
 459                             recorder->pointer_y - recorder->cursor_hot_y);
 460   cairo_paint (cr);
 461 
 462   cairo_destroy (cr);
 463   cairo_surface_destroy (surface);
 464   gst_buffer_unmap (buffer, &info);
 465 }
 466 
 467 /* Draw an overlay indicating how much of the target memory is used
 468  * for buffering frames.
 469  */
 470 static void
 471 recorder_draw_buffer_meter (ShellRecorder *recorder)
 472 {
 473   int fill_level;
 474 
 475   recorder_update_memory_used (recorder, FALSE);
 476 
 477   /* As the buffer gets more full, we go from green, to yellow, to red */
 478   if (recorder->memory_used > (recorder->memory_target * 3) / 4)
 479     cogl_set_source_color4f (1, 0, 0, 1);
 480   else if (recorder->memory_used > recorder->memory_target / 2)
 481     cogl_set_source_color4f (1, 1, 0, 1);
 482   else
 483     cogl_set_source_color4f (0, 1, 0, 1);
 484 
 485   fill_level = MIN (60, (recorder->memory_used * 60) / recorder->memory_target);
 486 
 487   /* A hollow rectangle filled from the left to fill_level */
 488   cogl_rectangle (recorder->stage_width - 64, recorder->stage_height - 10,
 489                   recorder->stage_width - 2,  recorder->stage_height - 9);
 490   cogl_rectangle (recorder->stage_width - 64, recorder->stage_height - 9,
 491                   recorder->stage_width - (63 - fill_level), recorder->stage_height - 3);
 492   cogl_rectangle (recorder->stage_width - 3,  recorder->stage_height - 9,
 493                   recorder->stage_width - 2,  recorder->stage_height - 3);
 494   cogl_rectangle (recorder->stage_width - 64, recorder->stage_height - 3,
 495                   recorder->stage_width - 2,  recorder->stage_height - 2);
 496 }
 497 
 498 /* We want to time-stamp each frame based on the actual time it was
 499  * recorded. We probably should use the pipeline clock rather than
 500  * gettimeofday(): that would be needed to get sync'ed audio correct.
 501  * I'm not immediately sure how to handle the adjustment we currently
 502  * do when pausing recording - is pausing the pipeline enough?
 503  */
 504 static GstClockTime
 505 get_wall_time (void)
 506 {
 507   GTimeVal tv;
 508 
 509   g_get_current_time (&tv);
 510 
 511   return tv.tv_sec * 1000000000LL + tv.tv_usec * 1000LL;
 512 }
 513 
 514 /* Retrieve a frame and feed it into the pipeline
 515  */
 516 static void
 517 recorder_record_frame (ShellRecorder *recorder)
 518 {
 519   GstBuffer *buffer;
 520   guint8 *data;
 521   guint size;
 522   GstClockTime now;
 523 
 524   g_return_if_fail (recorder->current_pipeline != NULL);
 525 
 526   /* If we get into the red zone, stop buffering new frames; 13/16 is
 527   * a bit more than the 3/4 threshold for a red indicator to keep the
 528   * indicator from flashing between red and yellow. */
 529   if (recorder->memory_used > (recorder->memory_target * 13) / 16)
 530     return;
 531 
 532   /* Drop frames to get down to something like the target frame rate; since frames
 533    * are generated with VBlank sync, we don't have full control anyways, so we just
 534    * drop frames if the interval since the last frame is less than 75% of the
 535    * desired inter-frame interval.
 536    */
 537   now = get_wall_time();
 538   if (now - recorder->last_frame_time < (3 * 1000000000LL / (4 * recorder->framerate)))
 539     return;
 540 
 541   recorder->last_frame_time = now;
 542 
 543   size = recorder->stage_width * recorder->stage_height * 4;
 544 
 545   data = shell_screen_grabber_grab (recorder->grabber,
 546                                     0, 0, recorder->stage_width, recorder->stage_height);
 547 
 548   buffer = gst_buffer_new();
 549   gst_buffer_insert_memory (buffer, -1,
 550                             gst_memory_new_wrapped (0, data, size, 0,
 551                                                     size, data, g_free));
 552 
 553   GST_BUFFER_PTS(buffer) = now - recorder->start_time;
 554 
 555   recorder_draw_cursor (recorder, buffer);
 556 
 557   shell_recorder_src_add_buffer (SHELL_RECORDER_SRC (recorder->current_pipeline->src), buffer);
 558   gst_buffer_unref (buffer);
 559 
 560   /* Reset the timeout that we used to avoid an overlong pause in the stream */
 561   recorder_remove_redraw_timeout (recorder);
 562   recorder_add_redraw_timeout (recorder);
 563 }
 564 
 565 /* We hook in by recording each frame right after the stage is painted
 566  * by clutter before glSwapBuffers() makes it visible to the user.
 567  */
 568 static void
 569 recorder_on_stage_paint (ClutterActor  *actor,
 570                          ShellRecorder *recorder)
 571 {
 572   if (recorder->state == RECORDER_STATE_RECORDING)
 573     {
 574       if (!recorder->only_paint)
 575         recorder_record_frame (recorder);
 576 
 577       cogl_set_source_texture (recorder->recording_icon);
 578       cogl_rectangle (recorder->stage_width - 32, recorder->stage_height - 42,
 579                       recorder->stage_width,      recorder->stage_height - 10);
 580     }
 581 
 582   if (recorder->state == RECORDER_STATE_RECORDING || recorder->memory_used != 0)
 583     recorder_draw_buffer_meter (recorder);
 584 }
 585 
 586 static void
 587 recorder_update_size (ShellRecorder *recorder)
 588 {
 589   ClutterActorBox allocation;
 590 
 591   clutter_actor_get_allocation_box (CLUTTER_ACTOR (recorder->stage), &allocation);
 592   recorder->stage_width = (int)(0.5 + allocation.x2 - allocation.x1);
 593   recorder->stage_height = (int)(0.5 + allocation.y2 - allocation.y1);
 594 }
 595 
 596 static void
 597 recorder_on_stage_notify_size (GObject          *object,
 598                                GParamSpec       *pspec,
 599                                ShellRecorder    *recorder)
 600 {
 601   recorder_update_size (recorder);
 602 
 603   /* This breaks the recording but tweaking the GStreamer pipeline a bit
 604    * might make it work, at least if the codec can handle a stream where
 605    * the frame size changes in the middle.
 606    */
 607   if (recorder->current_pipeline)
 608     recorder_pipeline_set_caps (recorder->current_pipeline);
 609 }
 610 
 611 static gboolean
 612 recorder_idle_redraw (gpointer data)
 613 {
 614   ShellRecorder *recorder = data;
 615 
 616   recorder->redraw_idle = 0;
 617   clutter_actor_queue_redraw (CLUTTER_ACTOR (recorder->stage));
 618 
 619   return FALSE;
 620 }
 621 
 622 static void
 623 recorder_queue_redraw (ShellRecorder *recorder)
 624 {
 625   /* If we just queue a redraw on every mouse motion (for example), we
 626    * starve Clutter, which operates at a very low priority. So
 627    * we need to queue a "low priority redraw" after timeline updates
 628    */
 629   if (recorder->state == RECORDER_STATE_RECORDING && recorder->redraw_idle == 0)
 630     recorder->redraw_idle = g_idle_add_full (CLUTTER_PRIORITY_REDRAW + 1,
 631                                              recorder_idle_redraw, recorder, NULL);
 632 }
 633 
 634 /* We use an event filter on the stage to get the XFixesCursorNotifyEvent
 635  * and also to track cursor position (when the cursor is over the stage's
 636  * input area); tracking cursor position here rather than with ClutterEvent
 637  * allows us to avoid worrying about event propagation and competing
 638  * signal handlers.
 639  */
 640 static ClutterX11FilterReturn
 641 recorder_event_filter (XEvent        *xev,
 642                        ClutterEvent  *cev,
 643                        gpointer       data)
 644 {
 645   ShellRecorder *recorder = data;
 646 
 647   if (xev->xany.window != clutter_x11_get_stage_window (recorder->stage))
 648     return CLUTTER_X11_FILTER_CONTINUE;
 649 
 650   if (xev->xany.type == recorder->xfixes_event_base + XFixesCursorNotify)
 651     {
 652       XFixesCursorNotifyEvent *notify_event = (XFixesCursorNotifyEvent *)xev;
 653 
 654       if (notify_event->subtype == XFixesDisplayCursorNotify)
 655         {
 656           if (recorder->cursor_image)
 657             {
 658               cairo_surface_destroy (recorder->cursor_image);
 659               recorder->cursor_image = NULL;
 660             }
 661 
 662           recorder_queue_redraw (recorder);
 663         }
 664     }
 665   else if (xev->xany.type == MotionNotify)
 666     {
 667       recorder->pointer_x = xev->xmotion.x;
 668       recorder->pointer_y = xev->xmotion.y;
 669 
 670       recorder_queue_redraw (recorder);
 671     }
 672   /* We want to track whether the pointer is over the stage
 673    * window itself, and not in a child window. A "virtual"
 674    * crossing is one that goes directly from ancestor to child.
 675    */
 676   else if (xev->xany.type == EnterNotify &&
 677            (xev->xcrossing.detail != NotifyVirtual &&
 678             xev->xcrossing.detail != NotifyNonlinearVirtual))
 679     {
 680       recorder->have_pointer = TRUE;
 681       recorder->pointer_x = xev->xcrossing.x;
 682       recorder->pointer_y = xev->xcrossing.y;
 683 
 684       recorder_queue_redraw (recorder);
 685     }
 686   else if (xev->xany.type == LeaveNotify &&
 687            (xev->xcrossing.detail != NotifyVirtual &&
 688             xev->xcrossing.detail != NotifyNonlinearVirtual))
 689     {
 690       recorder->have_pointer = FALSE;
 691       recorder->pointer_x = xev->xcrossing.x;
 692       recorder->pointer_y = xev->xcrossing.y;
 693 
 694       recorder_queue_redraw (recorder);
 695     }
 696 
 697   return CLUTTER_X11_FILTER_CONTINUE;
 698 }
 699 
 700 /* We optimize out querying the server for the pointer position if the
 701  * pointer is in the input area of the ClutterStage. We track changes to
 702  * that with Enter/Leave events, but we need to 100% accurate about the
 703  * initial condition, which is a little involved.
 704  */
 705 static void
 706 recorder_get_initial_cursor_position (ShellRecorder *recorder)
 707 {
 708   Display *xdisplay = clutter_x11_get_default_display ();
 709   Window xwindow = clutter_x11_get_stage_window (recorder->stage);
 710   XWindowAttributes xwa;
 711   Window root, child, parent;
 712   Window *children;
 713   guint n_children;
 714   int root_x,root_y;
 715   int window_x, window_y;
 716   guint mask;
 717 
 718   XGrabServer(xdisplay);
 719 
 720   XGetWindowAttributes (xdisplay, xwindow, &xwa);
 721   XQueryTree (xdisplay, xwindow, &root, &parent, &children, &n_children);
 722   XFree (children);
 723 
 724   if (xwa.map_state == IsViewable &&
 725       XQueryPointer (xdisplay, parent,
 726                      &root, &child, &root_x, &root_y, &window_x, &window_y, &mask) &&
 727       child == xwindow)
 728     {
 729       /* The point of this call is not actually to translate the coordinates -
 730        * we could do that ourselves using xwa.{x,y} -  but rather to see if
 731        * the pointer is in a child of the window, which we count as "not in
 732        * window", because we aren't guaranteed to get pointer events.
 733        */
 734       XTranslateCoordinates(xdisplay, parent, xwindow,
 735                             window_x, window_y,
 736                             &window_x, &window_y, &child);
 737       if (child == None)
 738         {
 739           recorder->have_pointer = TRUE;
 740           recorder->pointer_x = window_x;
 741           recorder->pointer_y = window_y;
 742         }
 743     }
 744   else
 745     recorder->have_pointer = FALSE;
 746 
 747   XUngrabServer(xdisplay);
 748   XFlush(xdisplay);
 749 
 750   /* While we are at it, add mouse events to the event mask; they will
 751    * be there for the stage windows that Clutter creates by default, but
 752    * maybe this stage was created differently. Since we've already
 753    * retrieved the event mask, it's almost free.
 754    */
 755   XSelectInput(xdisplay, xwindow,
 756                xwa.your_event_mask | EnterWindowMask | LeaveWindowMask | PointerMotionMask);
 757 }
 758 
 759 /* When the cursor is not over the stage's input area, we query for the
 760  * pointer position in a timeout.
 761  */
 762 static void
 763 recorder_update_pointer (ShellRecorder *recorder)
 764 {
 765   Display *xdisplay = clutter_x11_get_default_display ();
 766   Window xwindow = clutter_x11_get_stage_window (recorder->stage);
 767   Window root, child;
 768   int root_x,root_y;
 769   int window_x, window_y;
 770   guint mask;
 771 
 772   if (recorder->have_pointer)
 773     return;
 774 
 775   if (XQueryPointer (xdisplay, xwindow,
 776                      &root, &child, &root_x, &root_y, &window_x, &window_y, &mask))
 777     {
 778       if (window_x != recorder->pointer_x || window_y != recorder->pointer_y)
 779         {
 780           recorder->pointer_x = window_x;
 781           recorder->pointer_y = window_y;
 782 
 783           recorder_queue_redraw (recorder);
 784         }
 785     }
 786 }
 787 
 788 static gboolean
 789 recorder_update_pointer_timeout (gpointer data)
 790 {
 791   recorder_update_pointer (data);
 792 
 793   return TRUE;
 794 }
 795 
 796 static void
 797 recorder_add_update_pointer_timeout (ShellRecorder *recorder)
 798 {
 799   if (!recorder->update_pointer_timeout)
 800     recorder->update_pointer_timeout = g_timeout_add (UPDATE_POINTER_TIME,
 801                                                       recorder_update_pointer_timeout,
 802                                                       recorder);
 803 }
 804 
 805 static void
 806 recorder_remove_update_pointer_timeout (ShellRecorder *recorder)
 807 {
 808   if (recorder->update_pointer_timeout)
 809     {
 810       g_source_remove (recorder->update_pointer_timeout);
 811       recorder->update_pointer_timeout = 0;
 812     }
 813 }
 814 
 815 static void
 816 recorder_set_stage (ShellRecorder *recorder,
 817                     ClutterStage  *stage)
 818 {
 819   if (recorder->stage == stage)
 820     return;
 821 
 822   if (recorder->current_pipeline)
 823     shell_recorder_close (recorder);
 824 
 825   if (recorder->stage)
 826     {
 827       g_signal_handlers_disconnect_by_func (recorder->stage,
 828                                             (void *)recorder_on_stage_destroy,
 829                                             recorder);
 830       g_signal_handlers_disconnect_by_func (recorder->stage,
 831                                             (void *)recorder_on_stage_paint,
 832                                             recorder);
 833       g_signal_handlers_disconnect_by_func (recorder->stage,
 834                                             (void *)recorder_on_stage_notify_size,
 835                                             recorder);
 836 
 837       clutter_x11_remove_filter (recorder_event_filter, recorder);
 838 
 839       /* We don't don't deselect for cursor changes in case someone else just
 840        * happened to be selecting for cursor events on the same window; sending
 841        * us the events is close to free in any case.
 842        */
 843 
 844       if (recorder->redraw_idle)
 845         {
 846           g_source_remove (recorder->redraw_idle);
 847           recorder->redraw_idle = 0;
 848         }
 849     }
 850 
 851   recorder->stage = stage;
 852 
 853   if (recorder->stage)
 854     {
 855       int error_base;
 856 
 857       recorder->stage = stage;
 858       g_signal_connect (recorder->stage, "destroy",
 859                         G_CALLBACK (recorder_on_stage_destroy), recorder);
 860       g_signal_connect_after (recorder->stage, "paint",
 861                               G_CALLBACK (recorder_on_stage_paint), recorder);
 862       g_signal_connect (recorder->stage, "notify::width",
 863                         G_CALLBACK (recorder_on_stage_notify_size), recorder);
 864       g_signal_connect (recorder->stage, "notify::width",
 865                         G_CALLBACK (recorder_on_stage_notify_size), recorder);
 866 
 867       clutter_x11_add_filter (recorder_event_filter, recorder);
 868 
 869       recorder_update_size (recorder);
 870 
 871       recorder->have_xfixes = XFixesQueryExtension (clutter_x11_get_default_display (),
 872                                                     &recorder->xfixes_event_base,
 873                                                     &error_base);
 874       if (recorder->have_xfixes)
 875         XFixesSelectCursorInput (clutter_x11_get_default_display (),
 876                                    clutter_x11_get_stage_window (stage),
 877                                  XFixesDisplayCursorNotifyMask);
 878 
 879       clutter_stage_ensure_current (stage);
 880 
 881       recorder_get_initial_cursor_position (recorder);
 882     }
 883 }
 884 
 885 static void
 886 recorder_set_framerate (ShellRecorder *recorder,
 887                         int framerate)
 888 {
 889   if (framerate == recorder->framerate)
 890     return;
 891 
 892   if (recorder->current_pipeline)
 893     shell_recorder_close (recorder);
 894 
 895   recorder->framerate = framerate;
 896 
 897   g_object_notify (G_OBJECT (recorder), "framerate");
 898 }
 899 
 900 static void
 901 recorder_set_pipeline (ShellRecorder *recorder,
 902                        const char    *pipeline)
 903 {
 904   if (pipeline == recorder->pipeline_description ||
 905       (pipeline && recorder->pipeline_description && strcmp (recorder->pipeline_description, pipeline) == 0))
 906     return;
 907 
 908   if (recorder->current_pipeline)
 909     shell_recorder_close (recorder);
 910 
 911   if (recorder->pipeline_description)
 912     g_free (recorder->pipeline_description);
 913 
 914   recorder->pipeline_description = g_strdup (pipeline);
 915 
 916   g_object_notify (G_OBJECT (recorder), "pipeline");
 917 }
 918 
 919 static void
 920 recorder_set_filename (ShellRecorder *recorder,
 921                        const char    *filename)
 922 {
 923   if (filename == recorder->filename ||
 924       (filename && recorder->filename && strcmp (recorder->filename, filename) == 0))
 925     return;
 926 
 927   if (recorder->current_pipeline)
 928     shell_recorder_close (recorder);
 929 
 930   if (recorder->filename)
 931     g_free (recorder->filename);
 932 
 933   recorder->filename = g_strdup (filename);
 934 
 935   g_object_notify (G_OBJECT (recorder), "filename");
 936 }
 937 
 938 static void
 939 shell_recorder_set_property (GObject      *object,
 940                              guint         prop_id,
 941                              const GValue *value,
 942                              GParamSpec   *pspec)
 943 {
 944   ShellRecorder *recorder = SHELL_RECORDER (object);
 945 
 946   switch (prop_id)
 947     {
 948     case PROP_STAGE:
 949       recorder_set_stage (recorder, g_value_get_object (value));
 950       break;
 951     case PROP_FRAMERATE:
 952       recorder_set_framerate (recorder, g_value_get_int (value));
 953       break;
 954     case PROP_PIPELINE:
 955       recorder_set_pipeline (recorder, g_value_get_string (value));
 956       break;
 957     case PROP_FILENAME:
 958       recorder_set_filename (recorder, g_value_get_string (value));
 959       break;
 960     default:
 961       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 962       break;
 963     }
 964 }
 965 
 966 static void
 967 shell_recorder_get_property (GObject         *object,
 968                              guint            prop_id,
 969                              GValue          *value,
 970                              GParamSpec      *pspec)
 971 {
 972   ShellRecorder *recorder = SHELL_RECORDER (object);
 973 
 974   switch (prop_id)
 975     {
 976     case PROP_STAGE:
 977       g_value_set_object (value, G_OBJECT (recorder->stage));
 978       break;
 979     case PROP_FRAMERATE:
 980       g_value_set_int (value, recorder->framerate);
 981       break;
 982     case PROP_PIPELINE:
 983       g_value_set_string (value, recorder->pipeline_description);
 984       break;
 985     case PROP_FILENAME:
 986       g_value_set_string (value, recorder->filename);
 987       break;
 988     default:
 989       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 990       break;
 991     }
 992 }
 993 
 994 static void
 995 shell_recorder_class_init (ShellRecorderClass *klass)
 996 {
 997   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 998 
 999   gobject_class->finalize = shell_recorder_finalize;
1000   gobject_class->get_property = shell_recorder_get_property;
1001   gobject_class->set_property = shell_recorder_set_property;
1002 
1003   g_object_class_install_property (gobject_class,
1004                                    PROP_STAGE,
1005                                    g_param_spec_object ("stage",
1006                                                         "Stage",
1007                                                         "Stage to record",
1008                                                         CLUTTER_TYPE_STAGE,
1009                                                         G_PARAM_READWRITE));
1010 
1011   g_object_class_install_property (gobject_class,
1012                                    PROP_FRAMERATE,
1013                                    g_param_spec_int ("framerate",
1014                                                      "Framerate",
1015                                                      "Framerate used for resulting video in frames-per-second",
1016                                                       0,
1017                                                       G_MAXINT,
1018                                                       DEFAULT_FRAMES_PER_SECOND,
1019                                                       G_PARAM_READWRITE));
1020 
1021   g_object_class_install_property (gobject_class,
1022                                    PROP_PIPELINE,
1023                                    g_param_spec_string ("pipeline",
1024                                                         "Pipeline",
1025                                                         "GStreamer pipeline description to encode recordings",
1026                                                         NULL,
1027                                                         G_PARAM_READWRITE));
1028 
1029   g_object_class_install_property (gobject_class,
1030                                    PROP_FILENAME,
1031                                    g_param_spec_string ("filename",
1032                                                         "Filename",
1033                                                         "The filename template to use for output files",
1034                                                         NULL,
1035                                                         G_PARAM_READWRITE));
1036 }
1037 
1038 /* Sets the GstCaps (video format, in this case) on the stream
1039  */
1040 static void
1041 recorder_pipeline_set_caps (RecorderPipeline *pipeline)
1042 {
1043   GstCaps *caps;
1044 
1045   /* The data is always native-endian xRGB; videoconvert
1046    * doesn't support little-endian xRGB, but does support
1047    * big-endian BGRx.
1048    */
1049   caps = gst_caps_new_simple ("video/x-raw",
1050 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
1051                               "format", G_TYPE_STRING, "BGRx",
1052 #else
1053                               "format", G_TYPE_STRING, "xRGB",
1054 #endif
1055                               "bpp", G_TYPE_INT, 32,
1056                               "depth", G_TYPE_INT, 24,
1057                               "framerate", GST_TYPE_FRACTION, pipeline->recorder->framerate, 1,
1058                               "width", G_TYPE_INT, pipeline->recorder->stage_width,
1059                               "height", G_TYPE_INT, pipeline->recorder->stage_height,
1060                               NULL);
1061   g_object_set (pipeline->src, "caps", caps, NULL);
1062   gst_caps_unref (caps);
1063 }
1064 
1065 /* Augments the supplied pipeline with the source elements: the actual
1066  * ShellRecorderSrc element where we inject frames then additional elements
1067  * to convert the output into something palatable.
1068  */
1069 static gboolean
1070 recorder_pipeline_add_source (RecorderPipeline *pipeline)
1071 {
1072   GstPad *sink_pad = NULL, *src_pad = NULL;
1073   gboolean result = FALSE;
1074   GstElement *videoconvert;
1075 
1076   sink_pad = gst_bin_find_unlinked_pad (GST_BIN (pipeline->pipeline), GST_PAD_SINK);
1077   if (sink_pad == NULL)
1078     {
1079       g_warning("ShellRecorder: pipeline has no unlinked sink pad");
1080       goto out;
1081     }
1082 
1083   pipeline->src = gst_element_factory_make ("shellrecordersrc", NULL);
1084   if (pipeline->src == NULL)
1085     {
1086       g_warning ("Can't create recorder source element");
1087       goto out;
1088     }
1089   gst_bin_add (GST_BIN (pipeline->pipeline), pipeline->src);
1090 
1091   recorder_pipeline_set_caps (pipeline);
1092 
1093   /* The videoconvert element is a generic converter; it will convert
1094    * our supplied fixed format data into whatever the encoder wants
1095    */
1096   videoconvert = gst_element_factory_make ("videoconvert", NULL);
1097   if (!videoconvert)
1098     {
1099       g_warning("Can't create videoconvert element");
1100       goto out;
1101     }
1102   gst_bin_add (GST_BIN (pipeline->pipeline), videoconvert);
1103 
1104   gst_element_link_many (pipeline->src, videoconvert, NULL);
1105   src_pad = gst_element_get_static_pad (videoconvert, "src");
1106 
1107   if (!src_pad)
1108     {
1109       g_warning("ShellRecorder: can't get src pad to link into pipeline");
1110       goto out;
1111     }
1112 
1113   if (gst_pad_link (src_pad, sink_pad) != GST_PAD_LINK_OK)
1114     {
1115       g_warning("ShellRecorder: can't link to sink pad");
1116       goto out;
1117     }
1118 
1119   result = TRUE;
1120 
1121  out:
1122   if (sink_pad)
1123     gst_object_unref (sink_pad);
1124   if (src_pad)
1125     gst_object_unref (src_pad);
1126 
1127   return result;
1128 }
1129 
1130 static char *
1131 get_absolute_path (char *maybe_relative)
1132 {
1133   char *path;
1134 
1135   if (g_path_is_absolute (maybe_relative))
1136     path = g_strdup (maybe_relative);
1137   else
1138     {
1139       const char *video_dir = g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS);
1140       path = g_build_filename (video_dir, maybe_relative, NULL);
1141     }
1142 
1143   return path;
1144 }
1145 
1146 /* Open a file for writing. Opening the file ourselves and using fdsink has
1147  * the advantage over filesink of being able to use O_EXCL when we want to
1148  * avoid overwriting* an existing file. Returns -1 if the file couldn't
1149  * be opened.
1150  */
1151 static int
1152 recorder_open_outfile (ShellRecorder *recorder)
1153 {
1154   const char *pattern;
1155   int flags;
1156   int outfile = -1;
1157 
1158   pattern = recorder->filename;
1159   if (!pattern)
1160     return -1;
1161 
1162   while (TRUE)
1163     {
1164       GString *filename = g_string_new (NULL);
1165       const char *p;
1166       char *path;
1167 
1168       for (p = pattern; *p; p++)
1169         {
1170           if (*p == '%')
1171             {
1172               switch (*(p + 1))
1173                 {
1174                 case '%':
1175                 case '\0':
1176                   g_string_append_c (filename, '%');
1177                   break;
1178                 case 'd':
1179                   {
1180                     /* Appends date according to locale */
1181                     GDateTime *datetime = g_date_time_new_now_local ();
1182                     char *date_str = g_date_time_format (datetime, "%0x");
1183                     char *s;
1184 
1185                     for (s = date_str; *s; s++)
1186                       if (G_IS_DIR_SEPARATOR (*s))
1187                           *s = '-';
1188 
1189                     g_string_append (filename, date_str);
1190                     g_free (date_str);
1191                     g_date_time_unref (datetime);
1192                   }
1193                   break;
1194                 case 't':
1195                   {
1196                     /* Appends time according to locale */
1197                     GDateTime *datetime = g_date_time_new_now_local ();
1198                     char *time_str = g_date_time_format (datetime, "%0X");
1199                     char *s;
1200 
1201                     for (s = time_str; *s; s++)
1202                       if (G_IS_DIR_SEPARATOR (*s))
1203                           *s = ':';
1204 
1205                     g_string_append (filename, time_str);
1206                     g_free (time_str);
1207                     g_date_time_unref (datetime);
1208                   }
1209                   break;
1210                 default:
1211                   g_warning ("Unknown escape %%%c in filename", *(p + 1));
1212                   goto out;
1213                 }
1214 
1215               p++;
1216             }
1217           else
1218             g_string_append_c (filename, *p);
1219         }
1220 
1221       /* If a filename is explicitly specified without %u then we assume the user
1222        * is fine with over-writing the old contents; putting %u in the default
1223        * should avoid problems with malicious symlinks.
1224        */
1225       flags = O_WRONLY | O_CREAT | O_TRUNC;
1226 
1227       path = get_absolute_path (filename->str);
1228       outfile = open (path, flags, 0666);
1229       if (outfile != -1)
1230         {
1231           g_printerr ("Recording to %s\n", path);
1232 
1233           g_string_free (filename, TRUE);
1234           g_free (path);
1235           goto out;
1236         }
1237 
1238       if (outfile == -1 && errno != EEXIST)
1239         {
1240           g_warning ("Cannot open output file '%s': %s", filename->str, g_strerror (errno));
1241           g_string_free (filename, TRUE);
1242           g_free (path);
1243           goto out;
1244         }
1245 
1246       g_string_free (filename, TRUE);
1247       g_free (path);
1248     }
1249 
1250  out:
1251 
1252   return outfile;
1253 }
1254 
1255 /* Augments the supplied pipeline with a sink element to write to the output
1256  * file, if necessary.
1257  */
1258 static gboolean
1259 recorder_pipeline_add_sink (RecorderPipeline *pipeline)
1260 {
1261   GstPad *sink_pad = NULL, *src_pad = NULL;
1262   GstElement *fdsink;
1263   gboolean result = FALSE;
1264 
1265   src_pad = gst_bin_find_unlinked_pad (GST_BIN (pipeline->pipeline), GST_PAD_SRC);
1266   if (src_pad == NULL)
1267     {
1268       /* Nothing to do - assume that we were given a complete pipeline */
1269       return TRUE;
1270     }
1271 
1272   pipeline->outfile = recorder_open_outfile (pipeline->recorder);
1273   if (pipeline->outfile == -1)
1274     goto out;
1275 
1276   fdsink = gst_element_factory_make ("fdsink", NULL);
1277   if (fdsink == NULL)
1278     {
1279       g_warning("Can't create fdsink element");
1280       goto out;
1281     }
1282   gst_bin_add (GST_BIN (pipeline->pipeline), fdsink);
1283   g_object_set (fdsink, "fd", pipeline->outfile, NULL);
1284 
1285   sink_pad = gst_element_get_static_pad (fdsink, "sink");
1286   if (!sink_pad)
1287     {
1288       g_warning("ShellRecorder: can't get sink pad to link pipeline output");
1289       goto out;
1290     }
1291 
1292   if (gst_pad_link (src_pad, sink_pad) != GST_PAD_LINK_OK)
1293     {
1294       g_warning("ShellRecorder: can't link to sink pad");
1295       goto out;
1296     }
1297 
1298   result = TRUE;
1299 
1300  out:
1301   if (src_pad)
1302     gst_object_unref (src_pad);
1303   if (sink_pad)
1304     gst_object_unref (sink_pad);
1305 
1306   return result;
1307 }
1308 
1309 static gboolean
1310 recorder_update_memory_used_timeout (gpointer data)
1311 {
1312   ShellRecorder *recorder = data;
1313   recorder->update_memory_used_timeout = 0;
1314 
1315   recorder_update_memory_used (recorder, TRUE);
1316 
1317   return FALSE;
1318 }
1319 
1320 /* We throttle down the frequency which we recompute memory usage
1321  * and draw the buffer indicator to avoid cutting into performance.
1322  */
1323 static void
1324 recorder_pipeline_on_memory_used_changed (ShellRecorderSrc *src,
1325                                           GParamSpec       *spec,
1326                                           RecorderPipeline *pipeline)
1327 {
1328   ShellRecorder *recorder = pipeline->recorder;
1329   if (!recorder)
1330     return;
1331 
1332   if (recorder->update_memory_used_timeout == 0)
1333     recorder->update_memory_used_timeout = g_timeout_add (UPDATE_MEMORY_USED_DELAY,
1334                                                           recorder_update_memory_used_timeout,
1335                                                           recorder);
1336 }
1337 
1338 static void
1339 recorder_pipeline_free (RecorderPipeline *pipeline)
1340 {
1341   if (pipeline->pipeline != NULL)
1342     gst_object_unref (pipeline->pipeline);
1343 
1344   if (pipeline->outfile != -1)
1345     close (pipeline->outfile);
1346 
1347   g_free (pipeline);
1348 }
1349 
1350 /* Function gets called on pipeline-global events; we use it to
1351  * know when the pipeline is finished.
1352  */
1353 static gboolean
1354 recorder_pipeline_bus_watch (GstBus     *bus,
1355                              GstMessage *message,
1356                              gpointer    data)
1357 {
1358   RecorderPipeline *pipeline = data;
1359 
1360   switch (message->type)
1361     {
1362     case GST_MESSAGE_EOS:
1363       recorder_pipeline_closed (pipeline);
1364       return FALSE; /* remove watch */
1365     case GST_MESSAGE_ERROR:
1366       {
1367         GError *error;
1368 
1369         gst_message_parse_error (message, &error, NULL);
1370         g_warning ("Error in recording pipeline: %s\n", error->message);
1371         g_error_free (error);
1372         recorder_pipeline_closed (pipeline);
1373         return FALSE; /* remove watch */
1374       }
1375     default:
1376       break;
1377     }
1378 
1379   /* Leave the watch in place */
1380   return TRUE;
1381 }
1382 
1383 /* Clean up when the pipeline is finished
1384  */
1385 static void
1386 recorder_pipeline_closed (RecorderPipeline *pipeline)
1387 {
1388   g_signal_handlers_disconnect_by_func (pipeline->src,
1389                                         (gpointer) recorder_pipeline_on_memory_used_changed,
1390                                         pipeline);
1391 
1392   gst_element_set_state (pipeline->pipeline, GST_STATE_NULL);
1393 
1394   if (pipeline->recorder)
1395     {
1396       ShellRecorder *recorder = pipeline->recorder;
1397       if (pipeline == recorder->current_pipeline)
1398         {
1399           /* Error case; force a close */
1400           recorder->current_pipeline = NULL;
1401           shell_recorder_close (recorder);
1402         }
1403 
1404       recorder->pipelines = g_slist_remove (recorder->pipelines, pipeline);
1405     }
1406 
1407   recorder_pipeline_free (pipeline);
1408 }
1409 
1410 /*
1411  * Replaces '%T' in the passed pipeline with the thread count,
1412  * the maximum possible value is 64 (limit of what vp8enc supports)
1413  *
1414  * It is assumes that %T occurs only once.
1415  */
1416 static char*
1417 substitute_thread_count (const char *pipeline)
1418 {
1419   char *tmp;
1420   int n_threads;
1421   GString *result;
1422 
1423   tmp = strstr (pipeline, "%T");
1424 
1425   if (!tmp)
1426     return g_strdup (pipeline);
1427 
1428 #ifdef _SC_NPROCESSORS_ONLN
1429     {
1430       int n_processors = sysconf (_SC_NPROCESSORS_ONLN); /* includes hyper-threading */
1431       n_threads = MIN (MAX (1, n_processors - 1), 64);
1432     }
1433 #else
1434     n_threads = 3;
1435 #endif
1436 
1437   result = g_string_new (NULL);
1438   g_string_append_len (result, pipeline, tmp - pipeline);
1439   g_string_append_printf (result, "%d", n_threads);
1440   g_string_append (result, tmp + 2);
1441 
1442   return g_string_free (result, FALSE);;
1443 }
1444 
1445 static gboolean
1446 recorder_open_pipeline (ShellRecorder *recorder)
1447 {
1448   RecorderPipeline *pipeline;
1449   const char *pipeline_description;
1450   char *parsed_pipeline;
1451   GError *error = NULL;
1452   GstBus *bus;
1453 
1454   pipeline = g_new0(RecorderPipeline, 1);
1455   pipeline->recorder = recorder;
1456   pipeline->outfile = - 1;
1457 
1458   pipeline_description = recorder->pipeline_description;
1459   if (!pipeline_description)
1460     pipeline_description = DEFAULT_PIPELINE;
1461 
1462   parsed_pipeline = substitute_thread_count (pipeline_description);
1463 
1464   pipeline->pipeline = gst_parse_launch_full (parsed_pipeline, NULL,
1465                                               GST_PARSE_FLAG_FATAL_ERRORS,
1466                                               &error);
1467   g_free (parsed_pipeline);
1468 
1469   if (pipeline->pipeline == NULL)
1470     {
1471       g_warning ("ShellRecorder: failed to parse pipeline: %s", error->message);
1472       g_error_free (error);
1473       goto error;
1474     }
1475 
1476   if (!recorder_pipeline_add_source (pipeline))
1477     goto error;
1478 
1479   if (!recorder_pipeline_add_sink (pipeline))
1480     goto error;
1481 
1482   gst_element_set_state (pipeline->pipeline, GST_STATE_PLAYING);
1483 
1484   bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline->pipeline));
1485   gst_bus_add_watch (bus, recorder_pipeline_bus_watch, pipeline);
1486   gst_object_unref (bus);
1487 
1488   g_signal_connect (pipeline->src, "notify::memory-used",
1489                     G_CALLBACK (recorder_pipeline_on_memory_used_changed), pipeline);
1490 
1491   recorder->current_pipeline = pipeline;
1492   recorder->pipelines = g_slist_prepend (recorder->pipelines, pipeline);
1493 
1494   return TRUE;
1495 
1496  error:
1497   recorder_pipeline_free (pipeline);
1498 
1499   return FALSE;
1500 }
1501 
1502 static void
1503 recorder_close_pipeline (ShellRecorder *recorder)
1504 {
1505   if (recorder->current_pipeline != NULL)
1506     {
1507       /* This will send an EOS (end-of-stream) message after the last frame
1508        * is written. The bus watch for the pipeline will get it and do
1509        * final cleanup
1510        */
1511       shell_recorder_src_close (SHELL_RECORDER_SRC (recorder->current_pipeline->src));
1512 
1513       recorder->current_pipeline = NULL;
1514     }
1515 }
1516 
1517 /**
1518  * shell_recorder_new:
1519  * @stage: The #ClutterStage
1520  *
1521  * Create a new #ShellRecorder to record movies of a #ClutterStage
1522  *
1523  * Return value: The newly created #ShellRecorder object
1524  */
1525 ShellRecorder     *
1526 shell_recorder_new (ClutterStage  *stage)
1527 {
1528   return g_object_new (SHELL_TYPE_RECORDER,
1529                        "stage",    stage,
1530                        NULL);
1531 }
1532 
1533 /**
1534  * shell_recorder_set_framerate:
1535  * @recorder: the #ShellRecorder
1536  * @framerate: Framerate used for resulting video in frames-per-second.
1537  *
1538  * Sets the number of frames per second we try to record. Less frames
1539  * will be recorded when the screen doesn't need to be redrawn this
1540  * quickly. (This value will also be set as the framerate for the
1541  * GStreamer pipeline; whether that has an effect on the resulting
1542  * video will depend on the details of the pipeline and the codec. The
1543  * default encoding to webm format doesn't pay attention to the pipeline
1544  * framerate.)
1545  *
1546  * The default value is 30.
1547  */
1548 void
1549 shell_recorder_set_framerate (ShellRecorder *recorder,
1550                              int framerate)
1551 {
1552   g_return_if_fail (SHELL_IS_RECORDER (recorder));
1553 
1554   recorder_set_framerate (recorder, framerate);
1555 }
1556 
1557 /**
1558  * shell_recorder_set_filename:
1559  * @recorder: the #ShellRecorder
1560  * @filename: the filename template to use for output files,
1561  *            or %NULL for the defalt value.
1562  *
1563  * Sets the filename that will be used when creating output
1564  * files. This is only used if the configured pipeline has an
1565  * unconnected source pad (as the default pipeline does). If
1566  * the pipeline is complete, then the filename is unused. The
1567  * provided string is used as a template.It can contain
1568  * the following escapes:
1569  *
1570  * %d: The current date as YYYYYMMDD
1571  * %%: A literal percent
1572  *
1573  * The default value is 'shell-%d%u-%c.ogg'.
1574  */
1575 void
1576 shell_recorder_set_filename (ShellRecorder *recorder,
1577                              const char    *filename)
1578 {
1579   g_return_if_fail (SHELL_IS_RECORDER (recorder));
1580 
1581   recorder_set_filename (recorder, filename);
1582 
1583 }
1584 
1585 /**
1586  * shell_recorder_set_pipeline:
1587  * @recorder: the #ShellRecorder
1588  * @pipeline: (allow-none): the GStreamer pipeline used to encode recordings
1589  *            or %NULL for the default value.
1590  *
1591  * Sets the GStreamer pipeline used to encode recordings.
1592  * It follows the syntax used for gst-launch. The pipeline
1593  * should have an unconnected sink pad where the recorded
1594  * video is recorded. It will normally have a unconnected
1595  * source pad; output from that pad will be written into the
1596  * output file. (See shell_recorder_set_filename().) However
1597  * the pipeline can also take care of its own output - this
1598  * might be used to send the output to an icecast server
1599  * via shout2send or similar.
1600  *
1601  * The default value is 'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux'
1602  */
1603 void
1604 shell_recorder_set_pipeline (ShellRecorder *recorder,
1605                              const char    *pipeline)
1606 {
1607   g_return_if_fail (SHELL_IS_RECORDER (recorder));
1608 
1609   recorder_set_pipeline (recorder, pipeline);
1610 }
1611 
1612 /**
1613  * shell_recorder_record:
1614  * @recorder: the #ShellRecorder
1615  *
1616  * Starts recording, Starting the recording may fail if the output file
1617  * cannot be opened, or if the output stream cannot be created
1618  * for other reasons. In that case a warning is printed to
1619  * stderr. There is no way currently to get details on how
1620  * recording failed to start.
1621  *
1622  * An extra reference count is added to the recorder if recording
1623  * is succesfully started; the recording object will not be freed
1624  * until recording is stopped even if the creator no longer holds
1625  * a reference. Recording is automatically stopped if the stage
1626  * is destroyed.
1627  *
1628  * Return value: %TRUE if recording was succesfully started
1629  */
1630 gboolean
1631 shell_recorder_record (ShellRecorder *recorder)
1632 {
1633   g_return_val_if_fail (SHELL_IS_RECORDER (recorder), FALSE);
1634   g_return_val_if_fail (recorder->stage != NULL, FALSE);
1635   g_return_val_if_fail (recorder->state != RECORDER_STATE_RECORDING, FALSE);
1636 
1637   if (!recorder_open_pipeline (recorder))
1638     return FALSE;
1639 
1640   recorder->start_time = get_wall_time();
1641   recorder->last_frame_time = 0;
1642 
1643   recorder->state = RECORDER_STATE_RECORDING;
1644   recorder_add_update_pointer_timeout (recorder);
1645 
1646   /* Set up repaint hook */
1647   recorder->repaint_hook_id = clutter_threads_add_repaint_func(recorder_repaint_hook, recorder->stage, NULL);
1648 
1649   /* Record an initial frame and also redraw with the indicator */
1650   clutter_actor_queue_redraw (CLUTTER_ACTOR (recorder->stage));
1651 
1652   /* We keep a ref while recording to let a caller start a recording then
1653    * drop their reference to the recorder
1654    */
1655   g_object_ref (recorder);
1656 
1657   return TRUE;
1658 }
1659 
1660 /**
1661  * shell_recorder_close:
1662  * @recorder: the #ShellRecorder
1663  *
1664  * Stops recording. It's possible to call shell_recorder_record()
1665  * again to reopen a new recording stream, but unless change the
1666  * recording filename, this may result in the old recording being
1667  * overwritten.
1668  */
1669 void
1670 shell_recorder_close (ShellRecorder *recorder)
1671 {
1672   g_return_if_fail (SHELL_IS_RECORDER (recorder));
1673   g_return_if_fail (recorder->state != RECORDER_STATE_CLOSED);
1674 
1675   /* We want to record one more frame since some time may have
1676    * elapsed since the last frame
1677    */
1678   clutter_actor_paint (CLUTTER_ACTOR (recorder->stage));
1679 
1680   recorder_remove_update_pointer_timeout (recorder);
1681   recorder_close_pipeline (recorder);
1682 
1683   /* Queue a redraw to remove the recording indicator */
1684   clutter_actor_queue_redraw (CLUTTER_ACTOR (recorder->stage));
1685 
1686   if (recorder->repaint_hook_id != 0)
1687     {
1688       clutter_threads_remove_repaint_func (recorder->repaint_hook_id);
1689       recorder->repaint_hook_id = 0;
1690     }
1691 
1692   recorder_remove_redraw_timeout (recorder);
1693   recorder_close_pipeline (recorder);
1694 
1695   recorder->state = RECORDER_STATE_CLOSED;
1696 
1697   /* Release the refcount we took when we started recording */
1698   g_object_unref (recorder);
1699 }
1700 
1701 /**
1702  * shell_recorder_is_recording:
1703  *
1704  * Determine if recording is currently in progress. (The recorder
1705  * is not paused or closed.)
1706  *
1707  * Return value: %TRUE if the recorder is currently recording.
1708  */
1709 gboolean
1710 shell_recorder_is_recording (ShellRecorder *recorder)
1711 {
1712   g_return_val_if_fail (SHELL_IS_RECORDER (recorder), FALSE);
1713 
1714   return recorder->state == RECORDER_STATE_RECORDING;
1715 }