implement caps negotiation (set resolution and framerate) WORK IN PROGRESS!
[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 = 1;
160         self->video_info.fps_d = 25;
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_d) {
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_d)
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 //      if (self->encoder == NULL)
343 //      {
344 //              caps = gst_pad_template_get_caps (pad_template);
345 //              GST_LOG_OBJECT (self, "encoder not opened -> use template caps %" GST_PTR_FORMAT, caps);
346 //              return caps;
347 //      }
348 //      else if (!self->video_info.width || !self->video_info.height || !self->video_info.fps_d)
349 //      {
350 //              caps = gst_pad_template_get_caps (pad_template);
351 //              GST_LOG_OBJECT (self, "invalid video_info! -> use template caps %" GST_PTR_FORMAT, caps);
352 //              return caps;
353 //      }
354 //      else
355
356
357         GST_INFO_OBJECT (self, "return caps %" GST_PTR_FORMAT, caps);
358         return caps;
359 }
360
361 static gboolean
362 gst_dreamvideosource_setcaps (GstBaseSrc * bsrc, GstCaps * caps)
363 {
364         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (bsrc);
365         GstBaseSrcClass *bclass = GST_BASE_SRC_GET_CLASS (bsrc);
366         GstCaps *current_caps;
367         const GstStructure *structure;
368         VideoFormatInfo info;
369         gboolean ret;
370         int width, height;
371         const GValue *framerate, *par;
372         structure = gst_caps_get_structure (caps, 0);
373         
374         current_caps = gst_pad_get_current_caps (GST_BASE_SRC_PAD (bsrc));
375         if (current_caps && gst_caps_is_equal (current_caps, caps)) {
376                 GST_DEBUG_OBJECT (self, "New caps equal to old ones: %" GST_PTR_FORMAT, caps);
377                 ret = TRUE;
378         } else {
379                 if (gst_structure_has_name (structure, "video/x-h264"))
380                 {
381                         memset (&info, 0, sizeof(VideoFormatInfo));
382                         ret = gst_structure_get_int (structure, "width", &info.width);
383                         ret &= gst_structure_get_int (structure, "height", &info.height);
384                         framerate = gst_structure_get_value (structure, "framerate");
385                         if (framerate) {
386                                 info.fps_n = gst_value_get_fraction_numerator (framerate);
387                                 info.fps_d = gst_value_get_fraction_denominator (framerate);
388                         }
389                         else {
390                                 info.fps_n = 1;
391                                 info.fps_d = DEFAULT_FRAMERATE;
392                         }
393                         par = gst_structure_get_value (structure, "pixel-aspect-ratio");
394                         if (par) {
395                                 info.par_n = gst_value_get_fraction_numerator (par);
396                                 info.par_d = gst_value_get_fraction_denominator (par);
397                         }
398                         else {
399                                 info.par_n = 16;
400                                 info.par_d = 9;
401                         }
402                         GST_INFO_OBJECT (self, "set caps %" GST_PTR_FORMAT, caps);
403                         if (gst_dreamvideosource_set_format(self, &info))
404                                 ret = gst_pad_push_event (bsrc->srcpad, gst_event_new_caps (caps));
405                 }
406                 else {
407                         GST_WARNING_OBJECT (self, "unsupported caps: %" GST_PTR_FORMAT, caps);
408                         ret = FALSE;
409                 }
410         }
411         if (current_caps)
412                 gst_caps_unref (current_caps);
413         return ret;
414 }
415
416 static GstCaps *
417 gst_dreamvideosource_fixate (GstBaseSrc * bsrc, GstCaps * caps)
418 {
419         GstStructure *structure;
420
421         caps = gst_caps_make_writable (caps);
422         structure = gst_caps_get_structure (caps, 0);
423
424         gst_structure_fixate_field_nearest_int (structure, "width", 1280);
425         gst_structure_fixate_field_nearest_int (structure, "height", 720);
426         gst_structure_fixate_field_nearest_fraction (structure, "framerate", DEFAULT_FRAMERATE, 1);
427
428         if (gst_structure_has_field (structure, "pixel-aspect-ratio"))
429                 gst_structure_fixate_field_nearest_fraction (structure, "pixel-aspect-ratio", 16, 9);
430         else
431                 gst_structure_set (structure, "pixel-aspect-ratio", GST_TYPE_FRACTION, 16, 9, NULL);
432
433         caps = GST_BASE_SRC_CLASS (parent_class)->fixate (bsrc, caps);
434         GST_DEBUG_OBJECT (bsrc, "fixate caps: %" GST_PTR_FORMAT, caps);
435         return caps;
436 }
437
438 static gboolean
439 gst_dreamvideosource_negotiate (GstBaseSrc * bsrc)
440 {
441         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (bsrc);
442
443         GstState state;
444         GstCaps *thiscaps;
445         GstCaps *caps = NULL;
446         GstCaps *peercaps = NULL;
447         gboolean result = FALSE;
448         
449         gst_element_get_state (GST_ELEMENT(self), &state, NULL, 1*GST_MSECOND);
450         
451         if (state == GST_STATE_PLAYING)
452                 return TRUE;
453
454         thiscaps = gst_pad_query_caps (GST_BASE_SRC_PAD (bsrc), NULL);
455         GST_DEBUG_OBJECT (bsrc, "caps of src: %" GST_PTR_FORMAT, thiscaps);
456         
457         if (thiscaps == NULL || gst_caps_is_any (thiscaps))
458                 goto no_nego_needed;
459         
460         peercaps = gst_pad_peer_query_caps (GST_BASE_SRC_PAD (bsrc), NULL);
461         GST_DEBUG_OBJECT (bsrc, "caps of peer: %" GST_PTR_FORMAT, peercaps);
462         
463         if (peercaps && !gst_caps_is_any (peercaps)) {
464                 GstCaps *icaps = NULL;
465                 int i;
466                 
467                 /* Prefer the first caps we are compatible with that the peer proposed */
468                 for (i = 0; i < gst_caps_get_size (peercaps); i++) {
469                         /* get intersection */
470                         GstCaps *ipcaps = gst_caps_copy_nth (peercaps, i);
471                         
472                         GST_DEBUG_OBJECT (bsrc, "peer: %" GST_PTR_FORMAT, ipcaps);
473                         
474                         icaps = gst_caps_intersect (thiscaps, ipcaps);
475                         gst_caps_unref (ipcaps);
476                         
477                         if (!gst_caps_is_empty (icaps))
478                                 break;
479                         
480                         gst_caps_unref (icaps);
481                         icaps = NULL;
482                 }
483                 
484                 GST_DEBUG_OBJECT (bsrc, "intersect: %" GST_PTR_FORMAT, icaps);
485                 if (icaps) {
486                         /* If there are multiple intersections pick the one with the smallest
487                          * resolution strictly bigger then the first peer caps */
488                         if (gst_caps_get_size (icaps) > 1) {
489                                 GstStructure *s = gst_caps_get_structure (peercaps, 0);
490                                 int best = 0;
491                                 int twidth, theight;
492                                 int width = G_MAXINT, height = G_MAXINT;
493                                 
494                                 if (gst_structure_get_int (s, "width", &twidth) && gst_structure_get_int (s, "height", &theight)) {
495                                         
496                                                 /* Walk the structure backwards to get the first entry of the
497                                                 * smallest resolution bigger (or equal to) the preferred resolution)
498                                                 */
499                                                 for (i = gst_caps_get_size (icaps) - 1; i >= 0; i--) {
500                                                         GstStructure *is = gst_caps_get_structure (icaps, i);
501                                                         int w, h;
502                                                         
503                                                         if (gst_structure_get_int (is, "width", &w) && gst_structure_get_int (is, "height", &h))
504                                                         {
505                                                                 if (w >= twidth && w <= width && h >= theight && h <= height) {
506                                                                         width = w;
507                                                                         height = h;
508                                                                         best = i;
509                                                                 }
510                                                         }
511                                                 }
512                                         }
513                                         
514                                         caps = gst_caps_copy_nth (icaps, best);
515                                         gst_caps_unref (icaps);
516                         } else {
517                                 caps = icaps;
518                         }
519                 }
520                 gst_caps_unref (thiscaps);
521         } else {
522                 /* no peer or peer have ANY caps, work with our own caps then */
523                 caps = thiscaps;
524         }
525         if (peercaps)
526                 gst_caps_unref (peercaps);
527         if (caps) {
528                 caps = gst_caps_truncate (caps);
529                 
530                 /* now fixate */
531                 if (!gst_caps_is_empty (caps)) {
532                         caps = gst_dreamvideosource_fixate (bsrc, caps);
533                         GST_DEBUG_OBJECT (bsrc, "fixated to: %" GST_PTR_FORMAT, caps);
534                         
535                         if (gst_caps_is_any (caps)) {
536                                 /* hmm, still anything, so element can do anything and
537                                  * nego is not needed */
538                                 result = TRUE;
539                         } else if (gst_caps_is_fixed (caps)) {
540                                 /* yay, fixed caps, use those then */
541                                 result = gst_base_src_set_caps (bsrc, caps);
542                         }
543                 }
544                 gst_caps_unref (caps);
545         }
546         return result;
547         
548         no_nego_needed:
549         {
550                 GST_DEBUG_OBJECT (bsrc, "no negotiation needed");
551                 if (thiscaps)
552                         gst_caps_unref (thiscaps);
553                 return TRUE;
554         }
555 }
556
557 static void
558 gst_dreamvideosource_free_buffer (GstDreamVideoSource * self)
559 {
560         GST_LOG_OBJECT (self, "gst_dreamvideosource_free_buffer");
561 }
562
563 static GstFlowReturn
564 gst_dreamvideosource_create (GstPushSrc * psrc, GstBuffer ** outbuf)
565 {
566         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (psrc);
567         EncoderInfo *enc = self->encoder;
568         
569         GST_LOG_OBJECT (self, "new buffer requested");
570
571         if (!enc) {
572                 GST_WARNING_OBJECT (self, "encoder device not opened!");
573                 return GST_FLOW_ERROR;
574         }
575         
576         while (1)
577         {
578                 *outbuf = NULL;
579                 
580                 if (self->descriptors_available == 0)
581                 {
582                         self->descriptors_count = 0;
583                         int rlen = read(enc->fd, enc->buffer, VBUFSIZE);
584                         if (rlen <= 0 || rlen % VBDSIZE ) {
585                                 GST_WARNING_OBJECT (self, "read error %d (errno %i)", rlen, errno);
586                                 return GST_FLOW_ERROR;
587                         }
588                         self->descriptors_available = rlen / VBDSIZE;
589                         GST_LOG_OBJECT (self, "encoder buffer was empty, %d descriptors available", self->descriptors_available);
590                 }
591
592                 while (self->descriptors_count < self->descriptors_available) {
593                         off_t offset = self->descriptors_count * VBDSIZE;
594                         VideoBufferDescriptor *desc = (VideoBufferDescriptor*)(&enc->buffer[offset]);
595
596                         uint32_t f = desc->stCommon.uiFlags;
597
598                         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);
599
600                         if (G_UNLIKELY (f & CDB_FLAG_METADATA))
601                         { 
602                                 GST_LOG_OBJECT (self, "CDB_FLAG_METADATA... skip outdated packet");
603                                 self->descriptors_count = self->descriptors_available;
604                                 continue;
605                         }
606                         
607                         if (f & VBD_FLAG_DTS_VALID && desc->uiDTS)
608                         {
609                                 if (G_UNLIKELY (self->base_pts == GST_CLOCK_TIME_NONE))
610                                 {
611                                         if (self->dreamaudiosrc)
612                                         {
613                                                 g_mutex_lock (&self->mutex);
614                                                 guint64 audiosource_base_pts;
615                                                 g_signal_emit_by_name(self->dreamaudiosrc, "get-base-pts", &audiosource_base_pts);
616                                                 if (audiosource_base_pts != GST_CLOCK_TIME_NONE)
617                                                 {
618                                                         GST_DEBUG_OBJECT (self, "use DREAMAUDIOSOURCE's base_pts=%" GST_TIME_FORMAT "", GST_TIME_ARGS (audiosource_base_pts) );
619                                                         self->base_pts = audiosource_base_pts;
620                                                 }
621                                                 g_mutex_unlock (&self->mutex);
622                                         }
623                                         if (self->base_pts == GST_CLOCK_TIME_NONE)
624                                         {
625                                                 self->base_pts = MPEGTIME_TO_GSTTIME(desc->uiDTS);
626                                                 GST_DEBUG_OBJECT (self, "use mpeg stream pts as base_pts=%" GST_TIME_FORMAT"", GST_TIME_ARGS (self->base_pts) );
627                                         }
628                                 }
629                         }
630                         
631                         *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);
632                         
633                         if (f & CDB_FLAG_PTS_VALID)
634                         {
635                                 GstClockTime buffer_time = MPEGTIME_TO_GSTTIME(desc->stCommon.uiPTS);
636                                 if (self->base_pts != GST_CLOCK_TIME_NONE && buffer_time > self->base_pts )
637                                 {
638                                         buffer_time -= self->base_pts;
639                                         GST_BUFFER_PTS(*outbuf) = buffer_time;
640                                         GST_BUFFER_DTS(*outbuf) = buffer_time;
641                                 }
642                         }
643 #ifdef dump
644                         int wret = write(self->dumpfd, (unsigned char*)(enc->cdb + desc->stCommon.uiOffset), desc->stCommon.uiLength);
645                         GST_LOG_OBJECT (self, "read %i dumped %i total %" G_GSIZE_FORMAT " ", desc->stCommon.uiLength, wret, gst_buffer_get_size (*outbuf) );
646 #endif
647
648                         self->descriptors_count++;
649
650                         break;
651                 }
652
653                 if (self->descriptors_count == self->descriptors_available) {
654                         GST_LOG_OBJECT (self, "self->descriptors_count == self->descriptors_available -> release %i consumed descriptors", self->descriptors_count);
655                         /* release consumed descs */
656                         if (write(enc->fd, &self->descriptors_count, 4) != 4) {
657                                 GST_WARNING_OBJECT (self, "release consumed descs write error!");
658                                 return GST_FLOW_ERROR;
659                         }
660                         self->descriptors_available = 0;
661                 }
662                 
663                 if (*outbuf)
664                 {
665                         GST_DEBUG_OBJECT (self, "pushing %" GST_PTR_FORMAT "", *outbuf );
666                         return GST_FLOW_OK;
667                 }
668
669         }
670         return GST_FLOW_ERROR;
671 }
672
673 static GstStateChangeReturn gst_dreamvideosource_change_state (GstElement * element, GstStateChange transition)
674 {
675         g_return_val_if_fail (GST_DREAMVIDEOSOURCE (element), FALSE);
676         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (element);
677         int ret;
678
679         switch (transition) {
680                 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
681                         GST_LOG_OBJECT (self, "GST_STATE_CHANGE_PAUSED_TO_PLAYING");
682                         ret = ioctl(self->encoder->fd, VENC_START);
683                         if ( ret != 0 )
684                         {
685                                 GST_ERROR_OBJECT(self,"can't start encoder ioctl!");
686                                 return GST_STATE_CHANGE_FAILURE;
687                         }
688                         GST_INFO_OBJECT (self, "started encoder!");
689                         break;
690                 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
691                         GST_LOG_OBJECT (self, "GST_STATE_CHANGE_PLAYING_TO_PAUSED");
692                         ret = ioctl(self->encoder->fd, VENC_STOP);
693                         if ( ret != 0 )
694                         {
695                                 GST_ERROR_OBJECT(self,"can't stop encoder ioctl!");
696                                 return GST_STATE_CHANGE_FAILURE;
697                         }
698                         break;
699                 default:
700                         break;
701         }
702
703         if (GST_ELEMENT_CLASS (parent_class)->change_state)
704                 return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
705
706         return GST_STATE_CHANGE_SUCCESS;
707 }
708 static gboolean
709 gst_dreamvideosource_start (GstBaseSrc * bsrc)
710 {
711         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (bsrc);
712
713         char fn_buf[32];
714
715         self->encoder = malloc(sizeof(EncoderInfo));
716
717         if(!self->encoder) {
718                 GST_ERROR_OBJECT(self,"out of space");
719                 return FALSE;
720         }
721
722         sprintf(fn_buf, "/dev/venc%d", 0);
723         self->encoder->fd = open(fn_buf, O_RDWR | O_SYNC);
724         if(self->encoder->fd <= 0) {
725                 GST_ERROR_OBJECT(self,"cannot open device %s (%s)", fn_buf, strerror(errno));
726                 free(self->encoder);
727                 self->encoder = NULL;
728                 return FALSE;
729         }
730
731         self->encoder->buffer = malloc(VBUFSIZE);
732         if(!self->encoder->buffer) {
733                 GST_ERROR_OBJECT(self,"cannot alloc buffer");
734                 return FALSE;
735         }
736
737         self->encoder->cdb = (unsigned char *)mmap(0, VMMAPSIZE, PROT_READ, MAP_PRIVATE, self->encoder->fd, 0);
738
739         if(!self->encoder->cdb) {
740                 GST_ERROR_OBJECT(self,"cannot mmap cdb");
741                 return FALSE;
742         }
743 #ifdef dump
744         self->dumpfd = open("/media/hdd/movie/dreamvideosource.dump", O_WRONLY | O_CREAT | O_TRUNC);
745         GST_DEBUG_OBJECT (self, "dumpfd = %i (%s)", self->dumpfd, (self->dumpfd > 0) ? "OK" : strerror(errno));
746 #endif
747
748         gst_dreamvideosource_set_bitrate(self, DEFAULT_BITRATE);
749
750         self->dreamaudiosrc = gst_bin_get_by_name_recurse_up(GST_BIN(GST_ELEMENT_PARENT(self)), "dreamaudiosource0");
751
752         return TRUE;
753 }
754
755 static gboolean
756 gst_dreamvideosource_stop (GstBaseSrc * bsrc)
757 {
758         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (bsrc);
759         if (self->encoder) {
760                 if (self->encoder->fd > 0)
761                         ioctl(self->encoder->fd, VENC_STOP);
762                 close(self->encoder->fd);
763         }
764 #ifdef dump
765         close(self->dumpfd);
766 #endif
767         GST_DEBUG_OBJECT (self, "closed");
768         return TRUE;
769 }
770
771 static void
772 gst_dreamvideosource_finalize (GObject * gobject)
773 {
774         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (gobject);
775         if (self->encoder) {
776                 if (self->encoder->buffer)
777                         free(self->encoder->buffer);
778                 if (self->encoder->cdb) 
779                         munmap(self->encoder->cdb, VMMAPSIZE);
780                 free(self->encoder);
781         }
782         g_mutex_clear (&self->mutex);
783         GST_DEBUG_OBJECT (self, "finalized");
784         G_OBJECT_CLASS (parent_class)->finalize (gobject);
785 }