Fix Copyright holder
[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 void gst_dreamaudiosource_dispose (GObject * gobject);
60 static GstFlowReturn gst_dreamaudiosource_create (GstPushSrc * psrc, GstBuffer ** outbuf);
61
62 static void gst_dreamaudiosource_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
63 static void gst_dreamaudiosource_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
64
65 static GstStateChangeReturn gst_dreamaudiosource_change_state (GstElement * element, GstStateChange transition);
66 static gint64 gst_dreamaudiosource_get_base_pts (GstDreamAudioSource *self);
67
68 static void
69 gst_dreamaudiosource_class_init (GstDreamAudioSourceClass * klass)
70 {
71         GObjectClass *gobject_class;
72         GstElementClass *gstelement_class;
73         GstBaseSrcClass *gstbasesrc_class;
74         GstPushSrcClass *gstpush_src_class;
75
76         gobject_class = (GObjectClass *) klass;
77         gstelement_class = (GstElementClass *) klass;
78         gstbasesrc_class = (GstBaseSrcClass *) klass;
79         gstpush_src_class = (GstPushSrcClass *) klass;
80
81         gobject_class->set_property = gst_dreamaudiosource_set_property;
82         gobject_class->get_property = gst_dreamaudiosource_get_property;
83         gobject_class->dispose = gst_dreamaudiosource_dispose;
84
85         gst_element_class_add_pad_template (gstelement_class,
86                                             gst_static_pad_template_get (&srctemplate));
87
88         gst_element_class_set_static_metadata (gstelement_class,
89             "Dream Audio source", "Source/Audio",
90             "Provide an audio elementary stream from Dreambox encoder device",
91             "Andreas Frisch <fraxinas@opendreambox.org>");
92
93         gstelement_class->change_state = gst_dreamaudiosource_change_state;
94
95         gstbasesrc_class->get_caps = gst_dreamaudiosource_getcaps;
96         gstbasesrc_class->start = gst_dreamaudiosource_start;
97         gstbasesrc_class->stop = gst_dreamaudiosource_stop;
98
99         gstpush_src_class->create = gst_dreamaudiosource_create;
100
101         g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BITRATE,
102           g_param_spec_int ("bitrate", "Bitrate (kb/s)",
103             "Bitrate in kbit/sec", 16, 320, DEFAULT_BITRATE,
104             G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
105
106         gst_dreamaudiosource_signals[SIGNAL_GET_BASE_PTS] =
107                 g_signal_new ("get-base-pts",
108                 G_TYPE_FROM_CLASS (klass),
109                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
110                 G_STRUCT_OFFSET (GstDreamAudioSourceClass, get_base_pts),
111                 NULL, NULL, gst_dreamsource_marshal_INT64__VOID, G_TYPE_INT64, 0);
112
113         klass->get_base_pts = gst_dreamaudiosource_get_base_pts;
114
115 }
116
117 static gint64
118 gst_dreamaudiosource_get_base_pts (GstDreamAudioSource *self)
119 {
120         GST_DEBUG_OBJECT (self, "gst_dreamaudiosource_get_base_pts %" GST_TIME_FORMAT"", GST_TIME_ARGS (self->base_pts) );
121         return self->base_pts;
122 }
123
124 static void gst_dreamaudiosource_set_bitrate (GstDreamAudioSource * self, uint32_t bitrate)
125 {
126         if (!self->encoder || !self->encoder->fd)
127                 return;
128         uint32_t abr = bitrate*1000;
129         int ret = ioctl(self->encoder->fd, AENC_SET_BITRATE, &abr);
130         if (ret != 0)
131         {
132                 GST_WARNING_OBJECT (self, "can't set audio bitrate to %i bytes/s!", abr);
133                 return;
134         }
135         GST_INFO_OBJECT (self, "set audio bitrate to %i kBytes/s", bitrate);
136         self->audio_info.bitrate = abr;
137 }
138
139 gboolean
140 gst_dreamaudiosource_plugin_init (GstPlugin *plugin)
141 {
142         GST_DEBUG_CATEGORY_INIT (dreamaudiosource_debug, "dreamaudiosource", 0, "dreamaudiosource");
143         return gst_element_register (plugin, "dreamaudiosource", GST_RANK_PRIMARY, GST_TYPE_DREAMAUDIOSOURCE);
144 }
145
146 static void
147 gst_dreamaudiosource_init (GstDreamAudioSource * self)
148 {
149         self->encoder = NULL;
150         self->descriptors_available = 0;
151
152         g_mutex_init (&self->mutex);
153
154         gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
155         gst_base_src_set_live (GST_BASE_SRC (self), TRUE);
156
157         self->encoder = malloc(sizeof(EncoderInfo));
158
159         if(!self->encoder) {
160                 GST_ERROR_OBJECT(self,"out of space");
161                 return;
162         }
163
164         char fn_buf[32];
165         sprintf(fn_buf, "/dev/aenc%d", 0);
166         self->encoder->fd = open(fn_buf, O_RDWR | O_SYNC);
167         if(self->encoder->fd <= 0) {
168                 GST_ERROR_OBJECT(self,"cannot open device %s (%s)", fn_buf, strerror(errno));
169                 free(self->encoder);
170                 self->encoder = NULL;
171                 return;
172         }
173
174         self->encoder->buffer = malloc(ABUFSIZE);
175         if(!self->encoder->buffer) {
176                 GST_ERROR_OBJECT(self,"cannot alloc buffer");
177                 return;
178         }
179
180         self->encoder->cdb = (unsigned char *)mmap(0, AMMAPSIZE, PROT_READ, MAP_PRIVATE, self->encoder->fd, 0);
181         if(!self->encoder->cdb || self->encoder->cdb== MAP_FAILED) {
182                 GST_ERROR_OBJECT(self,"cannot mmap cdb: %s (%d)", strerror(errno));
183                 return;
184         }
185
186 #ifdef dump
187         self->dumpfd = open("/media/hdd/movie/dreamaudiosource.dump", O_WRONLY | O_CREAT | O_TRUNC);
188         GST_DEBUG_OBJECT (self, "dumpfd = %i (%s)", self->dumpfd, (self->dumpfd > 0) ? "OK" : strerror(errno));
189 #endif
190
191         gst_dreamaudiosource_set_bitrate(self, DEFAULT_BITRATE);
192 }
193
194 static void
195 gst_dreamaudiosource_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
196 {
197         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (object);
198
199         switch (prop_id) {
200                 case ARG_BITRATE:
201                         gst_dreamaudiosource_set_bitrate(self, g_value_get_int (value));
202                         break;
203                 default:
204                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
205                         break;
206         }
207 }
208
209 static void
210 gst_dreamaudiosource_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
211 {
212         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (object);
213
214         switch (prop_id) {
215                 case ARG_BITRATE:
216                         g_value_set_int (value, self->audio_info.bitrate/1000);
217                         break;
218                 default:
219                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
220                         break;
221         }
222 }
223
224 static GstCaps *
225 gst_dreamaudiosource_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
226 {
227         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (bsrc);
228         GstPadTemplate *pad_template;
229         GstCaps *caps;
230
231         pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS(self), "src");
232         g_return_val_if_fail (pad_template != NULL, NULL);
233
234         if (self->encoder == NULL) {
235                 GST_DEBUG_OBJECT (self, "encoder not opened -> use template caps");
236                 caps = gst_pad_template_get_caps (pad_template);
237         }
238         else
239         {
240                 caps = gst_caps_make_writable(gst_pad_template_get_caps (pad_template));
241         }
242
243         GST_DEBUG_OBJECT (self, "return caps %" GST_PTR_FORMAT, caps);
244         return caps;
245 }
246
247 static void
248 gst_dreamaudiosource_free_buffer (struct _bufferdebug * bufferdebug)
249 {
250         GList *list = g_list_first (bufferdebug->self->buffers_list);
251         int count = 0;
252         while (list) {
253                 GST_TRACE_OBJECT (bufferdebug->self, "buffers_list[%i] = %" GST_PTR_FORMAT "", count, list->data);
254                 count++;
255                 list = g_list_next (list);
256         }
257         bufferdebug->self->buffers_list = g_list_remove(g_list_first (bufferdebug->self->buffers_list), bufferdebug->buffer);
258         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));
259 }
260
261 static GstFlowReturn
262 gst_dreamaudiosource_create (GstPushSrc * psrc, GstBuffer ** outbuf)
263 {
264         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (psrc);
265         EncoderInfo *enc = self->encoder;
266         static int dumpoffset;
267
268         GST_LOG_OBJECT (self, "new buffer requested");
269
270         if (!enc) {
271                 GST_WARNING_OBJECT (self, "encoder device not opened!");
272                 return GST_FLOW_ERROR;
273         }
274
275         while (1)
276         {
277                 *outbuf = NULL;
278
279                 if (self->descriptors_available == 0)
280                 {
281                         self->descriptors_count = 0;
282                         int rlen = read(enc->fd, enc->buffer, ABUFSIZE);
283                         if (rlen <= 0 || rlen % ABDSIZE ) {
284                                 if ( errno == 512 )
285                                         return GST_FLOW_FLUSHING;
286                                 GST_WARNING_OBJECT (self, "read error %s (%i)", strerror(errno), errno);
287                                 return GST_FLOW_ERROR;
288                         }
289                         self->descriptors_available = rlen / ABDSIZE;
290                         GST_LOG_OBJECT (self, "encoder buffer was empty, %d descriptors available", self->descriptors_available);
291                 }
292
293                 while (self->descriptors_count < self->descriptors_available) {
294                         off_t offset = self->descriptors_count * ABDSIZE;
295                         AudioBufferDescriptor *desc = (AudioBufferDescriptor*)(&enc->buffer[offset]);
296
297                         uint32_t f = desc->stCommon.uiFlags;
298
299                         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);
300
301                         if (f & CDB_FLAG_METADATA) {
302                                 GST_LOG_OBJECT (self, "CDB_FLAG_METADATA... skip outdated packet");
303                                 self->descriptors_count = self->descriptors_available;
304                                 continue;
305                         }
306
307                         struct _bufferdebug * bdg = malloc(sizeof(struct _bufferdebug));
308                         *outbuf = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, enc->cdb, AMMAPSIZE, desc->stCommon.uiOffset, desc->stCommon.uiLength, bdg, (GDestroyNotify)gst_dreamaudiosource_free_buffer);
309                         bdg->self = self;
310                         bdg->buffer = *outbuf;
311                         self->buffers_list = g_list_append(self->buffers_list, *outbuf);
312
313                         if (f & CDB_FLAG_PTS_VALID)
314                         {
315                                 if (G_UNLIKELY (self->base_pts == GST_CLOCK_TIME_NONE))
316                                 {
317                                         if (self->dreamvideosrc)
318                                         {
319                                                 g_mutex_lock (&self->mutex);
320                                                 guint64 videosource_base_pts;
321                                                 g_signal_emit_by_name(self->dreamvideosrc, "get-base-pts", &videosource_base_pts);
322                                                 if (videosource_base_pts != GST_CLOCK_TIME_NONE)
323                                                 {
324                                                         GST_DEBUG_OBJECT (self, "use DREAMVIDEOSOURCE's base_pts=%" GST_TIME_FORMAT "", GST_TIME_ARGS (videosource_base_pts) );
325                                                         self->base_pts = videosource_base_pts;
326                                                 }
327                                                 g_mutex_unlock (&self->mutex);
328                                         }
329                                         else
330                                         {
331                                                 self->base_pts = MPEGTIME_TO_GSTTIME(desc->stCommon.uiPTS);
332                                                 GST_DEBUG_OBJECT (self, "use mpeg stream pts as base_pts=%" GST_TIME_FORMAT"", GST_TIME_ARGS (self->base_pts) );
333                                         }
334                                 }
335                                 GstClockTime buffer_time = MPEGTIME_TO_GSTTIME(desc->stCommon.uiPTS);
336                                 if (self->base_pts != GST_CLOCK_TIME_NONE && buffer_time > self->base_pts )
337                                 {
338                                         buffer_time -= self->base_pts;
339                                         GST_BUFFER_PTS(*outbuf) = buffer_time;
340                                         GST_BUFFER_DTS(*outbuf) = buffer_time;
341                                 }
342                                 bdg->buffer_pts = buffer_time;
343                         }
344
345 #ifdef dump
346                         int wret = write(self->dumpfd, (unsigned char*)(enc->cdb + desc->stCommon.uiOffset), desc->stCommon.uiLength);
347                         dumpoffset += wret;
348                         GST_LOG_OBJECT (self, "read %"G_GSIZE_FORMAT" dumped %i total 0x%08X", desc->stCommon.uiLength, wret, dumpoffset );
349 #endif
350                         self->descriptors_count++;
351
352                         break;
353                 }
354
355                 if (self->descriptors_count == self->descriptors_available) {
356                         GST_LOG_OBJECT (self, "self->descriptors_count == self->descriptors_available -> release %i consumed descriptors", self->descriptors_count);
357                         /* release consumed descs */
358                         if (write(enc->fd, &self->descriptors_count, 4) != 4) {
359                                 GST_WARNING_OBJECT (self, "release consumed descs write error!");
360                                 return GST_FLOW_ERROR;
361                         }
362                         self->descriptors_available = 0;
363                 }
364
365                 if (*outbuf)
366                 {
367                         GST_DEBUG_OBJECT (self, "pushing %" GST_PTR_FORMAT "", *outbuf );
368                         return GST_FLOW_OK;
369                 }
370         }
371
372         return GST_FLOW_ERROR;
373 }
374
375 static GstStateChangeReturn gst_dreamaudiosource_change_state (GstElement * element, GstStateChange transition)
376 {
377         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (element);
378         int ret;
379         switch (transition) {
380                 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
381                         GST_LOG_OBJECT (self, "GST_STATE_CHANGE_PAUSED_TO_PLAYING");
382                         self->base_pts = GST_CLOCK_TIME_NONE;
383                         ret = ioctl(self->encoder->fd, AENC_START);
384                         if ( ret != 0 )
385                         {
386                                 GST_ERROR_OBJECT(self,"can't start encoder ioctl!");
387                                 return GST_STATE_CHANGE_FAILURE;
388                         }
389                         self->descriptors_available = 0;
390                         GST_INFO_OBJECT (self, "started encoder!");
391                         break;
392                 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
393                         GST_LOG_OBJECT (self, "GST_STATE_CHANGE_PLAYING_TO_PAUSED self->descriptors_count=%i self->descriptors_available=%i", self->descriptors_count, self->descriptors_available);
394                         GST_OBJECT_LOCK (self);
395                         while (self->descriptors_count < self->descriptors_available)
396                                 GST_LOG_OBJECT (self, "flushing self->descriptors_count=%i", self->descriptors_count++);
397                         if (self->descriptors_count)
398                                 write(self->encoder->fd, &self->descriptors_count, 4);
399                         ret = ioctl(self->encoder->fd, AENC_STOP);
400                         GST_OBJECT_UNLOCK (self);
401                         if ( ret != 0 )
402                         {
403                                 GST_ERROR_OBJECT(self,"can't stop encoder ioctl!");
404                                 return GST_STATE_CHANGE_FAILURE;
405                         }
406                         GST_INFO_OBJECT (self, "stopped encoder!");
407                         break;
408                 default:
409                         break;
410         }
411         if (GST_ELEMENT_CLASS (parent_class)->change_state)
412                 return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
413         return GST_STATE_CHANGE_SUCCESS;
414 }
415
416 static gboolean
417 gst_dreamaudiosource_start (GstBaseSrc * bsrc)
418 {
419         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (bsrc);
420         self->dreamvideosrc = gst_bin_get_by_name_recurse_up(GST_BIN(GST_ELEMENT_PARENT(self)), "dreamvideosource0");
421         GST_DEBUG_OBJECT (self, "started. reference to dreamvideosource=%" GST_PTR_FORMAT"", self->dreamvideosrc);
422         return TRUE;
423 }
424
425 static gboolean
426 gst_dreamaudiosource_stop (GstBaseSrc * bsrc)
427 {
428         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (bsrc);
429         if (self->dreamvideosrc)
430                 gst_object_unref(self->dreamvideosrc);
431         GST_DEBUG_OBJECT (self, "stop");
432         return TRUE;
433 }
434
435 static void
436 gst_dreamaudiosource_dispose (GObject * gobject)
437 {
438         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (gobject);
439         if (self->encoder) {
440                 if (self->encoder->buffer)
441                         free(self->encoder->buffer);
442                 if (self->encoder->cdb)
443                         munmap(self->encoder->cdb, AMMAPSIZE);
444                 if (self->encoder->fd)
445                         close(self->encoder->fd);
446                 free(self->encoder);
447         }
448 #ifdef dump
449         close(self->dumpfd);
450 #endif
451         g_list_free(self->buffers_list);
452         g_mutex_clear (&self->mutex);
453         GST_DEBUG_OBJECT (self, "disposed");
454         G_OBJECT_CLASS (parent_class)->dispose (gobject);
455 }