implement input-mode property to switch between live, hdmi-in and background encoding
[gst-plugin-dreamsource.git] / src / gstdreamvideosource.c
1 /*
2  * GStreamer dreamvideosource
3  * Copyright 2014-2015 Andreas Frisch <fraxinas@opendreambox.org>
4  *
5  * This program is licensed under the Creative Commons
6  * Attribution-NonCommercial-ShareAlike 3.0 Unported
7  * License. To view a copy of this license, visit
8  * http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to
9  * Creative Commons,559 Nathan Abbott Way,Stanford,California 94305,USA.
10  *
11  * Alternatively, this program may be distributed and executed on
12  * hardware which is licensed by Dream Property GmbH.
13  *
14  * This program is NOT free software. It is open source, you are allowed
15  * to modify it (if you keep the license), but it may not be commercially
16  * distributed other than under the conditions noted above.
17  */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <gst/gst.h>
24 #include "gstdreamvideosource.h"
25
26 GST_DEBUG_CATEGORY_STATIC (dreamvideosource_debug);
27 #define GST_CAT_DEFAULT dreamvideosource_debug
28
29 GType gst_dreamvideosource_input_mode_get_type (void)
30 {
31         static volatile gsize input_mode_type = 0;
32         static const GEnumValue input_mode[] = {
33                 {GST_DREAMVIDEOSOURCE_INPUT_MODE_LIVE, "GST_DREAMVIDEOSOURCE_INPUT_MODE_LIVE", "live"},
34                 {GST_DREAMVIDEOSOURCE_INPUT_MODE_HDMI_IN, "GST_DREAMVIDEOSOURCE_INPUT_MODE_HDMI_IN", "hdmi_in"},
35                 {GST_DREAMVIDEOSOURCE_INPUT_MODE_BACKGROUND, "GST_DREAMVIDEOSOURCE_INPUT_MODE_BACKGROUND", "background"},
36                 {0, NULL, NULL},
37         };
38
39         if (g_once_init_enter (&input_mode_type)) {
40                 GType tmp = g_enum_register_static ("GstDreamVideoSourceInputMode", input_mode);
41                 g_once_init_leave (&input_mode_type, tmp);
42         }
43         return (GType) input_mode_type;
44 }
45
46 enum
47 {
48         SIGNAL_GET_BASE_PTS,
49         LAST_SIGNAL
50 };
51
52 enum
53 {
54         ARG_0,
55         ARG_CAPS,
56         ARG_BITRATE,
57         ARG_INPUT_MODE
58 };
59
60 static guint gst_dreamvideosource_signals[LAST_SIGNAL] = { 0 };
61
62 #define DEFAULT_BITRATE    2048
63 #define DEFAULT_FRAMERATE  25
64 #define DEFAULT_WIDTH      1280
65 #define DEFAULT_HEIGHT     720
66 #define DEFAULT_INPUT_MODE GST_DREAMVIDEOSOURCE_INPUT_MODE_LIVE
67
68 static GstStaticPadTemplate srctemplate =
69     GST_STATIC_PAD_TEMPLATE ("src",
70         GST_PAD_SRC,
71         GST_PAD_ALWAYS,
72         GST_STATIC_CAPS ("video/x-h264, "
73         "width = { 720, 1280, 1920 }, "
74         "height = { 576, 720, 1080 }, "
75         "framerate = { 25/1, 30/1, 50/1, 60/1 }, "
76         "pixel-aspect-ratio = { 5/4, 16/9 }, "
77         "stream-format = (string) byte-stream, "
78         "profile = (string) main")
79     );
80
81 #define gst_dreamvideosource_parent_class parent_class
82 G_DEFINE_TYPE (GstDreamVideoSource, gst_dreamvideosource, GST_TYPE_PUSH_SRC);
83
84 static GstCaps *gst_dreamvideosource_getcaps (GstBaseSrc * psrc, GstCaps * filter);
85 static gboolean gst_dreamvideosource_setcaps (GstBaseSrc * bsrc, GstCaps * caps);
86 static GstCaps *gst_dreamvideosource_fixate (GstBaseSrc * bsrc, GstCaps * caps);
87
88 static gboolean gst_dreamvideosource_start (GstBaseSrc * bsrc);
89 static gboolean gst_dreamvideosource_stop (GstBaseSrc * bsrc);
90 static gboolean gst_dreamvideosource_unlock (GstBaseSrc * bsrc);
91 static void gst_dreamvideosource_dispose (GObject * gobject);
92 static GstFlowReturn gst_dreamvideosource_create (GstPushSrc * psrc, GstBuffer ** outbuf);
93
94 static void gst_dreamvideosource_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
95 static void gst_dreamvideosource_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
96
97 static GstStateChangeReturn gst_dreamvideosource_change_state (GstElement * element, GstStateChange transition);
98 static gint64 gst_dreamvideosource_get_base_pts (GstDreamVideoSource *self);
99
100 static void
101 gst_dreamvideosource_class_init (GstDreamVideoSourceClass * klass)
102 {
103         GObjectClass *gobject_class;
104         GstElementClass *gstelement_class;
105         GstBaseSrcClass *gstbsrc_class;
106         GstPushSrcClass *gstpush_src_class;
107
108         gobject_class = (GObjectClass *) klass;
109         gstelement_class = (GstElementClass *) klass;
110         gstbsrc_class = (GstBaseSrcClass *) klass;
111         gstpush_src_class = (GstPushSrcClass *) klass;
112
113         gobject_class->set_property = gst_dreamvideosource_set_property;
114         gobject_class->get_property = gst_dreamvideosource_get_property;
115         gobject_class->dispose = gst_dreamvideosource_dispose;
116
117         gst_element_class_add_pad_template (gstelement_class,
118                                             gst_static_pad_template_get (&srctemplate));
119
120         gst_element_class_set_static_metadata (gstelement_class,
121             "Dream Video source", "Source/Video",
122             "Provide an h.264 video elementary stream from Dreambox encoder device",
123             "Andreas Frisch <fraxinas@opendreambox.org>");
124
125         gstelement_class->change_state = gst_dreamvideosource_change_state;
126
127         gstbsrc_class->get_caps = gst_dreamvideosource_getcaps;
128         gstbsrc_class->set_caps = gst_dreamvideosource_setcaps;
129         gstbsrc_class->fixate = gst_dreamvideosource_fixate;
130         gstbsrc_class->start = gst_dreamvideosource_start;
131         gstbsrc_class->stop = gst_dreamvideosource_stop;
132         gstbsrc_class->unlock = gst_dreamvideosource_unlock;
133
134         gstpush_src_class->create = gst_dreamvideosource_create;
135
136         g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BITRATE,
137           g_param_spec_int ("bitrate", "Bitrate (kb/s)",
138             "Bitrate in kbit/sec", 16, 200000, DEFAULT_BITRATE,
139             G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
140
141         g_object_class_install_property (gobject_class, ARG_CAPS,
142           g_param_spec_boxed ("caps", "Caps",
143             "The caps for the source stream", GST_TYPE_CAPS,
144             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
145
146         g_object_class_install_property (gobject_class, ARG_INPUT_MODE,
147           g_param_spec_enum ("input-mode", "Input Mode",
148                 "Select the input source of the video stream",
149                 GST_TYPE_DREAMVIDEOSOURCE_INPUT_MODE, DEFAULT_INPUT_MODE,
150                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
151
152         gst_dreamvideosource_signals[SIGNAL_GET_BASE_PTS] =
153                 g_signal_new ("get-base-pts",
154                 G_TYPE_FROM_CLASS (klass),
155                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
156                 G_STRUCT_OFFSET (GstDreamVideoSourceClass, get_base_pts),
157                 NULL, NULL, gst_dreamsource_marshal_INT64__VOID, G_TYPE_INT64, 0);
158
159         klass->get_base_pts = gst_dreamvideosource_get_base_pts;
160 }
161
162 static gint64
163 gst_dreamvideosource_get_base_pts (GstDreamVideoSource *self)
164 {
165         GST_DEBUG_OBJECT (self, "gst_dreamvideosource_get_base_pts %" GST_TIME_FORMAT"", GST_TIME_ARGS (self->base_pts) );
166         return self->base_pts;
167 }
168
169 static void gst_dreamvideosource_set_bitrate (GstDreamVideoSource * self, uint32_t bitrate)
170 {
171         if (!self->encoder || !self->encoder->fd)
172                 return;
173         g_mutex_lock (&self->mutex);
174         uint32_t vbr = bitrate*1000;
175         int ret = ioctl(self->encoder->fd, VENC_SET_BITRATE, &vbr);
176         if (ret != 0)
177         {
178                 GST_WARNING_OBJECT (self, "can't set video bitrate to %i bytes/s!", vbr);
179                 g_mutex_unlock (&self->mutex);
180                 return;
181         }
182         GST_INFO_OBJECT (self, "set video bitrate to %i kBytes/s", bitrate);
183         self->video_info.bitrate = vbr;
184         g_mutex_unlock (&self->mutex);
185 }
186
187 static gboolean gst_dreamvideosource_set_format (GstDreamVideoSource * self, VideoFormatInfo * info)
188 {
189         if (!self->encoder || !self->encoder->fd)
190         {
191                 GST_ERROR_OBJECT (self, "can't set format because encoder device not opened!");
192                 return FALSE;
193         }
194
195         GST_OBJECT_LOCK (self);
196
197         if (info->fps_n > 0)
198         {
199                 int venc_fps = 0;
200                 switch (info->fps_n) {
201                         case 25:
202                                 venc_fps = rate_25;
203                                 break;
204                         case 30:
205                                 venc_fps = rate_30;
206                                 break;
207                         case 50:
208                                 venc_fps = rate_50;
209                                 break;
210                         case 60:
211                                 venc_fps = rate_60;
212                                 break;
213                         default:
214                                 GST_ERROR_OBJECT (self, "invalid framerate %d/%d", info->fps_n, info->fps_d);
215                                 goto fail;
216                 }
217                 if (!ioctl(self->encoder->fd, VENC_SET_FRAMERATE, &venc_fps))
218                         GST_INFO_OBJECT (self, "set framerate to %d/%d -> ioctrl(%d, VENC_SET_FRAMERATE, &%d)", info->fps_n, info->fps_d, self->encoder->fd, venc_fps);
219                 else
220                 {
221                         GST_WARNING_OBJECT (self, "can't set framerate to %d/%d -> ioctrl(%d, VENC_SET_FRAMERATE, &%d)", info->fps_n, info->fps_d, self->encoder->fd, venc_fps);
222                         goto fail;
223                 }
224         }
225
226         if (info->width && info->height)
227         {
228                 int venc_size = 0;
229                 if ( info->width == 720 && info->height == 576 )
230                         venc_size = fmt_720x576;
231                 else if ( info->width == 1280 && info->height == 720)
232                         venc_size = fmt_1280x720;
233                 else if ( info->width == 1920 && info->height == 1080)
234                         venc_size = fmt_1920x1080;
235                 else
236                 {
237                         GST_ERROR_OBJECT (self, "invalid resolution %dx%d", info->width, info->height);
238                         goto fail;
239                 }
240                 if (!ioctl(self->encoder->fd, VENC_SET_RESOLUTION, &venc_size))
241                         GST_INFO_OBJECT (self, "set resolution to %dx%d -> ioctrl(%d, VENC_SET_RESOLUTION, &%d)", info->width, info->height, self->encoder->fd, venc_size);
242                 else
243                 {
244                         GST_WARNING_OBJECT (self, "can't set resolution to %dx%d -> ioctrl(%d, VENC_SET_RESOLUTION, &%d)", info->width, info->height, self->encoder->fd, venc_size);
245                         goto fail;
246                 }
247         }
248
249         self->video_info = *info;
250         GST_OBJECT_UNLOCK (self);
251         return TRUE;
252
253 fail:
254         GST_OBJECT_UNLOCK (self);
255         return FALSE;
256 }
257
258 void gst_dreamvideosource_set_input_mode (GstDreamVideoSource *self, GstDreamVideoSourceInputMode mode)
259 {
260         g_return_if_fail (GST_IS_DREAMVIDEOSOURCE (self));
261         GEnumValue *val = g_enum_get_value (G_ENUM_CLASS (g_type_class_ref (GST_TYPE_DREAMVIDEOSOURCE_INPUT_MODE)), mode);
262         if (!val)
263         {
264                 GST_ERROR_OBJECT (self, "no such input_mode %i!", mode);
265                 goto out;
266         }
267         const gchar *value_nick = val->value_nick;
268         GST_DEBUG_OBJECT (self, "setting input_mode to %s (%i)...", value_nick, mode);
269
270         g_mutex_lock (&self->mutex);
271         if (!self->encoder || !self->encoder->fd)
272         {
273                 GST_ERROR_OBJECT (self, "can't set input mode because encoder device not opened!");
274                 goto out;
275         }
276         int int_mode = mode;
277         int ret = ioctl(self->encoder->fd, VENC_SET_SOURCE, &int_mode);
278         if (ret != 0)
279         {
280                 GST_WARNING_OBJECT (self, "can't set input mode to %s (%i) error: %s", value_nick, mode, strerror(errno));
281                 goto out;
282         }
283         GST_INFO_OBJECT (self, "successfully set input mode to %s (%i)", value_nick, mode);
284         self->input_mode = mode;
285 out:
286         g_mutex_unlock (&self->mutex);
287         return;
288 }
289
290 GstDreamVideoSourceInputMode gst_dreamvideosource_get_input_mode (GstDreamVideoSource *self)
291 {
292         GstDreamVideoSourceInputMode result;
293         g_return_val_if_fail (GST_IS_DREAMVIDEOSOURCE (self), -1);
294         GST_OBJECT_LOCK (self);
295         result =self->input_mode;
296         GST_OBJECT_UNLOCK (self);
297         return result;
298 }
299
300 gboolean
301 gst_dreamvideosource_plugin_init (GstPlugin *plugin)
302 {
303         GST_DEBUG_CATEGORY_INIT (dreamvideosource_debug, "dreamvideosource", 0, "dreamvideosource");
304         return gst_element_register (plugin, "dreamvideosource", GST_RANK_PRIMARY, GST_TYPE_DREAMVIDEOSOURCE);
305 }
306
307 static void
308 gst_dreamvideosource_init (GstDreamVideoSource * self)
309 {
310         GstPadTemplate *pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS(self), "src");
311         self->current_caps = gst_pad_template_get_caps (pad_template);
312
313         self->encoder = NULL;
314         self->descriptors_available = 0;
315         self->input_mode = DEFAULT_INPUT_MODE;
316
317         self->buffers_in_use = 0;
318
319         g_mutex_init (&self->mutex);
320         READ_SOCKET (self) = -1;
321         WRITE_SOCKET (self) = -1;
322
323         gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
324         gst_base_src_set_live (GST_BASE_SRC (self), TRUE);
325
326         self->encoder = malloc(sizeof(EncoderInfo));
327
328         if(!self->encoder) {
329                 GST_ERROR_OBJECT(self,"out of space");
330                 return;
331         }
332
333         char fn_buf[32];
334         sprintf(fn_buf, "/dev/venc%d", 0);
335         self->encoder->fd = open(fn_buf, O_RDWR | O_SYNC);
336         if(self->encoder->fd <= 0) {
337                 GST_ERROR_OBJECT(self,"cannot open device %s (%s)", fn_buf, strerror(errno));
338                 free(self->encoder);
339                 self->encoder = NULL;
340                 return;
341         }
342
343         self->encoder->buffer = malloc(VBUFSIZE);
344         if(!self->encoder->buffer) {
345                 GST_ERROR_OBJECT(self,"cannot alloc buffer");
346                 return;
347         }
348
349         self->encoder->cdb = (unsigned char *)mmap(0, VMMAPSIZE, PROT_READ, MAP_PRIVATE, self->encoder->fd, 0);
350
351         if(!self->encoder->cdb) {
352                 GST_ERROR_OBJECT(self,"cannot mmap cdb");
353                 return;
354         }
355
356 #ifdef dump
357         self->dumpfd = open("/media/hdd/movie/dreamvideosource.dump", O_WRONLY | O_CREAT | O_TRUNC);
358         GST_DEBUG_OBJECT (self, "dumpfd = %i (%s)", self->dumpfd, (self->dumpfd > 0) ? "OK" : strerror(errno));
359 #endif
360 }
361
362 static void
363 gst_dreamvideosource_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
364 {
365         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (object);
366
367         switch (prop_id) {
368                 case ARG_CAPS:
369                 {
370                         GstCaps *caps = gst_caps_copy (gst_value_get_caps (value));
371                         gst_dreamvideosource_setcaps(GST_BASE_SRC(object), caps);
372                         gst_caps_unref (caps);
373                         break;
374                 }
375                 case ARG_BITRATE:
376                         gst_dreamvideosource_set_bitrate(self, g_value_get_int (value));
377                         break;
378                 case ARG_INPUT_MODE:
379                         gst_dreamvideosource_set_input_mode (self, g_value_get_enum (value));
380                         break;
381                 default:
382                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
383                         break;
384         }
385 }
386
387 static void
388 gst_dreamvideosource_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
389 {
390         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (object);
391
392         switch (prop_id) {
393                 case ARG_CAPS:
394                         g_value_take_boxed (value, gst_dreamvideosource_getcaps (GST_BASE_SRC(object), GST_CAPS_ANY));
395                         break;
396                 case ARG_BITRATE:
397                         g_value_set_int (value, self->video_info.bitrate/1000);
398                         break;
399                 case ARG_INPUT_MODE:
400                         g_value_set_enum (value, gst_dreamvideosource_get_input_mode (self));
401                         break;
402                 default:
403                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
404                         break;
405         }
406 }
407
408 static GstCaps *
409 gst_dreamvideosource_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
410 {
411         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (bsrc);
412         GstCaps *caps = gst_caps_copy(self->current_caps);
413
414         GST_LOG_OBJECT (self, "gst_dreamvideosource_getcaps %" GST_PTR_FORMAT " filter %" GST_PTR_FORMAT, caps, filter);
415
416         if (filter) {
417                 GstCaps *intersection;
418                 intersection = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
419                 gst_caps_unref (caps);
420                 caps = intersection;
421         }
422
423         GST_DEBUG_OBJECT (self, "return caps %" GST_PTR_FORMAT, caps);
424         return caps;
425 }
426
427 static gboolean
428 gst_dreamvideosource_setcaps (GstBaseSrc * bsrc, GstCaps * caps)
429 {
430         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (bsrc);
431         GstBaseSrcClass *bclass = GST_BASE_SRC_GET_CLASS (bsrc);
432         GstCaps *current_caps;
433         const GstStructure *structure;
434         VideoFormatInfo info;
435         gboolean ret;
436         int width, height;
437         const GValue *framerate, *par;
438         structure = gst_caps_get_structure (caps, 0);
439
440         current_caps = gst_pad_get_current_caps (GST_BASE_SRC_PAD (bsrc));
441         if (current_caps && gst_caps_is_equal (current_caps, caps)) {
442                 GST_DEBUG_OBJECT (self, "New caps equal to old ones: %" GST_PTR_FORMAT, caps);
443                 ret = TRUE;
444         } else {
445                 GstState state;
446                 gst_element_get_state (GST_ELEMENT(self), &state, NULL, 1*GST_MSECOND);
447                 if (state == GST_STATE_PLAYING)
448                 {
449                         GST_WARNING_OBJECT (self, "can't change caps while in PLAYING state %" GST_PTR_FORMAT, caps);
450                         return TRUE;
451                 }
452                 else if (gst_structure_has_name (structure, "video/x-h264"))
453                 {
454                         memset (&info, 0, sizeof(VideoFormatInfo));
455                         ret = gst_structure_get_int (structure, "width", &info.width);
456                         ret &= gst_structure_get_int (structure, "height", &info.height);
457                         framerate = gst_structure_get_value (structure, "framerate");
458                         if (GST_VALUE_HOLDS_FRACTION(framerate)) {
459                                 info.fps_n = gst_value_get_fraction_numerator (framerate);
460                                 info.fps_d = gst_value_get_fraction_denominator (framerate);
461                         }
462                         GST_INFO_OBJECT (self, "set caps %" GST_PTR_FORMAT, caps);
463                         gst_caps_replace (&self->current_caps, caps);
464
465                         if (gst_dreamvideosource_set_format(self, &info) && gst_caps_is_fixed(caps))
466                                 ret = gst_pad_push_event (bsrc->srcpad, gst_event_new_caps (caps));
467                 }
468                 else {
469                         GST_WARNING_OBJECT (self, "unsupported caps: %" GST_PTR_FORMAT, caps);
470                         ret = FALSE;
471                 }
472         }
473         if (current_caps)
474                 gst_caps_unref (current_caps);
475         return ret;
476 }
477
478 static GstCaps *
479 gst_dreamvideosource_fixate (GstBaseSrc * bsrc, GstCaps * caps)
480 {
481         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (bsrc);
482         GstStructure *structure;
483
484         caps = gst_caps_make_writable (caps);
485         structure = gst_caps_get_structure (caps, 0);
486
487         gst_structure_fixate_field_nearest_int (structure, "width", DEFAULT_WIDTH);
488         gst_structure_fixate_field_nearest_int (structure, "height", DEFAULT_HEIGHT);
489         gst_structure_fixate_field_nearest_fraction (structure, "framerate", DEFAULT_FRAMERATE, 1);
490         gst_structure_fixate_field_nearest_fraction (structure, "pixel-aspect-ratio", DEFAULT_WIDTH, DEFAULT_HEIGHT);
491
492         caps = GST_BASE_SRC_CLASS (parent_class)->fixate (bsrc, caps);
493         GST_DEBUG_OBJECT (bsrc, "fixate caps: %" GST_PTR_FORMAT, caps);
494         return caps;
495 }
496
497 static gboolean gst_dreamvideosource_unlock (GstBaseSrc * bsrc)
498 {
499         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (bsrc);
500         GST_DEBUG_OBJECT (self, "stop creating buffers");
501         SEND_COMMAND (self, CONTROL_STOP);
502         return TRUE;
503 }
504
505 static void
506 gst_dreamvideosource_free_buffer (GstDreamVideoSource * self)
507 {
508         self->buffers_in_use--;
509         GST_TRACE_OBJECT (self, "gst_dreamvideosource_free_buffer buffer still in use: %i", self->buffers_in_use);
510 }
511
512 static GstFlowReturn
513 gst_dreamvideosource_create (GstPushSrc * psrc, GstBuffer ** outbuf)
514 {
515         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (psrc);
516         EncoderInfo *enc = self->encoder;
517
518         GST_LOG_OBJECT (self, "new buffer requested");
519
520         if (!enc) {
521                 GST_WARNING_OBJECT (self, "encoder device not opened!");
522                 return GST_FLOW_ERROR;
523         }
524
525         while (1)
526         {
527                 *outbuf = NULL;
528
529                 if (self->descriptors_available == 0)
530                 {
531                         self->descriptors_count = 0;
532                         struct pollfd rfd[2];
533
534                         rfd[0].fd = enc->fd;
535                         rfd[0].events = POLLIN;
536                         rfd[1].fd = READ_SOCKET (self);
537                         rfd[1].events = POLLIN | POLLERR | POLLHUP | POLLPRI;
538
539                         int ret = poll(rfd, 2, 200);
540
541                         if (G_UNLIKELY (ret == -1))
542                         {
543                                 GST_ERROR_OBJECT (self, "SELECT ERROR!");
544                                 return GST_FLOW_ERROR;
545                         }
546                         else if ( ret == 0 )
547                         {
548                                 GST_LOG_OBJECT (self, "SELECT TIMEOUT");
549                                 //!!! TODO generate dummy payload
550                                 *outbuf = gst_buffer_new();
551                         }
552                         else if ( G_UNLIKELY (rfd[1].revents) )
553                         {
554                                 char command;
555                                 READ_COMMAND (self, command, ret);
556                                 if (command == CONTROL_STOP)
557                                 {
558                                         GST_LOG_OBJECT (self, "CONTROL_STOP!");
559                                         return GST_FLOW_FLUSHING;
560                                 }
561                         }
562                         else if ( G_LIKELY(rfd[0].revents & POLLIN) )
563                         {
564                                 int rlen = read(enc->fd, enc->buffer, VBUFSIZE);
565                                 if (rlen <= 0 || rlen % VBDSIZE ) {
566                                         if ( errno == 512 )
567                                                 return GST_FLOW_FLUSHING;
568                                         GST_WARNING_OBJECT (self, "read error %s (%i)", strerror(errno), errno);
569                                         return GST_FLOW_ERROR;
570                                 }
571                                 self->descriptors_available = rlen / VBDSIZE;
572                                 GST_LOG_OBJECT (self, "encoder buffer was empty, %d descriptors available", self->descriptors_available);
573                         }
574                 }
575
576                 while (self->descriptors_count < self->descriptors_available) {
577                         off_t offset = self->descriptors_count * VBDSIZE;
578                         VideoBufferDescriptor *desc = (VideoBufferDescriptor*)(&enc->buffer[offset]);
579
580                         uint32_t f = desc->stCommon.uiFlags;
581
582                         GST_LOG_OBJECT (self, "descriptors_count=%d, descriptors_available=%d\tuiOffset=%d, uiLength=%d", self->descriptors_count, self->descriptors_available, desc->stCommon.uiOffset, desc->stCommon.uiLength);
583
584                         if (G_UNLIKELY (f & CDB_FLAG_METADATA))
585                         {
586                                 GST_LOG_OBJECT (self, "CDB_FLAG_METADATA... skip outdated packet");
587                                 self->descriptors_count = self->descriptors_available;
588                                 continue;
589                         }
590
591                         if (f & VBD_FLAG_DTS_VALID && desc->uiDTS)
592                         {
593                                 if (G_UNLIKELY (self->base_pts == GST_CLOCK_TIME_NONE))
594                                 {
595                                         if (self->dreamaudiosrc)
596                                         {
597                                                 g_mutex_lock (&self->mutex);
598                                                 guint64 audiosource_base_pts;
599                                                 g_signal_emit_by_name(self->dreamaudiosrc, "get-base-pts", &audiosource_base_pts);
600                                                 if (audiosource_base_pts != GST_CLOCK_TIME_NONE)
601                                                 {
602                                                         GST_DEBUG_OBJECT (self, "use DREAMAUDIOSOURCE's base_pts=%" GST_TIME_FORMAT "", GST_TIME_ARGS (audiosource_base_pts) );
603                                                         self->base_pts = audiosource_base_pts;
604                                                 }
605                                                 g_mutex_unlock (&self->mutex);
606                                         }
607                                         if (self->base_pts == GST_CLOCK_TIME_NONE)
608                                         {
609                                                 self->base_pts = MPEGTIME_TO_GSTTIME(desc->uiDTS);
610                                                 GST_DEBUG_OBJECT (self, "use mpeg stream pts as base_pts=%" GST_TIME_FORMAT" (%lld)", GST_TIME_ARGS (self->base_pts), desc->uiDTS);
611                                         }
612                                 }
613                         }
614
615                         *outbuf = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, enc->cdb, VMMAPSIZE, desc->stCommon.uiOffset, desc->stCommon.uiLength, self, (GDestroyNotify)gst_dreamvideosource_free_buffer);
616
617                         if (f & CDB_FLAG_PTS_VALID)
618                         {
619                                 GstClockTime buffer_time = MPEGTIME_TO_GSTTIME(desc->stCommon.uiPTS);
620                                 if (self->base_pts != GST_CLOCK_TIME_NONE && buffer_time > self->base_pts )
621                                 {
622                                         buffer_time -= self->base_pts;
623                                         GST_BUFFER_PTS(*outbuf) = buffer_time;
624                                         GST_BUFFER_DTS(*outbuf) = buffer_time;
625                                 }
626                         }
627 #ifdef dump
628                         int wret = write(self->dumpfd, (unsigned char*)(enc->cdb + desc->stCommon.uiOffset), desc->stCommon.uiLength);
629                         GST_LOG_OBJECT (self, "read %i dumped %i total %" G_GSIZE_FORMAT " ", desc->stCommon.uiLength, wret, gst_buffer_get_size (*outbuf) );
630 #endif
631
632                         self->descriptors_count++;
633
634                         break;
635                 }
636
637                 if (self->descriptors_count == self->descriptors_available) {
638                         GST_LOG_OBJECT (self, "self->descriptors_count == self->descriptors_available -> release %i consumed descriptors", self->descriptors_count);
639                         /* release consumed descs */
640                         if (write(enc->fd, &self->descriptors_count, sizeof(self->descriptors_count)) != sizeof(self->descriptors_count)) {
641                                 GST_WARNING_OBJECT (self, "release consumed descs write error!");
642                                 return GST_FLOW_ERROR;
643                         }
644                         self->descriptors_available = 0;
645                 }
646
647                 if (*outbuf)
648                 {
649                         self->buffers_in_use++;
650                         GST_DEBUG_OBJECT (self, "pushing %" GST_PTR_FORMAT "", *outbuf );
651                         return GST_FLOW_OK;
652                 }
653
654         }
655         return GST_FLOW_ERROR;
656 }
657
658 static GstStateChangeReturn gst_dreamvideosource_change_state (GstElement * element, GstStateChange transition)
659 {
660         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (element);
661         GstStateChangeReturn sret = GST_STATE_CHANGE_SUCCESS;
662         int ret;
663         GST_OBJECT_LOCK (self);
664         switch (transition) {
665                 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
666                         GST_LOG_OBJECT (self, "GST_STATE_CHANGE_PAUSED_TO_PLAYING");
667                         self->base_pts = GST_CLOCK_TIME_NONE;
668                         ret = ioctl(self->encoder->fd, VENC_START);
669                         if ( ret != 0 )
670                                 goto fail;
671                         self->descriptors_available = 0;
672                         CLEAR_COMMAND (self);
673                         GST_INFO_OBJECT (self, "started encoder!");
674                         break;
675                 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
676                         GST_DEBUG_OBJECT (self, "GST_STATE_CHANGE_PLAYING_TO_PAUSED self->descriptors_count=%i self->descriptors_available=%i", self->descriptors_count, self->descriptors_available);
677                         while (self->descriptors_count < self->descriptors_available)
678                                 GST_LOG_OBJECT (self, "flushing self->descriptors_count=%i", self->descriptors_count++);
679                         if (self->descriptors_count)
680                                 write(self->encoder->fd, &self->descriptors_count, sizeof(self->descriptors_count));
681                         ret = ioctl(self->encoder->fd, VENC_STOP);
682                         if ( ret != 0 )
683                                 goto fail;
684                         GST_INFO_OBJECT (self, "stopped encoder!");
685                         break;
686                 default:
687                         break;
688         }
689         GST_OBJECT_UNLOCK (self);
690         if (GST_ELEMENT_CLASS (parent_class)->change_state)
691                 sret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
692         return sret;
693 fail:
694         GST_ERROR_OBJECT(self,"can't perform encoder ioctl!");
695         GST_OBJECT_UNLOCK (self);
696         return GST_STATE_CHANGE_FAILURE;
697 }
698
699 static gboolean
700 gst_dreamvideosource_start (GstBaseSrc * bsrc)
701 {
702         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (bsrc);
703         self->dreamaudiosrc = gst_bin_get_by_name_recurse_up(GST_BIN(GST_ELEMENT_PARENT(self)), "dreamaudiosource0");
704
705         int control_sock[2];
706         if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
707         {
708                 GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, (NULL), GST_ERROR_SYSTEM);
709                 return FALSE;
710         }
711         READ_SOCKET (self) = control_sock[0];
712         WRITE_SOCKET (self) = control_sock[1];
713         fcntl (READ_SOCKET (self), F_SETFL, O_NONBLOCK);
714         fcntl (WRITE_SOCKET (self), F_SETFL, O_NONBLOCK);
715
716         GST_DEBUG_OBJECT (self, "started. reference to dreamaudiosource=%" GST_PTR_FORMAT"", self->dreamaudiosrc);
717         return TRUE;
718 }
719
720 static gboolean
721 gst_dreamvideosource_stop (GstBaseSrc * bsrc)
722 {
723         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (bsrc);
724         if (self->dreamaudiosrc)
725                 gst_object_unref(self->dreamaudiosrc);
726         close (READ_SOCKET (self));
727         close (WRITE_SOCKET (self));
728         READ_SOCKET (self) = -1;
729         WRITE_SOCKET (self) = -1;
730         GST_DEBUG_OBJECT (self, "stop");
731         return TRUE;
732 }
733
734 static void
735 gst_dreamvideosource_dispose (GObject * gobject)
736 {
737         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (gobject);
738         if (self->encoder) {
739                 if (self->encoder->buffer)
740                         free(self->encoder->buffer);
741                 if (self->encoder->cdb)
742                         munmap(self->encoder->cdb, VMMAPSIZE);
743                 if (self->encoder->fd)
744                         close(self->encoder->fd);
745                 free(self->encoder);
746         }
747 #ifdef dump
748         close(self->dumpfd);
749 #endif
750         if (self->current_caps)
751                 gst_caps_unref(self->current_caps);
752         g_mutex_clear (&self->mutex);
753         GST_DEBUG_OBJECT (self, "disposed");
754         G_OBJECT_CLASS (parent_class)->dispose (gobject);
755 }