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