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