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