1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/gmyth-stream/gmencoder/src/gmencoder.c Tue Sep 18 14:06:39 2007 +0100
1.3 @@ -0,0 +1,1207 @@
1.4 +#ifdef HAVE_CONFIG_H
1.5 +#include "config.h"
1.6 +#endif
1.7 +
1.8 +#include <sys/stat.h>
1.9 +#include <fcntl.h>
1.10 +#include <unistd.h>
1.11 +#include <glib.h>
1.12 +#include <gst/gst.h>
1.13 +#include <string.h>
1.14 +#include <sys/types.h>
1.15 +#include <sys/socket.h>
1.16 +#include <libgnomevfs/gnome-vfs.h>
1.17 +#include <gst/interfaces/tuner.h>
1.18 +
1.19 +#include "gmencoder.h"
1.20 +
1.21 +#define G_MENCODER_GET_PRIVATE(obj) \
1.22 + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), G_TYPE_MENCODER, GMencoderPrivate))
1.23 +
1.24 +#define USE_MANUAL_SINK
1.25 +#define GMENCODER_TIMEOUT 5000
1.26 +
1.27 +typedef struct _GMencoderPrivate GMencoderPrivate;
1.28 +typedef struct _SetupInfo SetupInfo;
1.29 +
1.30 +struct _SetupInfo {
1.31 + gchar *video_encode;
1.32 + gchar *mux_name;
1.33 + gchar **video_encode_prop;
1.34 + gdouble video_fps;
1.35 + gdouble video_rate;
1.36 + guint video_width;
1.37 + guint video_height;
1.38 + gchar *audio_encode;
1.39 + gchar **audio_encode_prop;
1.40 + guint audio_rate;
1.41 +};
1.42 +
1.43 +
1.44 +struct _GMencoderPrivate {
1.45 + GstElement *pipe;
1.46 + GstElement *abin;
1.47 + GstElement *vbin;
1.48 + GstElement *sink;
1.49 + GstElement *src;
1.50 +
1.51 + GnomeVFSHandle *handle;
1.52 +
1.53 + gboolean ready;
1.54 + SetupInfo *info;
1.55 + GstClockTime videot;
1.56 + GstClockTime audiot;
1.57 + gint sources;
1.58 + gint tick_id;
1.59 + gint64 duration;
1.60 + gboolean send_chunked;
1.61 + gint timeout_id;
1.62 +
1.63 + //V4l info
1.64 + GstElement *v4lsrc;
1.65 + gchar *channel;
1.66 + gchar *norm;
1.67 + glong frequency;
1.68 +};
1.69 +
1.70 +enum {
1.71 + PAUSED,
1.72 + PLAYING,
1.73 + STOPED,
1.74 + EOS,
1.75 + ERROR,
1.76 + LAST_SIGNAL
1.77 +};
1.78 +
1.79 +static void g_mencoder_class_init(GMencoderClass * klass);
1.80 +static void g_mencoder_init(GMencoder * object);
1.81 +static void g_mencoder_dispose(GObject * object);
1.82 +static void g_mencoder_finalize(GObject * object);
1.83 +static GstElement *_create_audio_bin(const gchar * encode,
1.84 + gchar ** encode_prop, gint rate);
1.85 +static GstElement *_create_video_bin(const gchar * encode,
1.86 + gchar ** encode_prop,
1.87 + gdouble fps,
1.88 + gint rate, guint width, guint height,
1.89 + gboolean use_deinterlace);
1.90 +
1.91 +static gboolean
1.92 +_pipeline_bus_cb(GstBus * bus, GstMessage * msg, gpointer user_data);
1.93 +
1.94 +static void _decodebin_new_pad_cb(GstElement * object,
1.95 + GstPad * pad,
1.96 + gboolean flag, gpointer user_data);
1.97 +
1.98 +static void _decodebin_unknown_type_cb(GstElement * object,
1.99 + GstPad * pad,
1.100 + GstCaps * caps,
1.101 + gpointer user_data);
1.102 +
1.103 +static void _close_output(GMencoder * self);
1.104 +static gboolean _open_output(GMencoder * self, const gchar * uri);
1.105 +
1.106 +static GstElement *_create_source(GMencoder *self, const gchar * uri);
1.107 +static GstElement *_create_pipeline(GMencoder * self,
1.108 + const gchar * video_encode,
1.109 + const gchar * mux_name,
1.110 + gchar ** video_encode_prop,
1.111 + gdouble video_fps,
1.112 + gdouble video_rate,
1.113 + guint video_width,
1.114 + guint video_height,
1.115 + const gchar * audio_encode,
1.116 + gchar ** audio_encode_prop,
1.117 + guint audio_rate,
1.118 + gboolean deinterlace);
1.119 +static gboolean _process_timeout_cb (gpointer user_data);
1.120 +#ifdef USE_MANUAL_SINK
1.121 +static void _flush_queue (GMencoder *self);
1.122 +static void _buffer_arrive_cb (GstElement* object,
1.123 + GstBuffer* buff,
1.124 + GstPad* pad,
1.125 + gpointer user_data);
1.126 +#endif
1.127 +
1.128 +
1.129 +static gboolean _tick_cb(gpointer data);
1.130 +
1.131 +static guint g_mencoder_signals[LAST_SIGNAL] = { 0 };
1.132 +
1.133 +G_DEFINE_TYPE(GMencoder, g_mencoder, G_TYPE_OBJECT)
1.134 +
1.135 +static void g_mencoder_class_init(GMencoderClass * klass)
1.136 +{
1.137 + GObjectClass *object_class;
1.138 + object_class = (GObjectClass *) klass;
1.139 + g_type_class_add_private(klass, sizeof(GMencoderPrivate));
1.140 +
1.141 + object_class->dispose = g_mencoder_dispose;
1.142 + object_class->finalize = g_mencoder_finalize;
1.143 +
1.144 + g_mencoder_signals[PAUSED] =
1.145 + g_signal_new("paused",
1.146 + G_OBJECT_CLASS_TYPE(object_class),
1.147 + G_SIGNAL_RUN_FIRST,
1.148 + 0, NULL, NULL,
1.149 + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1.150 +
1.151 + g_mencoder_signals[PLAYING] =
1.152 + g_signal_new("playing",
1.153 + G_OBJECT_CLASS_TYPE(object_class),
1.154 + G_SIGNAL_RUN_FIRST,
1.155 + 0, NULL, NULL,
1.156 + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1.157 +
1.158 + g_mencoder_signals[STOPED] =
1.159 + g_signal_new("stoped",
1.160 + G_OBJECT_CLASS_TYPE(object_class),
1.161 + G_SIGNAL_RUN_FIRST,
1.162 + 0, NULL, NULL,
1.163 + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1.164 +
1.165 + g_mencoder_signals[EOS] =
1.166 + g_signal_new("eos",
1.167 + G_OBJECT_CLASS_TYPE(object_class),
1.168 + G_SIGNAL_RUN_FIRST,
1.169 + 0, NULL, NULL,
1.170 + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1.171 +
1.172 + g_mencoder_signals[ERROR] =
1.173 + g_signal_new("error",
1.174 + G_OBJECT_CLASS_TYPE(object_class),
1.175 + G_SIGNAL_RUN_LAST,
1.176 + 0, NULL, NULL,
1.177 + g_cclosure_marshal_VOID__STRING,
1.178 + G_TYPE_NONE, 1, G_TYPE_STRING);
1.179 +}
1.180 +
1.181 +static void
1.182 +g_mencoder_init(GMencoder * self)
1.183 +{
1.184 + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self);
1.185 + priv->info = g_new0(SetupInfo, 1);
1.186 +}
1.187 +
1.188 +static void
1.189 +g_mencoder_dispose(GObject * object)
1.190 +{
1.191 +}
1.192 +
1.193 +static void
1.194 +g_mencoder_finalize(GObject * object)
1.195 +{
1.196 + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(object);
1.197 +
1.198 + // TODO: clear vars
1.199 + g_mencoder_close_stream(G_MENCODER(object));
1.200 + g_free (priv->info);
1.201 +}
1.202 +
1.203 +GMencoder *
1.204 +g_mencoder_new(void)
1.205 +{
1.206 + return g_object_new(G_TYPE_MENCODER, NULL);
1.207 +}
1.208 +
1.209 +
1.210 +static void
1.211 +_obj_set_prop(GObject * obj, const gchar * prop_name,
1.212 + const gchar * prop_val)
1.213 +{
1.214 + GValue p = { 0 };
1.215 + GValue v = { 0 };
1.216 + GParamSpec *s = NULL;
1.217 + GObjectClass *k = G_OBJECT_GET_CLASS(obj);
1.218 +
1.219 +
1.220 + g_value_init(&v, G_TYPE_STRING);
1.221 + g_value_set_string(&v, prop_val);
1.222 +
1.223 + s = g_object_class_find_property(k, prop_name);
1.224 + if (s == NULL) {
1.225 + g_print("Invalid property name: %s\n", prop_name);
1.226 + return;
1.227 + }
1.228 +
1.229 + g_value_init(&p, s->value_type);
1.230 + switch (s->value_type) {
1.231 + case G_TYPE_INT:
1.232 + g_value_set_int(&p, atoi(prop_val));
1.233 + break;
1.234 + case G_TYPE_ULONG:
1.235 + g_value_set_ulong (&p, atol(prop_val));
1.236 + break;
1.237 + case G_TYPE_STRING:
1.238 + g_value_set_string(&p, prop_val);
1.239 + break;
1.240 + case G_TYPE_BOOLEAN:
1.241 + g_value_set_boolean(&p, (gboolean) atoi (prop_val));
1.242 + break;
1.243 + case G_TYPE_DOUBLE:
1.244 + g_value_set_double(&p, atof (prop_val));
1.245 + break;
1.246 + case G_TYPE_FLOAT:
1.247 + g_value_set_float(&p, (float) atof (prop_val));
1.248 + break;
1.249 + default:
1.250 + g_value_set_enum(&p, atoi(prop_val));
1.251 + g_warning ("Property %s of type %s. Not supported using default enum",
1.252 + prop_name, g_type_name (s->value_type));
1.253 + return;
1.254 + }
1.255 +
1.256 + g_object_set_property(obj, prop_name, &p);
1.257 + g_value_unset(&v);
1.258 + g_value_unset(&p);
1.259 +}
1.260 +
1.261 +static GstElement *
1.262 +_create_element_with_prop(const gchar * factory_name,
1.263 + const gchar * element_name, gchar ** prop)
1.264 +{
1.265 + GstElement *ret;
1.266 + int i;
1.267 +
1.268 + ret = gst_element_factory_make(factory_name, element_name);
1.269 + if (ret == NULL)
1.270 + return NULL;
1.271 +
1.272 + if (prop != NULL) {
1.273 + for (i = 0; i < g_strv_length(prop); i++) {
1.274 + if (prop[i] != NULL) {
1.275 + char **v = g_strsplit(prop[i], "=", 2);
1.276 + if (g_strv_length(v) == 2) {
1.277 + _obj_set_prop(G_OBJECT(ret), v[0], v[1]);
1.278 + }
1.279 + g_strfreev(v);
1.280 + }
1.281 + }
1.282 + }
1.283 +
1.284 + return ret;
1.285 +
1.286 +}
1.287 +
1.288 +static GstElement *
1.289 +_create_audio_bin(const gchar * encode, gchar ** encode_prop, gint rate)
1.290 +{
1.291 + GstElement *abin = NULL;
1.292 + GstElement *aqueue = NULL;
1.293 + GstElement *aconvert = NULL;
1.294 + GstElement *aencode = NULL;
1.295 + GstElement *aqueue_src = NULL;
1.296 + GstPad *apad = NULL;
1.297 +
1.298 + // audio/x-raw-int ! queue ! audioconvert ! faac ! rtpmp4gpay !
1.299 + // udpsink name=upd_audio host=224.0.0.1 port=5002
1.300 + abin = gst_bin_new("abin");
1.301 + aqueue = gst_element_factory_make("queue", "aqueue");
1.302 + aconvert = gst_element_factory_make("audioconvert", "aconvert");
1.303 + aencode =
1.304 + _create_element_with_prop((encode ? encode : "lame"), "aencode",
1.305 + encode_prop);
1.306 + aqueue_src = gst_element_factory_make("queue", "aqueue_src");
1.307 +
1.308 + if ((abin == NULL) || (aqueue == NULL) || (aconvert == NULL)
1.309 + || (aencode == NULL) || (aqueue_src == NULL)) {
1.310 + g_warning("Audio elements not found");
1.311 + goto error;
1.312 + }
1.313 +
1.314 + g_object_set(G_OBJECT(aencode), "bitrate", 32, NULL);
1.315 + /*
1.316 + * if (rate > 0) { g_object_set (G_OBJECT (aencode), "bitrate", 32,
1.317 + * NULL); }
1.318 + */
1.319 +
1.320 + gst_bin_add_many(GST_BIN(abin), aqueue, aconvert, aencode, aqueue_src,
1.321 + NULL);
1.322 + if (gst_element_link_many(aqueue, aconvert, aencode, aqueue_src, NULL)
1.323 + == FALSE) {
1.324 + g_warning("Not Link audio elements");
1.325 + }
1.326 + // TODO: apply audio rate
1.327 +
1.328 + // ghost pad the audio bin
1.329 + apad = gst_element_get_pad(aqueue, "sink");
1.330 + gst_element_add_pad(abin, gst_ghost_pad_new("sink", apad));
1.331 + gst_object_unref(apad);
1.332 +
1.333 + apad = gst_element_get_pad(aqueue_src, "src");
1.334 + gst_element_add_pad(abin, gst_ghost_pad_new("src", apad));
1.335 + gst_object_unref(apad);
1.336 +
1.337 + return abin;
1.338 + error:
1.339 + if (abin != NULL)
1.340 + gst_object_unref(abin);
1.341 +
1.342 + if (aqueue != NULL)
1.343 + gst_object_unref(aqueue);
1.344 +
1.345 + if (aconvert != NULL)
1.346 + gst_object_unref(aconvert);
1.347 +
1.348 + if (aencode != NULL)
1.349 + gst_object_unref(aencode);
1.350 +
1.351 + if (aqueue_src != NULL)
1.352 + gst_object_unref(aqueue_src);
1.353 +
1.354 + if (apad != NULL)
1.355 + gst_object_unref(apad);
1.356 +
1.357 + return NULL;
1.358 +}
1.359 +
1.360 +
1.361 +
1.362 +
1.363 +// queue ! videoscale ! video/x-raw-yuv,width=240,height=144 ! colorspace
1.364 +// ! rate ! encode ! queue
1.365 +static GstElement *
1.366 +_create_video_bin(const gchar * encode,
1.367 + gchar ** encode_prop,
1.368 + gdouble fps, gint rate, guint width, guint height,
1.369 + gboolean use_deinterlace)
1.370 +{
1.371 + GstElement *vbin = NULL;
1.372 + GstElement *vqueue = NULL;
1.373 + GstElement *vqueue_src = NULL;
1.374 + GstElement *vcolorspace = NULL;
1.375 + GstElement *vencode = NULL;
1.376 + GstElement *vrate = NULL;
1.377 + GstElement *deinterlace = NULL;
1.378 + GstElement *walk = NULL;
1.379 + GstPad *vpad = NULL;
1.380 +
1.381 + vbin = gst_bin_new("vbin");
1.382 + vqueue = gst_element_factory_make("queue", "vqueue");
1.383 + vcolorspace =
1.384 + gst_element_factory_make("ffmpegcolorspace", "colorspace");
1.385 +
1.386 + if (use_deinterlace) {
1.387 + deinterlace = gst_element_factory_make ("ffdeinterlace", "deinterlace");
1.388 + if (deinterlace == NULL) {
1.389 + g_warning ("Fail to create deinterlace element: Continue without deinterlace.");
1.390 + }
1.391 + }
1.392 +
1.393 +
1.394 + vencode = _create_element_with_prop((encode !=
1.395 + NULL ? encode :
1.396 + "ffenc_mpeg1video"), "vencode",
1.397 + encode_prop);
1.398 + vqueue_src = gst_element_factory_make("queue", "queue_src");
1.399 +
1.400 + if ((vbin == NULL) || (vqueue == NULL) || (vcolorspace == NULL)
1.401 + || (vencode == NULL) || (vqueue_src == NULL)) {
1.402 + g_warning("Video elements not found");
1.403 + goto error;
1.404 + }
1.405 +
1.406 + gst_bin_add_many(GST_BIN(vbin), vqueue, vcolorspace, vencode,
1.407 + vqueue_src, NULL);
1.408 +
1.409 + if (deinterlace != NULL) {
1.410 + gst_bin_add(GST_BIN(vbin), deinterlace);
1.411 + gst_element_link (vqueue, deinterlace);
1.412 + walk = deinterlace;
1.413 + } else {
1.414 + walk = vqueue;
1.415 + }
1.416 +
1.417 + if ((width > 0) && (height > 0)) {
1.418 + // Scalling video
1.419 + GstCaps *vcaps;
1.420 + GstElement *vscale =
1.421 + gst_element_factory_make("videoscale", "vscale");
1.422 +
1.423 + g_object_set (G_OBJECT (vscale), "method", 1, NULL);
1.424 +
1.425 + gst_bin_add(GST_BIN(vbin), vscale);
1.426 +
1.427 + vcaps = gst_caps_new_simple("video/x-raw-yuv",
1.428 + "width", G_TYPE_INT, width,
1.429 + "height", G_TYPE_INT, height, NULL);
1.430 +
1.431 + gst_element_link(walk, vscale);
1.432 +
1.433 + if (gst_element_link_filtered(vscale, vcolorspace, vcaps) == FALSE) {
1.434 + g_warning("Fail to resize video");
1.435 + gst_object_unref(vcaps);
1.436 + gst_object_unref(vscale);
1.437 + goto error;
1.438 + }
1.439 + gst_caps_unref(vcaps);
1.440 + } else {
1.441 + gst_element_link(walk, vcolorspace);
1.442 + }
1.443 +
1.444 + if (fps > 0) {
1.445 + // Changing the video fps
1.446 + GstCaps *vcaps;
1.447 + vrate = gst_element_factory_make("videorate", "vrate");
1.448 +
1.449 + gst_bin_add(GST_BIN(vbin), vrate);
1.450 +
1.451 + if (gst_element_link(vcolorspace, vrate) == FALSE) {
1.452 + g_warning("Fail to link video elements");
1.453 + goto error;
1.454 + }
1.455 +
1.456 + vcaps = gst_caps_new_simple("video/x-raw-yuv",
1.457 + "framerate", GST_TYPE_FRACTION,
1.458 + (int) (fps * 1000), 1000, NULL);
1.459 +
1.460 + if (gst_element_link_filtered(vrate, vencode, vcaps) == FALSE) {
1.461 + g_warning("Fail to link vrate with vencode.");
1.462 + goto error;
1.463 + }
1.464 + gst_caps_unref(vcaps);
1.465 + } else {
1.466 + if (gst_element_link(vcolorspace, vencode) == FALSE) {
1.467 + g_warning("Fail to link colorspace and video encode element.");
1.468 + goto error;
1.469 + }
1.470 + }
1.471 +
1.472 + gst_element_link(vencode, vqueue_src);
1.473 +
1.474 + // ghost pad the video bin
1.475 + vpad = gst_element_get_pad(vqueue, "sink");
1.476 + gst_element_add_pad(vbin, gst_ghost_pad_new("sink", vpad));
1.477 + gst_object_unref(vpad);
1.478 +
1.479 + vpad = gst_element_get_pad(vqueue_src, "src");
1.480 + gst_element_add_pad(vbin, gst_ghost_pad_new("src", vpad));
1.481 + gst_object_unref(vpad);
1.482 +
1.483 + return vbin;
1.484 +
1.485 + error:
1.486 + if (vpad != NULL)
1.487 + gst_object_unref(vpad);
1.488 +
1.489 + if (vbin != NULL)
1.490 + gst_object_unref(vbin);
1.491 +
1.492 + if (vqueue != NULL)
1.493 + gst_object_unref(vqueue);
1.494 +
1.495 + if (vencode != NULL)
1.496 + gst_object_unref(vencode);
1.497 +
1.498 + if (vqueue_src != NULL)
1.499 + gst_object_unref(vqueue_src);
1.500 +
1.501 + if (vcolorspace != NULL)
1.502 + gst_object_unref(vcolorspace);
1.503 +
1.504 + return NULL;
1.505 +}
1.506 +
1.507 +
1.508 +
1.509 +gboolean
1.510 +g_mencoder_setup_stream(GMencoder * self,
1.511 + gboolean chunked,
1.512 + gboolean deinterlace,
1.513 + const gchar * mux_name,
1.514 + const gchar * video_encode,
1.515 + gchar ** video_encode_prop,
1.516 + gdouble video_fps,
1.517 + gdouble video_rate,
1.518 + guint video_width,
1.519 + guint video_height,
1.520 + const gchar * audio_encode,
1.521 + gchar ** audio_encode_prop,
1.522 + guint audio_rate, const gchar * out_uri)
1.523 +{
1.524 + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self);
1.525 + if (priv->ready == TRUE) {
1.526 + g_warning
1.527 + ("Stream already configured. You need close stream first.");
1.528 + return FALSE;
1.529 + }
1.530 +
1.531 + _close_output(self);
1.532 + if (_open_output(self, out_uri) == FALSE) {
1.533 + return FALSE;
1.534 + }
1.535 +
1.536 + priv->sources = 0;
1.537 + priv->send_chunked = chunked;
1.538 + priv->pipe = _create_pipeline(self,
1.539 + video_encode,
1.540 + mux_name,
1.541 + video_encode_prop,
1.542 + video_fps,
1.543 + video_rate,
1.544 + video_width,
1.545 + video_height,
1.546 + audio_encode, audio_encode_prop,
1.547 + audio_rate,
1.548 + deinterlace);
1.549 +
1.550 + return (priv->pipe != NULL);
1.551 +}
1.552 +
1.553 +
1.554 +gboolean
1.555 +g_mencoder_append_uri(GMencoder * self, const gchar * uri)
1.556 +{
1.557 + GstPad *pad_src;
1.558 + GstPad *pad_sink;
1.559 + GstElement *src;
1.560 + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self);
1.561 + gboolean ret = FALSE;
1.562 + GstElement *ap = NULL;
1.563 + GstElement *vp = NULL;
1.564 +
1.565 +
1.566 + g_return_val_if_fail(priv->pipe != NULL, FALSE);
1.567 + g_return_val_if_fail(priv->ready == FALSE, FALSE);
1.568 +
1.569 + src = _create_source(self, uri);
1.570 + if (src == NULL)
1.571 + return FALSE;
1.572 +
1.573 + priv->src = gst_bin_get_by_name(GST_BIN(src), "src");
1.574 +
1.575 + gst_bin_add(GST_BIN(priv->pipe), src);
1.576 +
1.577 + ap = gst_bin_get_by_name(GST_BIN(priv->pipe), "abin");
1.578 + vp = gst_bin_get_by_name(GST_BIN(priv->pipe), "vbin");
1.579 +
1.580 + if ((vp == NULL) || (ap == NULL)) {
1.581 + g_warning("Fail to get output bin");
1.582 + goto error;
1.583 + }
1.584 +
1.585 + pad_src = gst_element_get_pad(src, "src_audio");
1.586 + pad_sink = gst_element_get_compatible_pad(ap,
1.587 + pad_src,
1.588 + gst_pad_get_caps(pad_src));
1.589 +
1.590 + if ((pad_sink == NULL) || (pad_src == NULL))
1.591 + goto error;
1.592 +
1.593 + GstPadLinkReturn lret = gst_pad_link(pad_src, pad_sink);
1.594 + if (lret != GST_PAD_LINK_OK)
1.595 + goto error;
1.596 +
1.597 + gst_object_unref(pad_src);
1.598 + gst_object_unref(pad_sink);
1.599 +
1.600 + pad_src = gst_element_get_pad(src, "src_video");
1.601 + pad_sink = gst_element_get_compatible_pad(vp,
1.602 + pad_src,
1.603 + gst_pad_get_caps(pad_src));
1.604 +
1.605 + if ((pad_src == NULL) || (pad_sink == NULL))
1.606 + goto error;
1.607 +
1.608 + if (gst_pad_link(pad_src, pad_sink) != GST_PAD_LINK_OK) {
1.609 + g_warning("invalid source. video");
1.610 + goto error;
1.611 + }
1.612 +
1.613 + priv->sources++;
1.614 + ret = TRUE;
1.615 + error:
1.616 +
1.617 + if ((src != NULL) && (ret == FALSE)) {
1.618 + gst_bin_remove(GST_BIN(priv->pipe), src);
1.619 + gst_object_unref(src);
1.620 + }
1.621 +
1.622 + if (ap != NULL)
1.623 + gst_object_unref(ap);
1.624 +
1.625 + if (vp != NULL)
1.626 + gst_object_unref(vp);
1.627 +
1.628 + if (pad_src != NULL)
1.629 + gst_object_unref(pad_src);
1.630 +
1.631 + if (pad_sink != NULL)
1.632 + gst_object_unref(pad_sink);
1.633 +
1.634 + return ret;
1.635 +}
1.636 +
1.637 +
1.638 +
1.639 +void
1.640 +g_mencoder_remove_uri(GMencoder * self, const gchar * uri)
1.641 +{
1.642 + // GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE (self);
1.643 + // TODO: remove src
1.644 +}
1.645 +
1.646 +void
1.647 +g_mencoder_play_stream(GMencoder * self)
1.648 +{
1.649 + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self);
1.650 + g_return_if_fail(priv->ready == FALSE);
1.651 + priv->ready = TRUE;
1.652 + gst_element_set_state(priv->pipe, GST_STATE_PLAYING);
1.653 + if (priv->tick_id != 0) {
1.654 + g_source_remove (priv->tick_id);
1.655 + }
1.656 + priv->tick_id = g_timeout_add(500, _tick_cb, self);
1.657 +
1.658 + if (priv->timeout_id != 0) {
1.659 + g_source_remove (priv->timeout_id);
1.660 + }
1.661 + //priv->timeout_id = g_timeout_add(GMENCODER_TIMEOUT, _process_timeout_cb, self);
1.662 +}
1.663 +
1.664 +void
1.665 +g_mencoder_pause_stream(GMencoder * self)
1.666 +{
1.667 + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self);
1.668 + g_return_if_fail(priv->ready == TRUE);
1.669 + gst_element_set_state(priv->pipe, GST_STATE_PAUSED);
1.670 +}
1.671 +
1.672 +void
1.673 +g_mencoder_close_stream(GMencoder * self)
1.674 +{
1.675 +
1.676 + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self);
1.677 + if (priv->tick_id != 0) {
1.678 + g_source_remove(priv->tick_id);
1.679 + priv->tick_id = 0;
1.680 + }
1.681 +
1.682 + if (priv->timeout_id != 0) {
1.683 + g_source_remove (priv->timeout_id);
1.684 + priv->timeout_id = 0;
1.685 + }
1.686 +
1.687 + if (priv->pipe != NULL) {
1.688 + // TODO: fixe pipeline dispose
1.689 + //gst_element_set_state (priv->pipe, GST_STATE_NULL);
1.690 + // g_debug ("SETING STATE TO NULL: OK");
1.691 + // gst_element_set_state (priv->pipe, GST_STATE_NULL);
1.692 + //gst_object_unref (priv->pipe);
1.693 + //gst_object_unref(priv->src);
1.694 + priv->src = NULL;
1.695 + priv->pipe = NULL;
1.696 + priv->abin = NULL;
1.697 + priv->vbin = NULL;
1.698 + priv->sink = NULL;
1.699 + }
1.700 + priv->ready = FALSE;
1.701 +}
1.702 +
1.703 +static GstElement *
1.704 +_create_pipeline(GMencoder * self,
1.705 + const gchar * video_encode,
1.706 + const gchar * mux_name,
1.707 + gchar ** video_encode_prop,
1.708 + gdouble video_fps,
1.709 + gdouble video_rate,
1.710 + guint video_width,
1.711 + guint video_height,
1.712 + const gchar * audio_encode,
1.713 + gchar ** audio_encode_prop, guint audio_rate,
1.714 + gboolean deinterlace)
1.715 +{
1.716 + GstBus *bus = NULL;
1.717 + GstElement *pipe = NULL;
1.718 + GstElement *sink = NULL;
1.719 + GstElement *mux = NULL;
1.720 + GstElement *abin = NULL;
1.721 + GstElement *vbin = NULL;
1.722 + GstElement *queue = NULL;
1.723 + GstPad *aux_pad = NULL;
1.724 + GstPad *mux_pad = NULL;
1.725 +
1.726 + pipe = gst_pipeline_new("pipe");
1.727 +
1.728 + mux =
1.729 + gst_element_factory_make((mux_name ? mux_name : "ffmux_mpeg"),
1.730 + "mux");
1.731 + if (mux == NULL)
1.732 + goto error;
1.733 +
1.734 + queue = gst_element_factory_make("queue", "queueu_sink");
1.735 +
1.736 +
1.737 + sink = gst_element_factory_make("fakesink", "sink");
1.738 + g_object_set (G_OBJECT (sink), "signal-handoffs", TRUE, NULL);
1.739 + g_signal_connect (G_OBJECT (sink),
1.740 + "handoff",
1.741 + G_CALLBACK (_buffer_arrive_cb),
1.742 + self);
1.743 +
1.744 + abin = _create_audio_bin(audio_encode, audio_encode_prop, audio_rate);
1.745 + if (abin == NULL)
1.746 + goto error;
1.747 +
1.748 + vbin =
1.749 + _create_video_bin(video_encode, video_encode_prop, video_fps,
1.750 + video_rate, video_width, video_height, deinterlace);
1.751 + if (vbin == NULL)
1.752 + goto error;
1.753 +
1.754 + // Finish Pipe
1.755 + gst_bin_add_many(GST_BIN(pipe), abin, vbin, mux, queue, sink, NULL);
1.756 +
1.757 +
1.758 + // Link bins with mux
1.759 + aux_pad = gst_element_get_pad(abin, "src");
1.760 + mux_pad =
1.761 + gst_element_get_compatible_pad(mux, aux_pad,
1.762 + GST_PAD_CAPS(aux_pad));
1.763 + if (mux_pad == NULL) {
1.764 + g_warning("Mux element no have audio PAD");
1.765 + goto error;
1.766 + }
1.767 + GstPadLinkReturn ret = gst_pad_link(aux_pad, mux_pad);
1.768 + if (ret != GST_PAD_LINK_OK) {
1.769 + g_warning("Fail link audio and mux: %d", ret);
1.770 + goto error;
1.771 +
1.772 + }
1.773 + gst_object_unref(aux_pad);
1.774 + gst_object_unref(mux_pad);
1.775 +
1.776 + aux_pad = gst_element_get_pad(vbin, "src");
1.777 + mux_pad =
1.778 + gst_element_get_compatible_pad(mux, aux_pad,
1.779 + GST_PAD_CAPS(aux_pad));
1.780 + if (mux_pad == NULL) {
1.781 + g_warning("Mux element no have video PAD");
1.782 + goto error;
1.783 + }
1.784 + ret = gst_pad_link(aux_pad, mux_pad);
1.785 + if (ret != GST_PAD_LINK_OK) {
1.786 + g_warning("Fail link video and mux: %d", ret);
1.787 + goto error;
1.788 + }
1.789 + gst_object_unref(aux_pad);
1.790 + gst_object_unref(mux_pad);
1.791 + aux_pad = NULL;
1.792 + mux_pad = NULL;
1.793 +
1.794 + // Link mux with sink
1.795 + gst_element_link_many(mux, queue, sink, NULL);
1.796 +
1.797 + bus = gst_pipeline_get_bus(GST_PIPELINE(pipe));
1.798 + gst_bus_add_watch(bus, _pipeline_bus_cb, self);
1.799 + gst_object_unref(bus);
1.800 + return pipe;
1.801 +
1.802 + error:
1.803 + g_warning("Invalid uri");
1.804 +
1.805 + if (pipe != NULL) {
1.806 + gst_object_unref(pipe);
1.807 + }
1.808 +
1.809 +
1.810 + if (mux != NULL) {
1.811 + gst_object_unref(mux);
1.812 + }
1.813 +
1.814 + if (mux_pad != NULL) {
1.815 + gst_object_unref(mux_pad);
1.816 + }
1.817 +
1.818 + if (aux_pad != NULL) {
1.819 + gst_object_unref(mux_pad);
1.820 + }
1.821 +
1.822 + if (sink != NULL) {
1.823 + gst_object_unref(sink);
1.824 + }
1.825 +
1.826 + if (abin != NULL) {
1.827 + gst_object_unref(abin);
1.828 + }
1.829 +
1.830 + if (vbin != NULL) {
1.831 + gst_object_unref(vbin);
1.832 + }
1.833 +
1.834 + return FALSE;
1.835 +}
1.836 +
1.837 +
1.838 +static void
1.839 +_close_output(GMencoder * self)
1.840 +{
1.841 +}
1.842 +
1.843 +static GstElement *
1.844 +_create_v4l_source (GMencoder *self, const gchar * uri)
1.845 +{
1.846 + gchar **info;
1.847 + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self);
1.848 +
1.849 +
1.850 + info = g_strsplit (uri+6, ":", 3);
1.851 + if (g_strv_length (info) != 3) {
1.852 + return NULL;
1.853 + }
1.854 +
1.855 + priv->v4lsrc = gst_element_factory_make ("v4l2src", "src");
1.856 + g_debug ("channel %s, norm %s, frequ %s", info[0], info[1], info[2]);
1.857 + g_object_set (G_OBJECT (priv->v4lsrc),
1.858 + "channel", info[0],
1.859 + "norm", info[1],
1.860 + "frequency", atoi (info[2]),
1.861 + NULL);
1.862 +
1.863 + return priv->v4lsrc;
1.864 +}
1.865 +
1.866 +static GstElement *
1.867 +_create_source(GMencoder *self, const gchar * uri)
1.868 +{
1.869 +
1.870 + GstElement *bsrc = NULL;
1.871 + GstElement *src = NULL;
1.872 + GstElement *aqueue = NULL;
1.873 + GstElement *vqueue = NULL;
1.874 + GstElement *decode = NULL;
1.875 + GstPad *src_pad = NULL;
1.876 +
1.877 +
1.878 + bsrc = gst_bin_new(NULL);
1.879 +
1.880 + // src = gst_element_factory_make ("gnomevfssrc", "src");
1.881 + // g_object_set (G_OBJECT (src), "location", uri, NULL);
1.882 + if (strncmp (uri, "v4l://", 6) == 0) {
1.883 + g_debug ("V4L");
1.884 + src = _create_v4l_source (self, uri);
1.885 + }
1.886 + else {
1.887 + src = gst_element_make_from_uri(GST_URI_SRC, uri, "src");
1.888 + }
1.889 +
1.890 + if (src == NULL)
1.891 + goto error;
1.892 +
1.893 + decode = gst_element_factory_make("decodebin2", "decode");
1.894 + if (decode == NULL)
1.895 + goto error;
1.896 +
1.897 + aqueue = gst_element_factory_make("queue", "aqueue");
1.898 + if (aqueue == NULL)
1.899 + goto error;
1.900 +
1.901 + vqueue = gst_element_factory_make("queue", "vqueue");
1.902 + if (vqueue == NULL)
1.903 + goto error;
1.904 +
1.905 + gst_bin_add_many(GST_BIN(bsrc), src, decode, aqueue, vqueue,
1.906 + NULL);
1.907 + gst_element_link (src, decode);
1.908 +
1.909 + g_signal_connect(G_OBJECT(decode),
1.910 + "new-decoded-pad",
1.911 + G_CALLBACK(_decodebin_new_pad_cb), bsrc);
1.912 +
1.913 + g_signal_connect(G_OBJECT(decode),
1.914 + "unknown-type",
1.915 + G_CALLBACK(_decodebin_unknown_type_cb), pipe);
1.916 +
1.917 + src_pad = gst_element_get_pad(aqueue, "src");
1.918 + gst_element_add_pad(bsrc, gst_ghost_pad_new("src_audio", src_pad));
1.919 + gst_object_unref(src_pad);
1.920 +
1.921 + src_pad = gst_element_get_pad(vqueue, "src");
1.922 + gst_element_add_pad(bsrc, gst_ghost_pad_new("src_video", src_pad));
1.923 + gst_object_unref(src_pad);
1.924 +
1.925 + return bsrc;
1.926 +
1.927 + error:
1.928 + g_debug ("Fail to create source element");
1.929 + if (src != NULL) {
1.930 + gst_object_unref(src);
1.931 + }
1.932 +
1.933 + if (decode != NULL) {
1.934 + gst_object_unref(decode);
1.935 + }
1.936 +
1.937 + if (aqueue != NULL) {
1.938 + gst_object_unref(aqueue);
1.939 + }
1.940 +
1.941 + if (vqueue != NULL) {
1.942 + gst_object_unref(vqueue);
1.943 + }
1.944 +
1.945 + return NULL;
1.946 +}
1.947 +
1.948 +static gboolean
1.949 +_open_output(GMencoder * self, const gchar * uri)
1.950 +{
1.951 + gchar **i;
1.952 + GnomeVFSResult result;
1.953 + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self);
1.954 +
1.955 + i = g_strsplit(uri, "://", 0);
1.956 + if (strcmp(i[0], "fd") == 0) {
1.957 + result = gnome_vfs_open_fd (&priv->handle, atoi(i[1]));
1.958 + } else {
1.959 + if (g_file_test (i[1], G_FILE_TEST_EXISTS) == FALSE) {
1.960 + result = gnome_vfs_create (&priv->handle, uri, GNOME_VFS_OPEN_WRITE, FALSE,
1.961 + GNOME_VFS_PERM_USER_WRITE | GNOME_VFS_PERM_USER_READ | GNOME_VFS_PERM_GROUP_READ);
1.962 + } else {
1.963 + result = gnome_vfs_open (&priv->handle, uri,
1.964 + GNOME_VFS_OPEN_WRITE | GNOME_VFS_OPEN_TRUNCATE);
1.965 + }
1.966 + }
1.967 +
1.968 + g_strfreev(i);
1.969 + return (result == GNOME_VFS_OK);
1.970 +}
1.971 +
1.972 +static gboolean
1.973 +_pipeline_bus_cb(GstBus * bus, GstMessage * msg, gpointer user_data)
1.974 +{
1.975 + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(user_data);
1.976 +
1.977 + switch (GST_MESSAGE_TYPE(msg)) {
1.978 +
1.979 + case GST_MESSAGE_STATE_CHANGED:
1.980 + {
1.981 + GstState oldstate;
1.982 + GstState newstate;
1.983 + GstState pendingstate;
1.984 +
1.985 +
1.986 + gst_message_parse_state_changed(msg, &oldstate,
1.987 + &newstate, &pendingstate);
1.988 +
1.989 + if (pendingstate != GST_STATE_VOID_PENDING)
1.990 + break;
1.991 +
1.992 + if ((oldstate == GST_STATE_READY)
1.993 + && (newstate == GST_STATE_PAUSED)) {
1.994 + if (priv->ready)
1.995 + g_signal_emit(user_data, g_mencoder_signals[PAUSED],
1.996 + 0);
1.997 + } else if ((oldstate == GST_STATE_PAUSED)
1.998 + && (newstate == GST_STATE_PLAYING)) {
1.999 + g_signal_emit(user_data, g_mencoder_signals[PLAYING], 0);
1.1000 + } else if ((oldstate == GST_STATE_READY) &&
1.1001 + (newstate == GST_STATE_NULL)) {
1.1002 + g_signal_emit(user_data, g_mencoder_signals[STOPED], 0);
1.1003 + }
1.1004 + break;
1.1005 + }
1.1006 +
1.1007 + case GST_MESSAGE_ERROR:
1.1008 + {
1.1009 + GError *error;
1.1010 + gchar *debug;
1.1011 + gchar *err_str;
1.1012 +
1.1013 + if (priv->tick_id != 0) {
1.1014 + g_source_remove(priv->tick_id);
1.1015 + priv->tick_id = 0;
1.1016 + }
1.1017 +
1.1018 + gst_message_parse_error(msg, &error, &debug);
1.1019 + err_str = g_strdup_printf("Error [%d] %s (%s)", error->code,
1.1020 + error->message, debug);
1.1021 + priv->ready = FALSE;
1.1022 + g_signal_emit(user_data, g_mencoder_signals[ERROR], 0,
1.1023 + err_str);
1.1024 + g_free(err_str);
1.1025 + g_clear_error(&error);
1.1026 + g_free(debug);
1.1027 + break;
1.1028 + }
1.1029 +
1.1030 + case GST_MESSAGE_EOS:
1.1031 + priv->ready = FALSE;
1.1032 +#ifdef USE_MANUAL_SINK
1.1033 + _flush_queue (G_MENCODER (user_data));
1.1034 +#endif
1.1035 + g_signal_emit(user_data, g_mencoder_signals[EOS], 0);
1.1036 + break;
1.1037 +
1.1038 + case GST_MESSAGE_DURATION:
1.1039 + {
1.1040 + GstFormat format;
1.1041 + gint64 duration;
1.1042 + gst_message_parse_duration(msg, &format, &duration);
1.1043 + if (format == GST_FORMAT_BYTES)
1.1044 + priv->duration = duration;
1.1045 + break;
1.1046 + }
1.1047 + default:
1.1048 + {
1.1049 + break;
1.1050 + }
1.1051 + }
1.1052 + return TRUE;
1.1053 +}
1.1054 +
1.1055 +
1.1056 +
1.1057 +static void
1.1058 +_decodebin_new_pad_cb(GstElement * object,
1.1059 + GstPad * pad, gboolean flag, gpointer user_data)
1.1060 +{
1.1061 + GstCaps *caps;
1.1062 + gchar *str_caps = NULL;
1.1063 + GstElement *sink_element;
1.1064 + GstPad *sink_pad;
1.1065 +
1.1066 + caps = gst_pad_get_caps(pad);
1.1067 + str_caps = gst_caps_to_string(caps);
1.1068 + if (strstr(str_caps, "audio") != NULL) {
1.1069 + sink_element = gst_bin_get_by_name(GST_BIN(user_data), "aqueue");
1.1070 + } else if (strstr(str_caps, "video") != NULL) {
1.1071 + sink_element = gst_bin_get_by_name(GST_BIN(user_data), "vqueue");
1.1072 + } else {
1.1073 + g_warning("invalid caps %s", str_caps);
1.1074 + }
1.1075 +
1.1076 + sink_pad = gst_element_get_pad(sink_element, "sink");
1.1077 + gst_pad_link(pad, sink_pad);
1.1078 +
1.1079 + gst_object_unref(sink_element);
1.1080 + gst_object_unref(sink_pad);
1.1081 + g_free(str_caps);
1.1082 + gst_caps_unref(caps);
1.1083 +}
1.1084 +
1.1085 +static void
1.1086 +_decodebin_unknown_type_cb(GstElement * object,
1.1087 + GstPad * pad, GstCaps * caps,
1.1088 + gpointer user_data)
1.1089 +{
1.1090 + g_warning("Unknown Type");
1.1091 + // priv->ready = FALSE;
1.1092 +}
1.1093 +
1.1094 +static gboolean
1.1095 +_tick_cb(gpointer user_data)
1.1096 +{
1.1097 + GstFormat format = GST_FORMAT_BYTES;
1.1098 + gint64 cur = 0;
1.1099 +
1.1100 + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(user_data);
1.1101 +
1.1102 + if (priv->duration == 0) {
1.1103 + gint64 d = 0;
1.1104 + if (gst_element_query_duration(priv->src, &format, &d))
1.1105 + priv->duration = d;
1.1106 + }
1.1107 +
1.1108 + if (priv->duration != 0) {
1.1109 + gst_element_query_position(priv->src, &format, &cur);
1.1110 + g_print("PROGRESS:%lli\n", (99 * cur) / priv->duration);
1.1111 + }
1.1112 +
1.1113 + return TRUE;
1.1114 +}
1.1115 +
1.1116 +static gboolean
1.1117 +_process_timeout_cb (gpointer user_data)
1.1118 +{
1.1119 + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(user_data);
1.1120 +
1.1121 + g_signal_emit(user_data, g_mencoder_signals[ERROR], 0, "timeout");
1.1122 + priv->timeout_id = 0;
1.1123 + return FALSE;
1.1124 +}
1.1125 +
1.1126 +
1.1127 +#ifdef USE_MANUAL_SINK
1.1128 +static gboolean
1.1129 +_send_buffer (GnomeVFSHandle *handle, gpointer buff, gint size)
1.1130 +{
1.1131 + gchar *msg;
1.1132 + GByteArray *b_send;
1.1133 + GnomeVFSResult result;
1.1134 + GnomeVFSFileSize bytes_written;
1.1135 +
1.1136 + b_send = g_byte_array_new ();
1.1137 + msg = g_strdup_printf ("%x\r\n", size);
1.1138 + b_send = g_byte_array_append (b_send, (const guint8*) msg, strlen (msg) * sizeof (gchar));
1.1139 + g_free (msg);
1.1140 +
1.1141 + b_send = g_byte_array_append (b_send, buff, size);
1.1142 +
1.1143 + msg = g_strdup ("\r\n");
1.1144 + b_send = g_byte_array_append (b_send, (const guint8*) msg, strlen (msg) * sizeof (gchar));
1.1145 + g_free (msg);
1.1146 +
1.1147 + result = gnome_vfs_write (handle, b_send->data, b_send->len, &bytes_written);
1.1148 + g_byte_array_free (b_send, TRUE);
1.1149 +
1.1150 + return (result == GNOME_VFS_OK);
1.1151 +}
1.1152 +
1.1153 +static void
1.1154 +_flush_queue (GMencoder *self)
1.1155 +{
1.1156 + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self);
1.1157 +
1.1158 + if (priv->send_chunked) {
1.1159 + GnomeVFSFileSize bytes_written;
1.1160 + gchar *end_msg;
1.1161 + end_msg = g_strdup ("0\r\n\r\n");
1.1162 + gnome_vfs_write (priv->handle,
1.1163 + (const guint8*) end_msg,
1.1164 + strlen(end_msg) * sizeof(gchar),
1.1165 + &bytes_written);
1.1166 + g_free (end_msg);
1.1167 + }
1.1168 +}
1.1169 +
1.1170 +static void
1.1171 +_buffer_arrive_cb (GstElement* object,
1.1172 + GstBuffer* buff,
1.1173 + GstPad* pad,
1.1174 + gpointer user_data)
1.1175 +{
1.1176 + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(user_data);
1.1177 +
1.1178 + if (priv->timeout_id != 0) {
1.1179 + g_source_remove (priv->timeout_id);
1.1180 + priv->timeout_id = 0;
1.1181 + }
1.1182 +
1.1183 + if (priv->send_chunked) {
1.1184 + if (_send_buffer (priv->handle, GST_BUFFER_DATA (buff), GST_BUFFER_SIZE (buff)) == FALSE)
1.1185 + goto error;
1.1186 + } else {
1.1187 + GnomeVFSResult result;
1.1188 + GnomeVFSFileSize bytes_written;
1.1189 +
1.1190 + result = gnome_vfs_write (priv->handle,
1.1191 + GST_BUFFER_DATA (buff),
1.1192 + GST_BUFFER_SIZE (buff),
1.1193 + &bytes_written);
1.1194 +
1.1195 + if (result != GNOME_VFS_OK)
1.1196 + goto error;
1.1197 + }
1.1198 +
1.1199 + return;
1.1200 +
1.1201 +error:
1.1202 + if (priv->tick_id != 0) {
1.1203 + g_source_remove(priv->tick_id);
1.1204 + priv->tick_id = 0;
1.1205 + }
1.1206 + g_signal_emit(user_data, g_mencoder_signals[ERROR], 0, "Fail to write on socket");
1.1207 + gst_element_set_state (priv->pipe, GST_STATE_PAUSED);
1.1208 +}
1.1209 +
1.1210 +#endif