/* * Copyright (C) 2007 Copyright (C) 2007 daniel g. siegel * * Licensed under the GNU General Public License Version 2 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "cheese-config.h" #include #include #include #include #include #include "cheese.h" #include "cheese-window.h" #include "cheese-pipeline-video.h" #include "cheese-effects-widget.h" #include "cheese-fileutil.h" G_DEFINE_TYPE(PipelineVideo, cheese_pipeline_video, G_TYPE_OBJECT) static GObjectClass *parent_class = NULL; typedef struct _PipelineVideoPrivate PipelineVideoPrivate; struct _PipelineVideoPrivate { gboolean lens_open; gchar *source_pipeline; gchar *used_effect; GstElement *pipeline; GstElement *pipeline_rec; GstElement *ximagesink; GstElement *fakesink; GstElement *source; GstElement *ffmpeg1, *ffmpeg2, *ffmpeg3; GstElement *tee; GstElement *queuedisplay, *queuemovie; GstElement *caps; GstElement *effect; GstElement *audiosrc; GstElement *audioconvert; GstElement *vorbisenc; GstElement *filesink; GstElement *oggmux; GstElement *theoraenc; GstCaps *filter; }; #define PIPELINE_VIDEO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PIPELINE_VIDEO_TYPE, PipelineVideoPrivate)) // private methods static void cheese_pipeline_video_create_display(PipelineVideo *self); static void cheese_pipeline_video_create_rec(PipelineVideo *self); void cheese_pipeline_video_finalize(GObject *object) { PipelineVideoPrivate *priv = PIPELINE_VIDEO_GET_PRIVATE(object); gst_caps_unref(priv->filter); (*parent_class->finalize) (object); return; } PipelineVideo * cheese_pipeline_video_new(void) { PipelineVideo *self = g_object_new(PIPELINE_VIDEO_TYPE, NULL); return self; } void cheese_pipeline_video_class_init(PipelineVideoClass *klass) { GObjectClass *object_class; parent_class = g_type_class_peek_parent(klass); object_class = (GObjectClass*) klass; object_class->finalize = cheese_pipeline_video_finalize; g_type_class_add_private(klass, sizeof(PipelineVideoPrivate)); G_OBJECT_CLASS(klass)->finalize = (GObjectFinalizeFunc) cheese_pipeline_video_finalize; } void cheese_pipeline_video_init(PipelineVideo *self) { } void cheese_pipeline_video_set_play(PipelineVideo *self) { PipelineVideoPrivate *priv = PIPELINE_VIDEO_GET_PRIVATE(self); if (priv->lens_open) { gst_element_set_state(priv->pipeline_rec, GST_STATE_PLAYING); } else { gst_element_set_state(priv->pipeline, GST_STATE_PLAYING); } return; } void cheese_pipeline_video_set_stop(PipelineVideo *self) { PipelineVideoPrivate *priv = PIPELINE_VIDEO_GET_PRIVATE(self); if (priv->lens_open) gst_element_set_state(priv->pipeline_rec, GST_STATE_NULL); else gst_element_set_state(priv->pipeline, GST_STATE_NULL); return; } void cheese_pipeline_video_button_clicked(GtkWidget *widget, gpointer self) { PipelineVideoPrivate *priv = PIPELINE_VIDEO_GET_PRIVATE(self); cheese_pipeline_video_set_stop(self); priv->lens_open = !priv->lens_open; if (priv->lens_open) cheese_pipeline_video_create_rec(self); else cheese_pipeline_video_create_display(self); cheese_pipeline_video_change_effect(priv->used_effect, self); cheese_pipeline_video_set_play(self); return; } GstElement * cheese_pipeline_video_get_ximagesink(PipelineVideo *self) { PipelineVideoPrivate *priv = PIPELINE_VIDEO_GET_PRIVATE(self); return priv->ximagesink; } GstElement * cheese_pipeline_video_get_fakesink(PipelineVideo *self) { PipelineVideoPrivate *priv = PIPELINE_VIDEO_GET_PRIVATE(self); return priv->fakesink; } GstElement * cheese_pipeline_video_get_pipeline(PipelineVideo *self) { PipelineVideoPrivate *priv = PIPELINE_VIDEO_GET_PRIVATE(self); return priv->lens_open ? priv->pipeline_rec : priv->pipeline; } void cheese_pipeline_video_change_effect(gchar *effect, gpointer self) { PipelineVideoPrivate *priv = PIPELINE_VIDEO_GET_PRIVATE(self); if (effect != NULL) { cheese_pipeline_video_set_stop(PIPELINE_VIDEO(self)); gst_element_unlink(priv->ffmpeg1, priv->effect); gst_element_unlink(priv->effect, priv->ffmpeg2); gst_bin_remove(GST_BIN(priv->pipeline_rec), priv->effect); gst_bin_remove(GST_BIN(priv->pipeline), priv->effect); g_print("changing to effect: %s\n", effect); priv->effect = gst_parse_bin_from_description(effect, TRUE, NULL); gst_bin_add(GST_BIN(priv->pipeline_rec), priv->effect); gst_bin_add(GST_BIN(priv->pipeline), priv->effect); gst_element_link(priv->ffmpeg1, priv->effect); gst_element_link(priv->effect, priv->ffmpeg2); cheese_pipeline_video_set_play(self); priv->used_effect = effect; } } void cheese_pipeline_video_create(gchar *source_pipeline, PipelineVideo *self) { PipelineVideoPrivate *priv = PIPELINE_VIDEO_GET_PRIVATE(self); priv->source_pipeline = source_pipeline; cheese_pipeline_video_create_display(self); //cheese_pipeline_video_create_rec(self); } static void cheese_pipeline_video_create_display(PipelineVideo *self) { PipelineVideoPrivate *priv = PIPELINE_VIDEO_GET_PRIVATE(self); gboolean link_ok; priv->pipeline = gst_pipeline_new("pipeline"); priv->source = gst_parse_bin_from_description(priv->source_pipeline, TRUE, NULL); gst_bin_add(GST_BIN(priv->pipeline), priv->source); priv->ffmpeg1 = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace"); gst_bin_add(GST_BIN(priv->pipeline), priv->ffmpeg1); priv->effect = gst_element_factory_make("identity", "effect"); gst_bin_add(GST_BIN(priv->pipeline), priv->effect); priv->ffmpeg2 = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace2"); gst_bin_add(GST_BIN(priv->pipeline), priv->ffmpeg2); priv->ffmpeg3 = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace3"); gst_bin_add(GST_BIN(priv->pipeline), priv->ffmpeg3); priv->tee = gst_element_factory_make("tee", "tee"); gst_bin_add(GST_BIN(priv->pipeline), priv->tee); priv->queuedisplay = gst_element_factory_make("queue", "queuedisplay"); gst_bin_add(GST_BIN(priv->pipeline), priv->queuedisplay); priv->ximagesink = gst_element_factory_make("gconfvideosink", "gconfvideosink"); gst_bin_add(GST_BIN(priv->pipeline), priv->ximagesink); gst_element_link(priv->source, priv->ffmpeg1); gst_element_link(priv->ffmpeg1, priv->effect); gst_element_link(priv->effect, priv->ffmpeg2); // theoraenc needs raw yuv data... priv->filter = gst_caps_new_simple("video/x-raw-yuv", NULL); link_ok = gst_element_link_filtered(priv->ffmpeg2, priv->tee, priv->filter); if (!link_ok) { g_warning("Failed to link elements!"); } gst_element_link(priv->tee, priv->queuedisplay); gst_element_link(priv->queuedisplay, priv->ffmpeg3); gst_element_link(priv->ffmpeg3, priv->ximagesink); } static void cheese_pipeline_video_create_rec(PipelineVideo *self) { PipelineVideoPrivate *priv = PIPELINE_VIDEO_GET_PRIVATE(self); gboolean link_ok; priv->pipeline_rec = gst_pipeline_new("pipeline"); priv->source = gst_parse_bin_from_description(priv->source_pipeline, TRUE, NULL); gst_bin_add(GST_BIN(priv->pipeline_rec), priv->source); priv->ffmpeg1 = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace"); gst_bin_add(GST_BIN(priv->pipeline_rec), priv->ffmpeg1); priv->effect = gst_element_factory_make("identity", "effect"); gst_bin_add(GST_BIN(priv->pipeline_rec), priv->effect); priv->ffmpeg2 = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace2"); gst_bin_add(GST_BIN(priv->pipeline_rec), priv->ffmpeg2); priv->ffmpeg3 = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace3"); gst_bin_add(GST_BIN(priv->pipeline_rec), priv->ffmpeg3); priv->tee = gst_element_factory_make("tee", "tee"); gst_bin_add(GST_BIN(priv->pipeline_rec), priv->tee); priv->queuedisplay = gst_element_factory_make("queue", "queuedisplay"); gst_bin_add(GST_BIN(priv->pipeline_rec), priv->queuedisplay); priv->ximagesink = gst_element_factory_make("gconfvideosink", "gconfvideosink"); gst_bin_add(GST_BIN(priv->pipeline_rec), priv->ximagesink); /* * the pipeline looks like this: * v4l(2)src -> ffmpegcsp * '-> videoscale * '-> ffmpegcsp -> effects -> ffmpegcsp * -------------------------------------------------------' * '--> tee (filtered) -> queue-> ffmpegcsp -> gconfvideosink * | * theoraenc * | * queuemovie -------, * |--------> mux -> filesink * | * vorbisenc * audioconvert--^ * gconfaudiosrc---^ */ gst_element_link(priv->source, priv->ffmpeg1); gst_element_link(priv->ffmpeg1, priv->effect); gst_element_link(priv->effect, priv->ffmpeg2); //gst_element_link(priv->ffmpeg2, priv->tee); // theoraenc needs raw yuv data... priv->filter = gst_caps_new_simple("video/x-raw-yuv", NULL); link_ok = gst_element_link_filtered(priv->ffmpeg2, priv->tee, priv->filter); if (!link_ok) { g_warning("Failed to link elements!"); } gst_element_link(priv->tee, priv->queuedisplay); gst_element_link(priv->queuedisplay, priv->ffmpeg3); gst_element_link(priv->ffmpeg3, priv->ximagesink); priv->audiosrc = gst_element_factory_make("gconfaudiosrc", "gconfaudiosrc"); gst_bin_add(GST_BIN(priv->pipeline_rec), priv->audiosrc); priv->audioconvert = gst_element_factory_make("audioconvert", "audioconvert"); gst_bin_add(GST_BIN(priv->pipeline_rec), priv->audioconvert); priv->vorbisenc = gst_element_factory_make("vorbisenc", "vorbisenc"); gst_bin_add(GST_BIN(priv->pipeline_rec), priv->vorbisenc); priv->oggmux = gst_element_factory_make("oggmux", "oggmux"); gst_bin_add(GST_BIN(priv->pipeline_rec), priv->oggmux); priv->filesink = gst_parse_bin_from_description("filesink location=/tmp/test0.ogg", TRUE, NULL); gst_bin_add(GST_BIN(priv->pipeline_rec), priv->filesink); priv->theoraenc = gst_element_factory_make("theoraenc", "theoraenc"); gst_bin_add(GST_BIN(priv->pipeline_rec), priv->theoraenc); priv->queuemovie = gst_element_factory_make("queue", "queuemovie"); gst_bin_add(GST_BIN(priv->pipeline_rec), priv->queuemovie); gst_element_link(priv->tee, priv->queuemovie); gst_element_link(priv->queuemovie, priv->theoraenc); gst_element_link(priv->tee, priv->theoraenc); gst_element_link(priv->theoraenc, priv->oggmux); priv->filter = gst_caps_new_simple("audio/x-raw-int", "channels", G_TYPE_INT, 2, "rate", G_TYPE_INT, 32000, "depth", G_TYPE_INT, 16, NULL); gst_element_link(priv->audiosrc, priv->audioconvert); //link_ok = gst_element_link_filtered(priv->audiosrc, priv->audioconvert, priv->filter); //link_ok = gst_element_link_filtered(priv->audioconvert, priv->vorbisenc, priv->filter); //if (!link_ok) { // g_warning("Failed to link elements!"); //} gst_element_link(priv->audioconvert, priv->vorbisenc); gst_element_link(priv->vorbisenc, priv->oggmux); gst_element_link(priv->oggmux, priv->filesink); } //void //cheese_pipeline_video_create_video(unsigned char *data, int width, int height) //{ // int i; // gchar *filename = NULL; // GnomeVFSURI *uri; // GdkPixbuf *pixbuf; // // i = 1; // //FIXME: get the video name // filename = cheese_fileutil_get_photo_filename(i); // // uri = gnome_vfs_uri_new(filename); // // while (gnome_vfs_uri_exists(uri)) { // i++; // //FIXME: get the video name // filename = cheese_fileutil_get_photo_filename(i); // g_free(uri); // uri = gnome_vfs_uri_new(filename); // } // g_free(uri); // // pixbuf = gdk_pixbuf_new_from_data (data, GDK_COLORSPACE_RGB, FALSE, 8, // width, height, width * 3, NULL, NULL); // // gdk_pixbuf_save(pixbuf, filename, "jpeg", NULL, NULL); // g_object_unref(G_OBJECT(pixbuf)); // // g_print("Video saved: %s (%dx%d)\n", filename, width, height); //}