switch framerate numerator and denominator :)
[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 Multimedia 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 enum
30 {
31         SIGNAL_GET_BASE_PTS,
32         LAST_SIGNAL
33 };
34
35 enum
36 {
37   ARG_0,
38   ARG_CAPS,
39   ARG_BITRATE,
40 };
41
42 static guint gst_dreamvideosource_signals[LAST_SIGNAL] = { 0 };
43
44
45 #define DEFAULT_BITRATE 2048
46 #define DEFAULT_FRAMERATE 25
47
48 static GstStaticPadTemplate srctemplate =
49     GST_STATIC_PAD_TEMPLATE ("src",
50         GST_PAD_SRC,
51         GST_PAD_ALWAYS,
52         GST_STATIC_CAPS ("video/x-h264, "
53         "width = { 720, 1280, 1920 }, "
54         "height = { 576, 720, 1080 }, "
55         "framerate = { 1/25, 1/30, 1/50, 1/60 }, "
56         "pixel-aspect-ratio = { 5/4, 16/9 }, "
57         "stream-format = (string) byte-stream, "
58         "profile = (string) main")
59     );
60
61 #define gst_dreamvideosource_parent_class parent_class
62 G_DEFINE_TYPE (GstDreamVideoSource, gst_dreamvideosource, GST_TYPE_PUSH_SRC);
63
64 static GstCaps *gst_dreamvideosource_getcaps (GstBaseSrc * psrc, GstCaps * filter);
65 static gboolean gst_dreamvideosource_setcaps (GstBaseSrc * bsrc, GstCaps * caps);
66 static GstCaps *gst_dreamvideosource_fixate (GstBaseSrc * bsrc, GstCaps * caps);
67 static gboolean gst_dreamvideosource_negotiate (GstBaseSrc * bsrc);
68
69 static gboolean gst_dreamvideosource_start (GstBaseSrc * bsrc);
70 static gboolean gst_dreamvideosource_stop (GstBaseSrc * bsrc);
71 static void gst_dreamvideosource_finalize (GObject * gobject);
72 static GstFlowReturn gst_dreamvideosource_create (GstPushSrc * psrc, GstBuffer ** outbuf);
73
74 static void gst_dreamvideosource_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
75 static void gst_dreamvideosource_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
76
77 static GstStateChangeReturn gst_dreamvideosource_change_state (GstElement * element, GstStateChange transition);
78 static gint64 gst_dreamvideosource_get_base_pts (GstDreamVideoSource *self);
79
80 static void
81 gst_dreamvideosource_class_init (GstDreamVideoSourceClass * klass)
82 {
83         GObjectClass *gobject_class;
84         GstElementClass *gstelement_class;
85         GstBaseSrcClass *gstbsrc_class;
86         GstPushSrcClass *gstpush_src_class;
87
88         gobject_class = (GObjectClass *) klass;
89         gstelement_class = (GstElementClass *) klass;
90         gstbsrc_class = (GstBaseSrcClass *) klass;
91         gstpush_src_class = (GstPushSrcClass *) klass;
92
93         gobject_class->set_property = gst_dreamvideosource_set_property;
94         gobject_class->get_property = gst_dreamvideosource_get_property;
95         gobject_class->finalize = gst_dreamvideosource_finalize;
96
97         gst_element_class_add_pad_template (gstelement_class,
98                                             gst_static_pad_template_get (&srctemplate));
99
100         gst_element_class_set_static_metadata (gstelement_class,
101             "Dream Video source", "Source/Video",
102             "Provide an h.264 video elementary stream from Dreambox encoder device",
103             "Andreas Frisch <fraxinas@opendreambox.org>");
104         
105         gstelement_class->change_state = gst_dreamvideosource_change_state;
106
107         gstbsrc_class->get_caps = gst_dreamvideosource_getcaps;
108         gstbsrc_class->set_caps = gst_dreamvideosource_setcaps;
109         gstbsrc_class->fixate = gst_dreamvideosource_fixate;
110 //      gstbsrc_class->negotiate = gst_dreamvideosource_negotiate;
111         gstbsrc_class->start = gst_dreamvideosource_start;
112         gstbsrc_class->stop = gst_dreamvideosource_stop;
113
114         gstpush_src_class->create = gst_dreamvideosource_create;
115         
116         g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BITRATE,
117           g_param_spec_int ("bitrate", "Bitrate (kb/s)",
118             "Bitrate in kbit/sec", 16, 200000, DEFAULT_BITRATE,
119             G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
120
121         g_object_class_install_property (gobject_class, ARG_CAPS,
122           g_param_spec_boxed ("caps", "Caps",
123             "The caps for the source stream", GST_TYPE_CAPS,
124             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
125
126         gst_dreamvideosource_signals[SIGNAL_GET_BASE_PTS] =
127                 g_signal_new ("get-base-pts",
128                 G_TYPE_FROM_CLASS (klass),
129                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
130                 G_STRUCT_OFFSET (GstDreamVideoSourceClass, get_base_pts),
131                 NULL, NULL, gst_dreamsource_marshal_INT64__VOID, G_TYPE_INT64, 0);
132
133         klass->get_base_pts = gst_dreamvideosource_get_base_pts;
134 }
135
136 static gint64
137 gst_dreamvideosource_get_base_pts (GstDreamVideoSource *self)
138 {
139         GST_DEBUG_OBJECT (self, "gst_dreamvideosource_get_base_pts " GST_TIME_FORMAT"", GST_TIME_ARGS (self->base_pts) );
140         return self->base_pts;
141 }
142
143 gboolean
144 gst_dreamvideosource_plugin_init (GstPlugin *plugin)
145 {
146         GST_DEBUG_CATEGORY_INIT (dreamvideosource_debug, "dreamvideosource", 0, "dreamvideosource");
147         return gst_element_register (plugin, "dreamvideosource", GST_RANK_PRIMARY, GST_TYPE_DREAMVIDEOSOURCE);
148 }
149
150 static void
151 gst_dreamvideosource_init (GstDreamVideoSource * self)
152 {
153         self->encoder = NULL;
154         self->descriptors_available = 0;
155         self->video_info.width = 1280;
156         self->video_info.height = 720;
157         self->video_info.par_n = 16;
158         self->video_info.par_d = 9;
159         self->video_info.fps_n = 25;
160         self->video_info.fps_d = 1;
161         self->base_pts = GST_CLOCK_TIME_NONE;
162
163         g_mutex_init (&self->mutex);
164         
165         gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
166         gst_base_src_set_live (GST_BASE_SRC (self), TRUE);
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         GstState state;
196         gst_element_get_state (GST_ELEMENT(self), &state, NULL, 1*GST_MSECOND);
197         
198         if (state != GST_STATE_PAUSED)
199         {
200                 GST_ERROR_OBJECT (self, "can't set format in %s state. must be in PAUSED!", gst_element_state_get_name (state));
201                 return FALSE;
202         }
203         
204         GST_OBJECT_LOCK (self);
205 //      g_mutex_lock (&self->mutex);
206
207         GST_INFO_OBJECT (self, "requested to set resolution to %dx%d, framerate to %d/%d aspect_ratio %d/%d", info->width, info->height, info->fps_n, info->fps_d, info->par_n, info->par_d);
208         
209         if ( (info->par_n == 5 && info->par_d == 4) || (info->par_n == 16 && info->par_d == 9) )
210         {
211                 int venc_size = 0, venc_fps = 0;
212                 switch (info->fps_n) {
213                         case 25:
214                                 venc_fps = rate_25;
215                                 break;
216                         case 30:
217                                 venc_fps = rate_30;
218                                 break;
219                         case 50:
220                                 venc_fps = rate_50;
221                                 break;
222                         case 60:
223                                 venc_fps = rate_60;
224                                 break;
225                         default:
226                                 GST_ERROR_OBJECT (self, "invalid framerate %d/%d", info->fps_n, info->fps_d);
227                                 goto fail;
228                 }
229                 
230                 if ( info->width == 720 && info->height == 576 )
231                         venc_size = fmt_720x576;
232                 else if ( info->width == 1280 && info->height == 720)
233                         venc_size = fmt_1280x720;
234                 else if ( info->width == 1920 && info->height == 1080)
235                         venc_size = fmt_1920x1080;
236                 else
237                 {
238                         GST_ERROR_OBJECT (self, "invalid resolution %dx%d", info->width, info->height);
239                         goto fail;
240                 }
241                         
242                 if (!ioctl(self->encoder->fd, VENC_SET_FRAMERATE, &venc_fps))
243                         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);
244                 else
245                 {
246                         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);
247                         goto fail;
248                 }
249
250                 if (!ioctl(self->encoder->fd, VENC_SET_RESOLUTION, &venc_size))
251                         GST_INFO_OBJECT (self, "set resolution to %dx%d -> ioctrl(%d, VENC_SET_RESOLUTION, &%d)", info->width, info->height, self->encoder->fd, venc_size);
252                 else
253                 {
254                         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);
255                         goto fail;
256                 }
257                 
258                 self->video_info = *info;
259 //              g_mutex_unlock (&self->mutex);
260                 GST_OBJECT_UNLOCK (self);
261                 return TRUE;
262         }
263         
264         GST_INFO_OBJECT (self, "invalid aspect!");
265         
266 fail:
267 //      g_mutex_unlock (&self->mutex);
268         GST_OBJECT_UNLOCK (self);
269         return FALSE;
270 }
271
272 static void
273 gst_dreamvideosource_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
274 {
275         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (object);
276         
277         switch (prop_id) {
278                 case ARG_CAPS:
279                 {
280                         GstCaps *caps = gst_caps_copy(gst_value_get_caps (value));
281                         gst_dreamvideosource_setcaps (GST_BASE_SRC(object), caps);
282                         gst_caps_unref(caps);
283                         break;
284                 }
285                 case ARG_BITRATE:
286                         gst_dreamvideosource_set_bitrate(self, g_value_get_int (value));
287                         break;
288                 default:
289                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
290                         break;
291         }
292 }
293
294 static void
295 gst_dreamvideosource_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
296 {
297         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (object);
298         
299         switch (prop_id) {
300                 case ARG_CAPS:
301                         g_value_take_boxed (value, gst_dreamvideosource_getcaps (GST_BASE_SRC(object), GST_CAPS_ANY));
302                         break;
303                 case ARG_BITRATE:
304                         g_value_set_int (value, self->video_info.bitrate/1000);
305                         break;
306                 default:
307                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
308                         break;
309         }
310 }
311
312 static GstCaps *
313 gst_dreamvideosource_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
314 {
315         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (bsrc);
316         GstPadTemplate *pad_template;
317         GstCaps *caps;
318
319         pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS(self), "src");
320         g_return_val_if_fail (pad_template != NULL, NULL);
321         
322         GST_LOG_OBJECT (self, "gst_dreamvideosource_getcaps filter %" GST_PTR_FORMAT, caps);
323         
324         caps = gst_pad_template_get_caps (pad_template);
325         
326         if (self->encoder && self->video_info.width && self->video_info.height & self->video_info.fps_n)
327         {
328                 caps = gst_caps_make_writable(gst_pad_template_get_caps (pad_template));
329                 gst_caps_set_simple(caps, "width", G_TYPE_INT, self->video_info.width, NULL);
330                 gst_caps_set_simple(caps, "height", G_TYPE_INT, self->video_info.height, NULL);
331                 gst_caps_set_simple(caps, "framerate", GST_TYPE_FRACTION, self->video_info.fps_n, self->video_info.fps_d, NULL);
332                 gst_caps_set_simple(caps, "pixel-aspect-ratio", GST_TYPE_FRACTION, self->video_info.par_n, self->video_info.par_d, NULL);
333         }
334         
335         if (filter) {
336                 GstCaps *intersection;
337                 intersection = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
338                 gst_caps_unref (caps);
339                 caps = intersection;
340         }       
341
342         GST_INFO_OBJECT (self, "return caps %" GST_PTR_FORMAT, caps);
343         return caps;
344 }
345
346 static gboolean
347 gst_dreamvideosource_setcaps (GstBaseSrc * bsrc, GstCaps * caps)
348 {
349         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (bsrc);
350         GstBaseSrcClass *bclass = GST_BASE_SRC_GET_CLASS (bsrc);
351         GstCaps *current_caps;
352         const GstStructure *structure;
353         VideoFormatInfo info;
354         gboolean ret;
355         int width, height;
356         const GValue *framerate, *par;
357         structure = gst_caps_get_structure (caps, 0);
358         
359         current_caps = gst_pad_get_current_caps (GST_BASE_SRC_PAD (bsrc));
360         if (current_caps && gst_caps_is_equal (current_caps, caps)) {
361                 GST_DEBUG_OBJECT (self, "New caps equal to old ones: %" GST_PTR_FORMAT, caps);
362                 ret = TRUE;
363         } else {
364                 if (gst_structure_has_name (structure, "video/x-h264"))
365                 {
366                         memset (&info, 0, sizeof(VideoFormatInfo));
367                         ret = gst_structure_get_int (structure, "width", &info.width);
368                         ret &= gst_structure_get_int (structure, "height", &info.height);
369                         framerate = gst_structure_get_value (structure, "framerate");
370                         if (framerate) {
371                                 info.fps_n = gst_value_get_fraction_numerator (framerate);
372                                 info.fps_d = gst_value_get_fraction_denominator (framerate);
373                         }
374                         else {
375                                 info.fps_n = DEFAULT_FRAMERATE;
376                                 info.fps_d = 1;
377                         }
378                         par = gst_structure_get_value (structure, "pixel-aspect-ratio");
379                         if (par) {
380                                 info.par_n = gst_value_get_fraction_numerator (par);
381                                 info.par_d = gst_value_get_fraction_denominator (par);
382                         }
383                         else {
384                                 info.par_n = 16;
385                                 info.par_d = 9;
386                         }
387                         GST_INFO_OBJECT (self, "set caps %" GST_PTR_FORMAT, caps);
388                         if (gst_dreamvideosource_set_format(self, &info))
389                                 ret = gst_pad_push_event (bsrc->srcpad, gst_event_new_caps (caps));
390                 }
391                 else {
392                         GST_WARNING_OBJECT (self, "unsupported caps: %" GST_PTR_FORMAT, caps);
393                         ret = FALSE;
394                 }
395         }
396         if (current_caps)
397                 gst_caps_unref (current_caps);
398         return ret;
399 }
400
401 static GstCaps *
402 gst_dreamvideosource_fixate (GstBaseSrc * bsrc, GstCaps * caps)
403 {
404         GstStructure *structure;
405
406         caps = gst_caps_make_writable (caps);
407         structure = gst_caps_get_structure (caps, 0);
408
409         gst_structure_fixate_field_nearest_int (structure, "width", 1280);
410         gst_structure_fixate_field_nearest_int (structure, "height", 720);
411         gst_structure_fixate_field_nearest_fraction (structure, "framerate", DEFAULT_FRAMERATE, 1);
412
413         if (gst_structure_has_field (structure, "pixel-aspect-ratio"))
414                 gst_structure_fixate_field_nearest_fraction (structure, "pixel-aspect-ratio", 16, 9);
415         else
416                 gst_structure_set (structure, "pixel-aspect-ratio", GST_TYPE_FRACTION, 16, 9, NULL);
417
418         caps = GST_BASE_SRC_CLASS (parent_class)->fixate (bsrc, caps);
419         GST_DEBUG_OBJECT (bsrc, "fixate caps: %" GST_PTR_FORMAT, caps);
420         return caps;
421 }
422
423 static gboolean
424 gst_dreamvideosource_negotiate (GstBaseSrc * bsrc)
425 {
426         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (bsrc);
427
428         GstState state;
429         GstCaps *thiscaps;
430         GstCaps *caps = NULL;
431         GstCaps *peercaps = NULL;
432         gboolean result = FALSE;
433         
434         gst_element_get_state (GST_ELEMENT(self), &state, NULL, 1*GST_MSECOND);
435         
436         if (state == GST_STATE_PLAYING)
437                 return TRUE;
438
439         thiscaps = gst_pad_query_caps (GST_BASE_SRC_PAD (bsrc), NULL);
440         GST_DEBUG_OBJECT (bsrc, "caps of src: %" GST_PTR_FORMAT, thiscaps);
441         
442         if (thiscaps == NULL || gst_caps_is_any (thiscaps))
443                 goto no_nego_needed;
444         
445         peercaps = gst_pad_peer_query_caps (GST_BASE_SRC_PAD (bsrc), NULL);
446         GST_DEBUG_OBJECT (bsrc, "caps of peer: %" GST_PTR_FORMAT, peercaps);
447         
448         if (peercaps && !gst_caps_is_any (peercaps)) {
449                 GstCaps *icaps = NULL;
450                 int i;
451                 
452                 /* Prefer the first caps we are compatible with that the peer proposed */
453                 for (i = 0; i < gst_caps_get_size (peercaps); i++) {
454                         /* get intersection */
455                         GstCaps *ipcaps = gst_caps_copy_nth (peercaps, i);
456                         
457                         GST_DEBUG_OBJECT (bsrc, "peer: %" GST_PTR_FORMAT, ipcaps);
458                         
459                         icaps = gst_caps_intersect (thiscaps, ipcaps);
460                         gst_caps_unref (ipcaps);
461                         
462                         if (!gst_caps_is_empty (icaps))
463                                 break;
464                         
465                         gst_caps_unref (icaps);
466                         icaps = NULL;
467                 }
468                 
469                 GST_DEBUG_OBJECT (bsrc, "intersect: %" GST_PTR_FORMAT, icaps);
470                 if (icaps) {
471                         /* If there are multiple intersections pick the one with the smallest
472                          * resolution strictly bigger then the first peer caps */
473                         if (gst_caps_get_size (icaps) > 1) {
474                                 GstStructure *s = gst_caps_get_structure (peercaps, 0);
475                                 int best = 0;
476                                 int twidth, theight;
477                                 int width = G_MAXINT, height = G_MAXINT;
478                                 
479                                 if (gst_structure_get_int (s, "width", &twidth) && gst_structure_get_int (s, "height", &theight)) {
480                                         
481                                                 /* Walk the structure backwards to get the first entry of the
482                                                 * smallest resolution bigger (or equal to) the preferred resolution)
483                                                 */
484                                                 for (i = gst_caps_get_size (icaps) - 1; i >= 0; i--) {
485                                                         GstStructure *is = gst_caps_get_structure (icaps, i);
486                                                         int w, h;
487                                                         
488                                                         if (gst_structure_get_int (is, "width", &w) && gst_structure_get_int (is, "height", &h))
489                                                         {
490                                                                 if (w >= twidth && w <= width && h >= theight && h <= height) {
491                                                                         width = w;
492                                                                         height = h;
493                                                                         best = i;
494                                                                 }
495                                                         }
496                                                 }
497                                         }
498                                         
499                                         caps = gst_caps_copy_nth (icaps, best);
500                                         gst_caps_unref (icaps);
501                         } else {
502                                 caps = icaps;
503                         }
504                 }
505                 gst_caps_unref (thiscaps);
506         } else {
507                 /* no peer or peer have ANY caps, work with our own caps then */
508                 caps = thiscaps;
509         }
510         if (peercaps)
511                 gst_caps_unref (peercaps);
512         if (caps) {
513                 caps = gst_caps_truncate (caps);
514                 
515                 /* now fixate */
516                 if (!gst_caps_is_empty (caps)) {
517                         caps = gst_dreamvideosource_fixate (bsrc, caps);
518                         GST_DEBUG_OBJECT (bsrc, "fixated to: %" GST_PTR_FORMAT, caps);
519                         
520                         if (gst_caps_is_any (caps)) {
521                                 /* hmm, still anything, so element can do anything and
522                                  * nego is not needed */
523                                 result = TRUE;
524                         } else if (gst_caps_is_fixed (caps)) {
525                                 /* yay, fixed caps, use those then */
526                                 result = gst_base_src_set_caps (bsrc, caps);
527                         }
528                 }
529                 gst_caps_unref (caps);
530         }
531         return result;
532         
533         no_nego_needed:
534         {
535                 GST_DEBUG_OBJECT (bsrc, "no negotiation needed");
536                 if (thiscaps)
537                         gst_caps_unref (thiscaps);
538                 return TRUE;
539         }
540 }
541
542 static void
543 gst_dreamvideosource_free_buffer (GstDreamVideoSource * self)
544 {
545         GST_LOG_OBJECT (self, "gst_dreamvideosource_free_buffer");
546 }
547
548 static GstFlowReturn
549 gst_dreamvideosource_create (GstPushSrc * psrc, GstBuffer ** outbuf)
550 {
551         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (psrc);
552         EncoderInfo *enc = self->encoder;
553         
554         GST_LOG_OBJECT (self, "new buffer requested");
555
556         if (!enc) {
557                 GST_WARNING_OBJECT (self, "encoder device not opened!");
558                 return GST_FLOW_ERROR;
559         }
560         
561         while (1)
562         {
563                 *outbuf = NULL;
564                 
565                 if (self->descriptors_available == 0)
566                 {
567                         self->descriptors_count = 0;
568                         int rlen = read(enc->fd, enc->buffer, VBUFSIZE);
569                         if (rlen <= 0 || rlen % VBDSIZE ) {
570                                 GST_WARNING_OBJECT (self, "read error %d (errno %i)", rlen, errno);
571                                 return GST_FLOW_ERROR;
572                         }
573                         self->descriptors_available = rlen / VBDSIZE;
574                         GST_LOG_OBJECT (self, "encoder buffer was empty, %d descriptors available", self->descriptors_available);
575                 }
576
577                 while (self->descriptors_count < self->descriptors_available) {
578                         off_t offset = self->descriptors_count * VBDSIZE;
579                         VideoBufferDescriptor *desc = (VideoBufferDescriptor*)(&enc->buffer[offset]);
580
581                         uint32_t f = desc->stCommon.uiFlags;
582
583                         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);
584
585                         if (G_UNLIKELY (f & CDB_FLAG_METADATA))
586                         { 
587                                 GST_LOG_OBJECT (self, "CDB_FLAG_METADATA... skip outdated packet");
588                                 self->descriptors_count = self->descriptors_available;
589                                 continue;
590                         }
591                         
592                         if (f & VBD_FLAG_DTS_VALID && desc->uiDTS)
593                         {
594                                 if (G_UNLIKELY (self->base_pts == GST_CLOCK_TIME_NONE))
595                                 {
596                                         if (self->dreamaudiosrc)
597                                         {
598                                                 g_mutex_lock (&self->mutex);
599                                                 guint64 audiosource_base_pts;
600                                                 g_signal_emit_by_name(self->dreamaudiosrc, "get-base-pts", &audiosource_base_pts);
601                                                 if (audiosource_base_pts != GST_CLOCK_TIME_NONE)
602                                                 {
603                                                         GST_DEBUG_OBJECT (self, "use DREAMAUDIOSOURCE's base_pts=%" GST_TIME_FORMAT "", GST_TIME_ARGS (audiosource_base_pts) );
604                                                         self->base_pts = audiosource_base_pts;
605                                                 }
606                                                 g_mutex_unlock (&self->mutex);
607                                         }
608                                         if (self->base_pts == GST_CLOCK_TIME_NONE)
609                                         {
610                                                 self->base_pts = MPEGTIME_TO_GSTTIME(desc->uiDTS);
611                                                 GST_DEBUG_OBJECT (self, "use mpeg stream pts as base_pts=%" GST_TIME_FORMAT"", GST_TIME_ARGS (self->base_pts) );
612                                         }
613                                 }
614                         }
615                         
616                         *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);
617                         
618                         if (f & CDB_FLAG_PTS_VALID)
619                         {
620                                 GstClockTime buffer_time = MPEGTIME_TO_GSTTIME(desc->stCommon.uiPTS);
621                                 if (self->base_pts != GST_CLOCK_TIME_NONE && buffer_time > self->base_pts )
622                                 {
623                                         buffer_time -= self->base_pts;
624                                         GST_BUFFER_PTS(*outbuf) = buffer_time;
625                                         GST_BUFFER_DTS(*outbuf) = buffer_time;
626                                 }
627                         }
628 #ifdef dump
629                         int wret = write(self->dumpfd, (unsigned char*)(enc->cdb + desc->stCommon.uiOffset), desc->stCommon.uiLength);
630                         GST_LOG_OBJECT (self, "read %i dumped %i total %" G_GSIZE_FORMAT " ", desc->stCommon.uiLength, wret, gst_buffer_get_size (*outbuf) );
631 #endif
632
633                         self->descriptors_count++;
634
635                         break;
636                 }
637
638                 if (self->descriptors_count == self->descriptors_available) {
639                         GST_LOG_OBJECT (self, "self->descriptors_count == self->descriptors_available -> release %i consumed descriptors", self->descriptors_count);
640                         /* release consumed descs */
641                         if (write(enc->fd, &self->descriptors_count, 4) != 4) {
642                                 GST_WARNING_OBJECT (self, "release consumed descs write error!");
643                                 return GST_FLOW_ERROR;
644                         }
645                         self->descriptors_available = 0;
646                 }
647                 
648                 if (*outbuf)
649                 {
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         g_return_val_if_fail (GST_DREAMVIDEOSOURCE (element), FALSE);
661         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (element);
662         int ret;
663
664         switch (transition) {
665                 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
666                         GST_LOG_OBJECT (self, "GST_STATE_CHANGE_PAUSED_TO_PLAYING");
667                         ret = ioctl(self->encoder->fd, VENC_START);
668                         if ( ret != 0 )
669                         {
670                                 GST_ERROR_OBJECT(self,"can't start encoder ioctl!");
671                                 return GST_STATE_CHANGE_FAILURE;
672                         }
673                         GST_INFO_OBJECT (self, "started encoder!");
674                         break;
675                 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
676                         GST_LOG_OBJECT (self, "GST_STATE_CHANGE_PLAYING_TO_PAUSED");
677                         ret = ioctl(self->encoder->fd, VENC_STOP);
678                         if ( ret != 0 )
679                         {
680                                 GST_ERROR_OBJECT(self,"can't stop encoder ioctl!");
681                                 return GST_STATE_CHANGE_FAILURE;
682                         }
683                         break;
684                 default:
685                         break;
686         }
687
688         if (GST_ELEMENT_CLASS (parent_class)->change_state)
689                 return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
690
691         return GST_STATE_CHANGE_SUCCESS;
692 }
693 static gboolean
694 gst_dreamvideosource_start (GstBaseSrc * bsrc)
695 {
696         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (bsrc);
697
698         char fn_buf[32];
699
700         self->encoder = malloc(sizeof(EncoderInfo));
701
702         if(!self->encoder) {
703                 GST_ERROR_OBJECT(self,"out of space");
704                 return FALSE;
705         }
706
707         sprintf(fn_buf, "/dev/venc%d", 0);
708         self->encoder->fd = open(fn_buf, O_RDWR | O_SYNC);
709         if(self->encoder->fd <= 0) {
710                 GST_ERROR_OBJECT(self,"cannot open device %s (%s)", fn_buf, strerror(errno));
711                 free(self->encoder);
712                 self->encoder = NULL;
713                 return FALSE;
714         }
715
716         self->encoder->buffer = malloc(VBUFSIZE);
717         if(!self->encoder->buffer) {
718                 GST_ERROR_OBJECT(self,"cannot alloc buffer");
719                 return FALSE;
720         }
721
722         self->encoder->cdb = (unsigned char *)mmap(0, VMMAPSIZE, PROT_READ, MAP_PRIVATE, self->encoder->fd, 0);
723
724         if(!self->encoder->cdb) {
725                 GST_ERROR_OBJECT(self,"cannot mmap cdb");
726                 return FALSE;
727         }
728 #ifdef dump
729         self->dumpfd = open("/media/hdd/movie/dreamvideosource.dump", O_WRONLY | O_CREAT | O_TRUNC);
730         GST_DEBUG_OBJECT (self, "dumpfd = %i (%s)", self->dumpfd, (self->dumpfd > 0) ? "OK" : strerror(errno));
731 #endif
732
733         gst_dreamvideosource_set_bitrate(self, DEFAULT_BITRATE);
734
735         self->dreamaudiosrc = gst_bin_get_by_name_recurse_up(GST_BIN(GST_ELEMENT_PARENT(self)), "dreamaudiosource0");
736
737         return TRUE;
738 }
739
740 static gboolean
741 gst_dreamvideosource_stop (GstBaseSrc * bsrc)
742 {
743         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (bsrc);
744         if (self->encoder) {
745                 if (self->encoder->fd > 0)
746                         ioctl(self->encoder->fd, VENC_STOP);
747                 close(self->encoder->fd);
748         }
749 #ifdef dump
750         close(self->dumpfd);
751 #endif
752         GST_DEBUG_OBJECT (self, "closed");
753         return TRUE;
754 }
755
756 static void
757 gst_dreamvideosource_finalize (GObject * gobject)
758 {
759         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (gobject);
760         if (self->encoder) {
761                 if (self->encoder->buffer)
762                         free(self->encoder->buffer);
763                 if (self->encoder->cdb) 
764                         munmap(self->encoder->cdb, VMMAPSIZE);
765                 free(self->encoder);
766         }
767         g_mutex_clear (&self->mutex);
768         GST_DEBUG_OBJECT (self, "finalized");
769         G_OBJECT_CLASS (parent_class)->finalize (gobject);
770 }