implement async unlocking in videosource also. improve state-change handlers (still...
[gst-plugin-dreamsource.git] / src / gstdreamaudiosource.c
1 /*
2  * GStreamer dreamaudiosource
3  * Copyright 2014-2015 Andreas Frisch <fraxinas@opendreambox.org>
4  *
5  * This program is licensed under the Creative Commons
6  * Attribution-NonCommercial-ShareAlike 3.0 Unported
7  * License. To view a copy of this license, visit
8  * http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to
9  * Creative Commons,559 Nathan Abbott Way,Stanford,California 94305,USA.
10  *
11  * Alternatively, this program may be distributed and executed on
12  * hardware which is licensed by Dream Property GmbH.
13  *
14  * This program is NOT free software. It is open source, you are allowed
15  * to modify it (if you keep the license), but it may not be commercially
16  * distributed other than under the conditions noted above.
17  */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <gst/gst.h>
24 #include "gstdreamaudiosource.h"
25
26 GST_DEBUG_CATEGORY_STATIC (dreamaudiosource_debug);
27 #define GST_CAT_DEFAULT dreamaudiosource_debug
28
29 enum
30 {
31         SIGNAL_GET_BASE_PTS,
32         LAST_SIGNAL
33 };
34 enum
35 {
36         ARG_0,
37         ARG_BITRATE,
38 };
39
40 static guint gst_dreamaudiosource_signals[LAST_SIGNAL] = { 0 };
41
42 #define DEFAULT_BITRATE 128
43
44 static GstStaticPadTemplate srctemplate =
45     GST_STATIC_PAD_TEMPLATE ("src",
46         GST_PAD_SRC,
47         GST_PAD_ALWAYS,
48         GST_STATIC_CAPS ("audio/mpeg, "
49         "mpegversion = 4,"
50         "stream-format = (string) adts")
51     );
52
53 #define gst_dreamaudiosource_parent_class parent_class
54 G_DEFINE_TYPE (GstDreamAudioSource, gst_dreamaudiosource, GST_TYPE_PUSH_SRC);
55
56 static GstCaps *gst_dreamaudiosource_getcaps (GstBaseSrc * psrc, GstCaps * filter);
57 static gboolean gst_dreamaudiosource_start (GstBaseSrc * bsrc);
58 static gboolean gst_dreamaudiosource_stop (GstBaseSrc * bsrc);
59 static gboolean gst_dreamaudiosource_unlock (GstBaseSrc * bsrc);
60 static void gst_dreamaudiosource_dispose (GObject * gobject);
61 static GstFlowReturn gst_dreamaudiosource_create (GstPushSrc * psrc, GstBuffer ** outbuf);
62
63 static void gst_dreamaudiosource_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
64 static void gst_dreamaudiosource_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
65
66 static GstStateChangeReturn gst_dreamaudiosource_change_state (GstElement * element, GstStateChange transition);
67 static gint64 gst_dreamaudiosource_get_base_pts (GstDreamAudioSource *self);
68
69 static void
70 gst_dreamaudiosource_class_init (GstDreamAudioSourceClass * klass)
71 {
72         GObjectClass *gobject_class;
73         GstElementClass *gstelement_class;
74         GstBaseSrcClass *gstbasesrc_class;
75         GstPushSrcClass *gstpush_src_class;
76
77         gobject_class = (GObjectClass *) klass;
78         gstelement_class = (GstElementClass *) klass;
79         gstbasesrc_class = (GstBaseSrcClass *) klass;
80         gstpush_src_class = (GstPushSrcClass *) klass;
81
82         gobject_class->set_property = gst_dreamaudiosource_set_property;
83         gobject_class->get_property = gst_dreamaudiosource_get_property;
84         gobject_class->dispose = gst_dreamaudiosource_dispose;
85
86         gst_element_class_add_pad_template (gstelement_class,
87                                             gst_static_pad_template_get (&srctemplate));
88
89         gst_element_class_set_static_metadata (gstelement_class,
90             "Dream Audio source", "Source/Audio",
91             "Provide an audio elementary stream from Dreambox encoder device",
92             "Andreas Frisch <fraxinas@opendreambox.org>");
93
94         gstelement_class->change_state = gst_dreamaudiosource_change_state;
95
96         gstbasesrc_class->get_caps = gst_dreamaudiosource_getcaps;
97         gstbasesrc_class->start = gst_dreamaudiosource_start;
98         gstbasesrc_class->stop = gst_dreamaudiosource_stop;
99         gstbasesrc_class->unlock = gst_dreamaudiosource_unlock;
100
101         gstpush_src_class->create = gst_dreamaudiosource_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, 320, DEFAULT_BITRATE,
106             G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
107
108         gst_dreamaudiosource_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 (GstDreamAudioSourceClass, get_base_pts),
113                 NULL, NULL, gst_dreamsource_marshal_INT64__VOID, G_TYPE_INT64, 0);
114
115         klass->get_base_pts = gst_dreamaudiosource_get_base_pts;
116
117 }
118
119 static gint64
120 gst_dreamaudiosource_get_base_pts (GstDreamAudioSource *self)
121 {
122         GST_DEBUG_OBJECT (self, "gst_dreamaudiosource_get_base_pts %" GST_TIME_FORMAT"", GST_TIME_ARGS (self->base_pts) );
123         return self->base_pts;
124 }
125
126 static void gst_dreamaudiosource_set_bitrate (GstDreamAudioSource * self, uint32_t bitrate)
127 {
128         if (!self->encoder || !self->encoder->fd)
129                 return;
130         uint32_t abr = bitrate*1000;
131         int ret = ioctl(self->encoder->fd, AENC_SET_BITRATE, &abr);
132         if (ret != 0)
133         {
134                 GST_WARNING_OBJECT (self, "can't set audio bitrate to %i bytes/s!", abr);
135                 return;
136         }
137         GST_INFO_OBJECT (self, "set audio bitrate to %i kBytes/s", bitrate);
138         self->audio_info.bitrate = abr;
139 }
140
141 gboolean
142 gst_dreamaudiosource_plugin_init (GstPlugin *plugin)
143 {
144         GST_DEBUG_CATEGORY_INIT (dreamaudiosource_debug, "dreamaudiosource", 0, "dreamaudiosource");
145         return gst_element_register (plugin, "dreamaudiosource", GST_RANK_PRIMARY, GST_TYPE_DREAMAUDIOSOURCE);
146 }
147
148 static void
149 gst_dreamaudiosource_init (GstDreamAudioSource * self)
150 {
151         self->encoder = NULL;
152         self->buffers_list = NULL;
153         self->descriptors_available = 0;
154
155         g_mutex_init (&self->mutex);
156         READ_SOCKET (self) = -1;
157         WRITE_SOCKET (self) = -1;
158
159         gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
160         gst_base_src_set_live (GST_BASE_SRC (self), TRUE);
161
162         self->encoder = malloc(sizeof(EncoderInfo));
163
164         if(!self->encoder) {
165                 GST_ERROR_OBJECT(self,"out of space");
166                 return;
167         }
168
169         char fn_buf[32];
170         sprintf(fn_buf, "/dev/aenc%d", 0);
171         self->encoder->fd = open(fn_buf, O_RDWR | O_SYNC);
172         if(self->encoder->fd <= 0) {
173                 GST_ERROR_OBJECT(self,"cannot open device %s (%s)", fn_buf, strerror(errno));
174                 free(self->encoder);
175                 self->encoder = NULL;
176                 return;
177         }
178
179         int flags = fcntl(self->encoder->fd, F_GETFL, 0);
180         fcntl(self->encoder->fd, F_SETFL, flags | O_NONBLOCK);
181
182         self->encoder->buffer = malloc(ABUFSIZE);
183         if(!self->encoder->buffer) {
184                 GST_ERROR_OBJECT(self,"cannot alloc buffer");
185                 return;
186         }
187
188         self->encoder->cdb = (unsigned char *)mmap(0, AMMAPSIZE, PROT_READ, MAP_PRIVATE, self->encoder->fd, 0);
189         if(!self->encoder->cdb || self->encoder->cdb== MAP_FAILED) {
190                 GST_ERROR_OBJECT(self,"cannot mmap cdb: %s (%d)", strerror(errno));
191                 return;
192         }
193
194 #ifdef dump
195         self->dumpfd = open("/media/hdd/movie/dreamaudiosource.dump", O_WRONLY | O_CREAT | O_TRUNC);
196         GST_DEBUG_OBJECT (self, "dumpfd = %i (%s)", self->dumpfd, (self->dumpfd > 0) ? "OK" : strerror(errno));
197 #endif
198 }
199
200 static void
201 gst_dreamaudiosource_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
202 {
203         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (object);
204
205         switch (prop_id) {
206                 case ARG_BITRATE:
207                         gst_dreamaudiosource_set_bitrate(self, g_value_get_int (value));
208                         break;
209                 default:
210                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
211                         break;
212         }
213 }
214
215 static void
216 gst_dreamaudiosource_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
217 {
218         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (object);
219
220         switch (prop_id) {
221                 case ARG_BITRATE:
222                         g_value_set_int (value, self->audio_info.bitrate/1000);
223                         break;
224                 default:
225                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
226                         break;
227         }
228 }
229
230 static GstCaps *
231 gst_dreamaudiosource_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
232 {
233         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (bsrc);
234         GstPadTemplate *pad_template;
235         GstCaps *caps;
236
237         pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS(self), "src");
238         g_return_val_if_fail (pad_template != NULL, NULL);
239
240         if (self->encoder == NULL) {
241                 GST_DEBUG_OBJECT (self, "encoder not opened -> use template caps");
242                 caps = gst_pad_template_get_caps (pad_template);
243         }
244         else
245         {
246                 caps = gst_caps_make_writable(gst_pad_template_get_caps (pad_template));
247         }
248
249         GST_DEBUG_OBJECT (self, "return caps %" GST_PTR_FORMAT, caps);
250         return caps;
251 }
252
253 static gboolean gst_dreamaudiosource_unlock (GstBaseSrc * bsrc)
254 {
255         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (bsrc);
256         GST_DEBUG_OBJECT (self, "stop creating buffers");
257         SEND_COMMAND (self, CONTROL_STOP);
258         return TRUE;
259 }
260
261 static void
262 gst_dreamaudiosource_free_buffer (struct _bufferdebug * bufferdebug)
263 {
264         GST_OBJECT_LOCK (bufferdebug->self);
265         GList *list = g_list_first (bufferdebug->self->buffers_list);
266         int count = 0;
267         while (list) {
268                 GST_TRACE_OBJECT (bufferdebug->self, "buffers_list[%i] = %" GST_PTR_FORMAT "", count, list->data);
269                 count++;
270                 list = g_list_next (list);
271         }
272         bufferdebug->self->buffers_list = g_list_remove(g_list_first (bufferdebug->self->buffers_list), bufferdebug->buffer);
273         GST_TRACE_OBJECT (bufferdebug->self, "removing %" GST_PTR_FORMAT " @ %" GST_TIME_FORMAT " from list -> new length=%i", bufferdebug->buffer, GST_TIME_ARGS (bufferdebug->buffer_pts), g_list_length(bufferdebug->self->buffers_list));
274         GST_OBJECT_UNLOCK (bufferdebug->self);
275 }
276
277 static GstFlowReturn
278 gst_dreamaudiosource_create (GstPushSrc * psrc, GstBuffer ** outbuf)
279 {
280         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (psrc);
281         EncoderInfo *enc = self->encoder;
282
283         static int dumpoffset;
284
285         GST_LOG_OBJECT (self, "new buffer requested");
286
287         if (!enc) {
288                 GST_WARNING_OBJECT (self, "encoder device not opened!");
289                 return GST_FLOW_ERROR;
290         }
291
292         while (1)
293         {
294                 *outbuf = NULL;
295
296                 if (self->descriptors_available == 0)
297                 {
298                         self->descriptors_count = 0;
299                         struct pollfd rfd[2];
300
301                         rfd[0].fd = enc->fd;
302                         rfd[0].events = POLLIN;
303                         rfd[1].fd = READ_SOCKET (self);
304                         rfd[1].events = POLLIN | POLLERR | POLLHUP | POLLPRI;
305
306                         int ret = poll(rfd, 2, 200);
307
308                         if (G_UNLIKELY (ret == -1))
309                         {
310                                 GST_ERROR_OBJECT (self, "SELECT ERROR!");
311                                 return GST_FLOW_ERROR;
312                         }
313                         else if ( ret == 0 )
314                         {
315                                 GST_LOG_OBJECT (self, "SELECT TIMEOUT");
316                                 //!!! TODO generate dummy payload
317                                 *outbuf = gst_buffer_new();
318                         }
319                         else if ( G_UNLIKELY (rfd[1].revents) )
320                         {
321                                 char command;
322                                 READ_COMMAND (self, command, ret);
323                                 if (command == CONTROL_STOP)
324                                 {
325                                         GST_LOG_OBJECT (self, "CONTROL_STOP!");
326                                         return GST_FLOW_FLUSHING;
327                                 }
328                         }
329                         else if ( G_LIKELY(rfd[0].revents & POLLIN) )
330                         {
331                                 int rlen = read(enc->fd, enc->buffer, ABUFSIZE);
332                                 if (rlen <= 0 || rlen % ABDSIZE ) {
333                                         if ( errno == 512 )
334                                                 return GST_FLOW_FLUSHING;
335                                         GST_WARNING_OBJECT (self, "read error %s (%i)", strerror(errno), errno);
336                                         return GST_FLOW_ERROR;
337                                 }
338                                 self->descriptors_available = rlen / ABDSIZE;
339                                 GST_LOG_OBJECT (self, "encoder buffer was empty, %d descriptors available", self->descriptors_available);
340                         }
341                 }
342
343                 while (self->descriptors_count < self->descriptors_available) {
344                         off_t offset = self->descriptors_count * ABDSIZE;
345                         AudioBufferDescriptor *desc = (AudioBufferDescriptor*)(&enc->buffer[offset]);
346
347                         uint32_t f = desc->stCommon.uiFlags;
348
349                         GST_LOG_OBJECT (self, "descriptors_count=%d, descriptors_available=%u\tuiOffset=%u, uiLength=%"G_GSIZE_FORMAT"", self->descriptors_count, self->descriptors_available, desc->stCommon.uiOffset, desc->stCommon.uiLength);
350
351                         if (f & CDB_FLAG_METADATA) {
352                                 GST_LOG_OBJECT (self, "CDB_FLAG_METADATA... skip outdated packet");
353                                 self->descriptors_count = self->descriptors_available;
354                                 continue;
355                         }
356
357                         struct _bufferdebug * bdg = NULL;
358                         GDestroyNotify buffer_free_func = NULL;
359                         if ( gst_debug_category_get_threshold (dreamaudiosource_debug) >= GST_LEVEL_TRACE)
360                         {
361                                 bdg = malloc(sizeof(struct _bufferdebug));
362                                 buffer_free_func = (GDestroyNotify) gst_dreamaudiosource_free_buffer;
363                         }
364
365                         *outbuf = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, enc->cdb, AMMAPSIZE, desc->stCommon.uiOffset, desc->stCommon.uiLength, bdg, buffer_free_func);
366
367                         if (bdg)
368                         {
369                                 bdg->self = self;
370                                 bdg->buffer = *outbuf;
371                                 self->buffers_list = g_list_append(self->buffers_list, *outbuf);
372                         }
373
374                         if (f & CDB_FLAG_PTS_VALID)
375                         {
376                                 if (G_UNLIKELY (self->base_pts == GST_CLOCK_TIME_NONE))
377                                 {
378                                         if (self->dreamvideosrc)
379                                         {
380                                                 g_mutex_lock (&self->mutex);
381                                                 guint64 videosource_base_pts;
382                                                 g_signal_emit_by_name(self->dreamvideosrc, "get-base-pts", &videosource_base_pts);
383                                                 if (videosource_base_pts != GST_CLOCK_TIME_NONE)
384                                                 {
385                                                         GST_DEBUG_OBJECT (self, "use DREAMVIDEOSOURCE's base_pts=%" GST_TIME_FORMAT "", GST_TIME_ARGS (videosource_base_pts) );
386                                                         self->base_pts = videosource_base_pts;
387                                                 }
388                                                 g_mutex_unlock (&self->mutex);
389                                         }
390                                         else
391                                         {
392                                                 self->base_pts = MPEGTIME_TO_GSTTIME(desc->stCommon.uiPTS);
393                                                 GST_DEBUG_OBJECT (self, "use mpeg stream pts as base_pts=%" GST_TIME_FORMAT"", GST_TIME_ARGS (self->base_pts) );
394                                         }
395                                 }
396                                 GstClockTime buffer_time = MPEGTIME_TO_GSTTIME(desc->stCommon.uiPTS);
397                                 if (self->base_pts != GST_CLOCK_TIME_NONE && buffer_time > self->base_pts )
398                                 {
399                                         buffer_time -= self->base_pts;
400                                         GST_BUFFER_PTS(*outbuf) = buffer_time;
401                                         GST_BUFFER_DTS(*outbuf) = buffer_time;
402                                 }
403                                 if (bdg)
404                                         bdg->buffer_pts = buffer_time;
405                         }
406
407 #ifdef dump
408                         int wret = write(self->dumpfd, (unsigned char*)(enc->cdb + desc->stCommon.uiOffset), desc->stCommon.uiLength);
409                         dumpoffset += wret;
410                         GST_LOG_OBJECT (self, "read %"G_GSIZE_FORMAT" dumped %i total 0x%08X", desc->stCommon.uiLength, wret, dumpoffset );
411 #endif
412                         self->descriptors_count++;
413
414                         break;
415                 }
416
417                 if (self->descriptors_count == self->descriptors_available) {
418                         GST_LOG_OBJECT (self, "self->descriptors_count == self->descriptors_available -> release %i consumed descriptors", self->descriptors_count);
419                         /* release consumed descs */
420                         if (write(enc->fd, &self->descriptors_count, 4) != 4) {
421                                 GST_WARNING_OBJECT (self, "release consumed descs write error!");
422                                 return GST_FLOW_ERROR;
423                         }
424                         self->descriptors_available = 0;
425                 }
426
427                 if (*outbuf)
428                 {
429                         GST_DEBUG_OBJECT (self, "pushing %" GST_PTR_FORMAT "", *outbuf );
430                         return GST_FLOW_OK;
431                 }
432         }
433
434         return GST_FLOW_ERROR;
435 }
436
437 static GstStateChangeReturn gst_dreamaudiosource_change_state (GstElement * element, GstStateChange transition)
438 {
439         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (element);
440         GstStateChangeReturn sret = GST_STATE_CHANGE_SUCCESS;
441         int ret;
442         GST_OBJECT_LOCK (self);
443         switch (transition) {
444                 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
445                         GST_LOG_OBJECT (self, "GST_STATE_CHANGE_PAUSED_TO_PLAYING");
446                         self->base_pts = GST_CLOCK_TIME_NONE;
447                         ret = ioctl(self->encoder->fd, AENC_START);
448                         if ( ret != 0 )
449                                 goto fail;
450                         self->descriptors_available = 0;
451                         GST_INFO_OBJECT (self, "started encoder!");
452                         break;
453                 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
454                         GST_INFO_OBJECT (self, "GST_STATE_CHANGE_PLAYING_TO_PAUSED self->descriptors_count=%i self->descriptors_available=%i", self->descriptors_count, self->descriptors_available);
455                         //!!! TODO this frequently deadloops
456                         while (self->descriptors_count < self->descriptors_available)
457                                 GST_INFO_OBJECT (self, "flushing self->descriptors_count=%i", self->descriptors_count++);
458                         if (self->descriptors_count)
459                                 write(self->encoder->fd, &self->descriptors_count, 4);
460                         ret = ioctl(self->encoder->fd, AENC_STOP);
461                         if ( ret != 0 )
462                                 goto fail;
463                         GST_INFO_OBJECT (self, "stopped encoder!");
464                         break;
465                 default:
466                         break;
467         }
468         GST_OBJECT_UNLOCK (self);
469         if (GST_ELEMENT_CLASS (parent_class)->change_state)
470                 sret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
471         return sret;
472 fail:
473         GST_ERROR_OBJECT(self,"can't perform encoder ioctl!");
474         GST_OBJECT_UNLOCK (self);
475         return GST_STATE_CHANGE_FAILURE;
476 }
477
478 static gboolean
479 gst_dreamaudiosource_start (GstBaseSrc * bsrc)
480 {
481         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (bsrc);
482         self->dreamvideosrc = gst_bin_get_by_name_recurse_up(GST_BIN(GST_ELEMENT_PARENT(self)), "dreamvideosource0");
483
484         int control_sock[2];
485         if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
486         {
487                 GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, (NULL), GST_ERROR_SYSTEM);
488                 return FALSE;
489         }
490         READ_SOCKET (self) = control_sock[0];
491         WRITE_SOCKET (self) = control_sock[1];
492         fcntl (READ_SOCKET (self), F_SETFL, O_NONBLOCK);
493         fcntl (WRITE_SOCKET (self), F_SETFL, O_NONBLOCK);
494
495         GST_DEBUG_OBJECT (self, "started. reference to dreamvideosource=%" GST_PTR_FORMAT"", self->dreamvideosrc);
496         return TRUE;
497 }
498
499 static gboolean
500 gst_dreamaudiosource_stop (GstBaseSrc * bsrc)
501 {
502         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (bsrc);
503         if (self->dreamvideosrc)
504                 gst_object_unref(self->dreamvideosrc);
505         GST_DEBUG_OBJECT (self, "stop");
506         return TRUE;
507 }
508
509 static void
510 gst_dreamaudiosource_dispose (GObject * gobject)
511 {
512         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (gobject);
513         if (self->encoder) {
514                 if (self->encoder->buffer)
515                         free(self->encoder->buffer);
516                 if (self->encoder->cdb)
517                         munmap(self->encoder->cdb, AMMAPSIZE);
518                 if (self->encoder->fd)
519                         close(self->encoder->fd);
520                 free(self->encoder);
521         }
522 #ifdef dump
523         close(self->dumpfd);
524 #endif
525         g_list_free(self->buffers_list);
526         g_mutex_clear (&self->mutex);
527         GST_DEBUG_OBJECT (self, "disposed");
528         G_OBJECT_CLASS (parent_class)->dispose (gobject);
529 }