implement bitrate property setters
[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_BITRATE,
39 };
40
41 static guint gst_dreamvideosource_signals[LAST_SIGNAL] = { 0 };
42
43
44 #define DEFAULT_BITRATE 2048
45
46 static GstStaticPadTemplate srctemplate =
47     GST_STATIC_PAD_TEMPLATE ("src",
48         GST_PAD_SRC,
49         GST_PAD_ALWAYS,
50         GST_STATIC_CAPS ("video/x-h264, "
51         "width = " GST_VIDEO_SIZE_RANGE ", "
52         "height = " GST_VIDEO_SIZE_RANGE ", "
53         "framerate = " GST_VIDEO_FPS_RANGE ", "
54         "stream-format = (string) byte-stream, "
55         "profile = (string) main")
56     );
57
58 #define gst_dreamvideosource_parent_class parent_class
59 G_DEFINE_TYPE (GstDreamVideoSource, gst_dreamvideosource, GST_TYPE_PUSH_SRC);
60
61 static GstCaps *gst_dreamvideosource_getcaps (GstBaseSrc * psrc, GstCaps * filter);
62 static gboolean gst_dreamvideosource_start (GstBaseSrc * bsrc);
63 static gboolean gst_dreamvideosource_stop (GstBaseSrc * bsrc);
64 static void gst_dreamvideosource_finalize (GObject * gobject);
65 static GstFlowReturn gst_dreamvideosource_create (GstPushSrc * psrc, GstBuffer ** outbuf);
66
67 static void gst_dreamvideosource_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
68 static void gst_dreamvideosource_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
69
70 static gint64 gst_dreamvideosource_get_base_pts (GstDreamVideoSource *self);
71
72 static void
73 gst_dreamvideosource_class_init (GstDreamVideoSourceClass * klass)
74 {
75         GObjectClass *gobject_class;
76         GstElementClass *gstelement_class;
77         GstBaseSrcClass *gstbasesrc_class;
78         GstPushSrcClass *gstpush_src_class;
79
80         gobject_class = (GObjectClass *) klass;
81         gstelement_class = (GstElementClass *) klass;
82         gstbasesrc_class = (GstBaseSrcClass *) klass;
83         gstpush_src_class = (GstPushSrcClass *) klass;
84
85         gobject_class->set_property = gst_dreamvideosource_set_property;
86         gobject_class->get_property = gst_dreamvideosource_get_property;
87         gobject_class->finalize = gst_dreamvideosource_finalize;
88
89         gst_element_class_add_pad_template (gstelement_class,
90                                             gst_static_pad_template_get (&srctemplate));
91
92         gst_element_class_set_static_metadata (gstelement_class,
93             "Dream Video source", "Source/Video",
94             "Provide an h.264 video elementary stream from Dreambox encoder device",
95             "Andreas Frisch <fraxinas@opendreambox.org>");
96
97         gstbasesrc_class->get_caps = gst_dreamvideosource_getcaps;
98         gstbasesrc_class->start = gst_dreamvideosource_start;
99         gstbasesrc_class->stop = gst_dreamvideosource_stop;
100
101         gstpush_src_class->create = gst_dreamvideosource_create;
102         
103         g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BITRATE,
104           g_param_spec_int ("bitrate", "Bitrate (kb/s)",
105             "Bitrate in kbit/sec", 16, 200000, DEFAULT_BITRATE,
106             G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
107
108            gst_dreamvideosource_signals[SIGNAL_GET_BASE_PTS] =
109                 g_signal_new ("get-base-pts",
110                 G_TYPE_FROM_CLASS (klass),
111                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
112                 G_STRUCT_OFFSET (GstDreamVideoSourceClass, get_base_pts),
113                 NULL, NULL, gst_dreamsource_marshal_INT64__VOID, G_TYPE_INT64, 0);
114
115         klass->get_base_pts = gst_dreamvideosource_get_base_pts;
116 }
117
118 static gint64
119 gst_dreamvideosource_get_base_pts (GstDreamVideoSource *self)
120 {
121         GST_DEBUG_OBJECT (self, "gst_dreamvideosource_get_base_pts " GST_TIME_FORMAT"", GST_TIME_ARGS (self->base_pts) );
122         return self->base_pts;
123 }
124
125 gboolean
126 gst_dreamvideosource_plugin_init (GstPlugin *plugin)
127 {
128         GST_DEBUG_CATEGORY_INIT (dreamvideosource_debug, "dreamvideosource", 0, "dreamvideosource");
129         return gst_element_register (plugin, "dreamvideosource", GST_RANK_PRIMARY, GST_TYPE_DREAMVIDEOSOURCE);
130 }
131
132 static void
133 gst_dreamvideosource_init (GstDreamVideoSource * self)
134 {
135         self->encoder = NULL;
136         self->descriptors_available = 0;
137         self->video_info.width = 1280;
138         self->video_info.height = 720;
139         self->video_info.par_n = 16;
140         self->video_info.par_d = 16;
141         self->video_info.fps_n = 25;
142         self->video_info.fps_d = 1;
143         self->base_pts = GST_CLOCK_TIME_NONE;
144
145         g_mutex_init (&self->mutex);
146         
147         gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
148         gst_base_src_set_live (GST_BASE_SRC (self), TRUE);
149 }
150
151 static void gst_dreamvideosource_set_bitrate (GstDreamVideoSource * self, uint32_t bitrate)
152 {
153         if (!self->encoder || !self->encoder->fd)
154                 return;
155         uint32_t vbr = bitrate*1000;            
156         int ret = ioctl(self->encoder->fd, VENC_SET_BITRATE, &vbr);
157         if (ret != 0)
158         {
159                 GST_WARNING_OBJECT (self, "can't set video bitrate to %i bytes/s!", vbr);
160                 return;
161         }
162         GST_INFO_OBJECT (self, "set video bitrate to %i kBytes/s", bitrate);
163         self->video_info.bitrate = vbr;
164 }
165
166 static void
167 gst_dreamvideosource_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
168 {
169         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (object);
170         
171         switch (prop_id) {
172                 case ARG_BITRATE:
173                         gst_dreamvideosource_set_bitrate(self, g_value_get_int (value));
174                         break;
175                 default:
176                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
177                         break;
178         }
179 }
180
181 static void
182 gst_dreamvideosource_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
183 {
184         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (object);
185         
186         switch (prop_id) {
187                 case ARG_BITRATE:
188                         g_value_set_int (value, self->video_info.bitrate/1000);
189                         break;
190                 default:
191                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
192                         break;
193         }
194 }
195
196 static GstCaps *
197 gst_dreamvideosource_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
198 {
199         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (bsrc);
200         GstPadTemplate *pad_template;
201         GstCaps *caps;
202
203         pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS(self), "src");
204         g_return_val_if_fail (pad_template != NULL, NULL);
205
206         if (self->encoder == NULL) {
207                 GST_DEBUG_OBJECT (self, "encoder not opened -> use template caps");
208                 caps = gst_pad_template_get_caps (pad_template);
209         }
210         else
211         {
212                 caps = gst_caps_make_writable(gst_pad_template_get_caps (pad_template));
213                 gst_caps_set_simple(caps, "width", G_TYPE_INT, self->video_info.width, NULL);
214                 gst_caps_set_simple(caps, "height", G_TYPE_INT, self->video_info.height, NULL);
215                 gst_caps_set_simple(caps, "framerate", GST_TYPE_FRACTION, self->video_info.fps_n, self->video_info.fps_d, NULL);
216         }
217
218         GST_INFO_OBJECT (self, "return caps %" GST_PTR_FORMAT, caps);
219         return caps;
220 }
221
222 static void
223 gst_dreamvideosource_free_buffer (GstDreamVideoSource * self)
224 {
225         GST_LOG_OBJECT (self, "gst_dreamvideosource_free_buffer");
226 }
227
228 static GstFlowReturn
229 gst_dreamvideosource_create (GstPushSrc * psrc, GstBuffer ** outbuf)
230 {
231         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (psrc);
232         EncoderInfo *enc = self->encoder;
233         
234         GST_LOG_OBJECT (self, "new buffer requested");
235
236         if (!enc) {
237                 GST_WARNING_OBJECT (self, "encoder device not opened!");
238                 return GST_FLOW_ERROR;
239         }
240         
241         while (1)
242         {
243                 *outbuf = NULL;
244                 
245                 if (self->descriptors_available == 0)
246                 {
247                         self->descriptors_count = 0;
248                         int rlen = read(enc->fd, enc->buffer, VBUFSIZE);
249                         if (rlen <= 0 || rlen % VBDSIZE ) {
250                                 GST_WARNING_OBJECT (self, "read error %d (errno %i)", rlen, errno);
251                                 return GST_FLOW_ERROR;
252                         }
253                         self->descriptors_available = rlen / VBDSIZE;
254                         GST_LOG_OBJECT (self, "encoder buffer was empty, %d descriptors available", self->descriptors_available);
255                 }
256
257                 while (self->descriptors_count < self->descriptors_available) {
258                         off_t offset = self->descriptors_count * VBDSIZE;
259                         VideoBufferDescriptor *desc = (VideoBufferDescriptor*)(&enc->buffer[offset]);
260
261                         uint32_t f = desc->stCommon.uiFlags;
262
263                         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);
264
265                         if (G_UNLIKELY (f & CDB_FLAG_METADATA))
266                         { 
267                                 GST_LOG_OBJECT (self, "CDB_FLAG_METADATA... skip outdated packet");
268                                 self->descriptors_count = self->descriptors_available;
269                                 continue;
270                         }
271                         
272                         if (f & VBD_FLAG_DTS_VALID && desc->uiDTS)
273                         {
274                                 if (G_UNLIKELY (self->base_pts == GST_CLOCK_TIME_NONE))
275                                 {
276                                         if (self->dreamaudiosrc)
277                                         {
278                                                 g_mutex_lock (&self->mutex);
279                                                 guint64 audiosource_base_pts;
280                                                 g_signal_emit_by_name(self->dreamaudiosrc, "get-base-pts", &audiosource_base_pts);
281                                                 if (audiosource_base_pts != GST_CLOCK_TIME_NONE)
282                                                 {
283                                                         GST_DEBUG_OBJECT (self, "use DREAMAUDIOSOURCE's base_pts=%" GST_TIME_FORMAT "", GST_TIME_ARGS (audiosource_base_pts) );
284                                                         self->base_pts = audiosource_base_pts;
285                                                 }
286                                                 g_mutex_unlock (&self->mutex);
287                                         }
288                                         if (self->base_pts == GST_CLOCK_TIME_NONE)
289                                         {
290                                                 self->base_pts = MPEGTIME_TO_GSTTIME(desc->uiDTS);
291                                                 GST_DEBUG_OBJECT (self, "use mpeg stream pts as base_pts=%" GST_TIME_FORMAT"", GST_TIME_ARGS (self->base_pts) );
292                                         }
293                                 }
294                         }
295                         
296                         *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);
297                         
298                         if (f & CDB_FLAG_PTS_VALID)
299                         {
300                                 GstClockTime buffer_time = MPEGTIME_TO_GSTTIME(desc->stCommon.uiPTS);
301                                 if (self->base_pts != GST_CLOCK_TIME_NONE && buffer_time > self->base_pts )
302                                 {
303                                         buffer_time -= self->base_pts;
304                                         GST_BUFFER_PTS(*outbuf) = buffer_time;
305                                         GST_BUFFER_DTS(*outbuf) = buffer_time;
306                                 }
307                         }
308 #ifdef dump
309                         int wret = write(self->dumpfd, (unsigned char*)(enc->cdb + desc->stCommon.uiOffset), desc->stCommon.uiLength);
310                         GST_LOG_OBJECT (self, "read %i dumped %i total %" G_GSIZE_FORMAT " ", desc->stCommon.uiLength, wret, gst_buffer_get_size (*outbuf) );
311 #endif
312
313                         self->descriptors_count++;
314
315                         break;
316                 }
317
318                 if (self->descriptors_count == self->descriptors_available) {
319                         GST_LOG_OBJECT (self, "self->descriptors_count == self->descriptors_available -> release %i consumed descriptors", self->descriptors_count);
320                         /* release consumed descs */
321                         if (write(enc->fd, &self->descriptors_count, 4) != 4) {
322                                 GST_WARNING_OBJECT (self, "release consumed descs write error!");
323                                 return GST_FLOW_ERROR;
324                         }
325                         self->descriptors_available = 0;
326                 }
327                 
328                 if (*outbuf)
329                 {
330                         GST_DEBUG_OBJECT (self, "pushing %" GST_PTR_FORMAT "", *outbuf );
331                         return GST_FLOW_OK;
332                 }
333
334         }
335         return GST_FLOW_ERROR;
336 }
337
338 static gboolean
339 gst_dreamvideosource_start (GstBaseSrc * bsrc)
340 {
341         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (bsrc);
342
343         char fn_buf[32];
344
345         self->encoder = malloc(sizeof(EncoderInfo));
346
347         if(!self->encoder) {
348                 GST_ERROR_OBJECT(self,"out of space");
349                 return FALSE;
350         }
351
352         sprintf(fn_buf, "/dev/venc%d", 0);
353         self->encoder->fd = open(fn_buf, O_RDWR | O_SYNC);
354         if(self->encoder->fd <= 0) {
355                 GST_ERROR_OBJECT(self,"cannot open device %s (%s)", fn_buf, strerror(errno));
356                 free(self->encoder);
357                 self->encoder = NULL;
358                 return FALSE;
359         }
360
361         self->encoder->buffer = malloc(VBUFSIZE);
362         if(!self->encoder->buffer) {
363                 GST_ERROR_OBJECT(self,"cannot alloc buffer");
364                 return FALSE;
365         }
366
367         self->encoder->cdb = (unsigned char *)mmap(0, VMMAPSIZE, PROT_READ, MAP_PRIVATE, self->encoder->fd, 0);
368
369         if(!self->encoder->cdb) {
370                 GST_ERROR_OBJECT(self,"cannot mmap cdb");
371                 return FALSE;
372         }
373 #ifdef dump
374         self->dumpfd = open("/media/hdd/movie/dreamvideosource.dump", O_WRONLY | O_CREAT | O_TRUNC);
375         GST_DEBUG_OBJECT (self, "dumpfd = %i (%s)", self->dumpfd, (self->dumpfd > 0) ? "OK" : strerror(errno));
376 #endif
377
378         gst_dreamvideosource_set_bitrate(self, DEFAULT_BITRATE);
379
380         int ret = ioctl(self->encoder->fd, VENC_START);
381         if ( ret != 0 )
382         {
383                 GST_ERROR_OBJECT(self,"can't start encoder ioctl!");
384                 return FALSE;
385         }
386         GST_INFO_OBJECT (self, "started encoder!");
387         
388         self->dreamaudiosrc = gst_bin_get_by_name_recurse_up(GST_BIN(GST_ELEMENT_PARENT(self)), "dreamaudiosource0");
389
390         return TRUE;
391 }
392
393 static gboolean
394 gst_dreamvideosource_stop (GstBaseSrc * bsrc)
395 {
396         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (bsrc);
397         if (self->encoder) {
398                 if (self->encoder->fd > 0)
399                         ioctl(self->encoder->fd, VENC_STOP);
400                 close(self->encoder->fd);
401         }
402 #ifdef dump
403         close(self->dumpfd);
404 #endif
405         GST_DEBUG_OBJECT (self, "closed");
406         return TRUE;
407 }
408
409 static void
410 gst_dreamvideosource_finalize (GObject * gobject)
411 {
412         GstDreamVideoSource *self = GST_DREAMVIDEOSOURCE (gobject);
413         if (self->encoder) {
414                 if (self->encoder->buffer)
415                         free(self->encoder->buffer);
416                 if (self->encoder->cdb) 
417                         munmap(self->encoder->cdb, VMMAPSIZE);
418                 free(self->encoder);
419         }
420         g_mutex_clear (&self->mutex);
421         GST_DEBUG_OBJECT (self, "finalized");
422         G_OBJECT_CLASS (parent_class)->finalize (gobject);
423 }