post LEAVE instead of ENTER stream message when stopping readthread, reformat extra...
[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 GType gst_dreamaudiosource_input_mode_get_type (void)
30 {
31         static volatile gsize input_mode_type = 0;
32         static const GEnumValue input_mode[] = {
33                 {GST_DREAMAUDIOSOURCE_INPUT_MODE_LIVE, "GST_DREAMAUDIOSOURCE_INPUT_MODE_LIVE", "live"},
34                 {GST_DREAMAUDIOSOURCE_INPUT_MODE_HDMI_IN, "GST_DREAMAUDIOSOURCE_INPUT_MODE_HDMI_IN", "hdmi_in"},
35                 {GST_DREAMAUDIOSOURCE_INPUT_MODE_BACKGROUND, "GST_DREAMAUDIOSOURCE_INPUT_MODE_BACKGROUND", "background"},
36                 {0, NULL, NULL},
37         };
38
39         if (g_once_init_enter (&input_mode_type)) {
40                 GType tmp = g_enum_register_static ("GstDreamAudioSourceInputMode", input_mode);
41                 g_once_init_leave (&input_mode_type, tmp);
42         }
43         return (GType) input_mode_type;
44 }
45
46 enum
47 {
48         SIGNAL_GET_DTS_OFFSET,
49         LAST_SIGNAL
50 };
51 enum
52 {
53         ARG_0,
54         ARG_BITRATE,
55         ARG_INPUT_MODE
56 };
57
58 static guint gst_dreamaudiosource_signals[LAST_SIGNAL] = { 0 };
59
60 #define DEFAULT_BITRATE     128
61 #define DEFAULT_SAMPLERATE  48000
62 #define DEFAULT_INPUT_MODE  GST_DREAMAUDIOSOURCE_INPUT_MODE_LIVE
63 #define DEFAULT_BUFFER_SIZE 26
64
65 static GstStaticPadTemplate srctemplate =
66     GST_STATIC_PAD_TEMPLATE ("src",
67         GST_PAD_SRC,
68         GST_PAD_ALWAYS,
69         GST_STATIC_CAPS ("audio/mpeg, "
70         "mpegversion = 4,"
71         "stream-format = (string) adts,"
72         "rate = 48000")
73     );
74
75 #define gst_dreamaudiosource_parent_class parent_class
76 G_DEFINE_TYPE (GstDreamAudioSource, gst_dreamaudiosource, GST_TYPE_PUSH_SRC);
77
78 static GstCaps *gst_dreamaudiosource_getcaps (GstBaseSrc * bsrc, GstCaps * filter);
79 static gboolean gst_dreamaudiosource_unlock (GstBaseSrc * bsrc);
80 static gboolean gst_dreamaudiosource_unlock_stop (GstBaseSrc * bsrc);
81 static gboolean gst_dreamaudiosource_query (GstBaseSrc * bsrc, GstQuery * query);
82
83 static void gst_dreamaudiosource_dispose (GObject * gobject);
84 static GstFlowReturn gst_dreamaudiosource_create (GstPushSrc * psrc, GstBuffer ** outbuf);
85
86 static void gst_dreamaudiosource_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
87 static void gst_dreamaudiosource_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
88
89 static GstStateChangeReturn gst_dreamaudiosource_change_state (GstElement * element, GstStateChange transition);
90 static gint64 gst_dreamaudiosource_get_dts_offset (GstDreamAudioSource *self);
91
92 static gboolean gst_dreamaudiosource_encoder_init (GstDreamAudioSource * self);
93 static void gst_dreamaudiosource_encoder_release (GstDreamAudioSource * self);
94
95 static void gst_dreamaudiosource_read_thread_func (GstDreamAudioSource * self);
96
97 #ifdef PROVIDE_CLOCK
98 static GstClock *gst_dreamaudiosource_provide_clock (GstElement * elem);
99 // static GstClockTime gst_dreamaudiosource_get_encoder_time_ (GstClock * clock, GstBaseSrc * bsrc);
100 #endif
101
102 static void
103 gst_dreamaudiosource_class_init (GstDreamAudioSourceClass * klass)
104 {
105         GObjectClass *gobject_class;
106         GstElementClass *gstelement_class;
107         GstBaseSrcClass *gstbasesrc_class;
108         GstPushSrcClass *gstpush_src_class;
109
110         gobject_class = (GObjectClass *) klass;
111         gstelement_class = (GstElementClass *) klass;
112         gstbasesrc_class = (GstBaseSrcClass *) klass;
113         gstpush_src_class = (GstPushSrcClass *) klass;
114
115         gobject_class->set_property = gst_dreamaudiosource_set_property;
116         gobject_class->get_property = gst_dreamaudiosource_get_property;
117         gobject_class->dispose = gst_dreamaudiosource_dispose;
118
119         gst_element_class_add_pad_template (gstelement_class,
120                                             gst_static_pad_template_get (&srctemplate));
121
122         gst_element_class_set_static_metadata (gstelement_class,
123             "Dream Audio source", "Source/Audio",
124             "Provide an audio elementary stream from Dreambox encoder device",
125             "Andreas Frisch <fraxinas@opendreambox.org>");
126
127         gstelement_class->change_state = gst_dreamaudiosource_change_state;
128
129         gstbasesrc_class->get_caps = gst_dreamaudiosource_getcaps;
130         gstbasesrc_class->unlock = gst_dreamaudiosource_unlock;
131         gstbasesrc_class->unlock_stop = gst_dreamaudiosource_unlock_stop;
132         gstbasesrc_class->query = gst_dreamaudiosource_query;
133
134         gstpush_src_class->create = gst_dreamaudiosource_create;
135
136 #ifdef PROVIDE_CLOCK
137         gstelement_class->provide_clock = GST_DEBUG_FUNCPTR (gst_dreamaudiosource_provide_clock);
138 //      g_type_class_ref (GST_TYPE_SYSTEM_CLOCK);
139 #endif
140
141         g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BITRATE,
142           g_param_spec_int ("bitrate", "Bitrate (kb/s)",
143             "Bitrate in kbit/sec", 16, 320, DEFAULT_BITRATE,
144             G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
145
146         g_object_class_install_property (gobject_class, ARG_INPUT_MODE,
147           g_param_spec_enum ("input-mode", "Input Mode",
148             "Select the input source of the audio stream",
149             GST_TYPE_DREAMAUDIOSOURCE_INPUT_MODE, DEFAULT_INPUT_MODE,
150             G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
151
152         gst_dreamaudiosource_signals[SIGNAL_GET_DTS_OFFSET] =
153                 g_signal_new ("get-dts-offset",
154                 G_TYPE_FROM_CLASS (klass),
155                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
156                 G_STRUCT_OFFSET (GstDreamAudioSourceClass, get_dts_offset),
157                 NULL, NULL, gst_dreamsource_marshal_INT64__VOID, G_TYPE_INT64, 0);
158
159         klass->get_dts_offset = gst_dreamaudiosource_get_dts_offset;
160 }
161
162 static gint64
163 gst_dreamaudiosource_get_dts_offset (GstDreamAudioSource *self)
164 {
165         GST_DEBUG_OBJECT (self, "gst_dreamaudiosource_get_dts_offset %" GST_TIME_FORMAT"", GST_TIME_ARGS (self->dts_offset) );
166         return self->dts_offset;
167 }
168
169 static void gst_dreamaudiosource_set_bitrate (GstDreamAudioSource * self, uint32_t bitrate)
170 {
171         g_mutex_lock (&self->mutex);
172         uint32_t abr = bitrate*1000;
173         if (!self->encoder || !self->encoder->fd)
174         {
175                 self->audio_info.bitrate = bitrate;
176                 g_mutex_unlock (&self->mutex);
177                 return;
178         }
179
180         int ret = ioctl(self->encoder->fd, AENC_SET_BITRATE, &abr);
181         if (ret != 0)
182         {
183                 GST_WARNING_OBJECT (self, "can't set audio bitrate to %i bytes/s!", abr);
184                 g_mutex_unlock (&self->mutex);
185                 return;
186         }
187         GST_INFO_OBJECT (self, "set audio bitrate to %i kBytes/s", bitrate);
188         self->audio_info.bitrate = bitrate;
189         g_mutex_unlock (&self->mutex);
190 }
191
192 void gst_dreamaudiosource_set_input_mode (GstDreamAudioSource *self, GstDreamAudioSourceInputMode mode)
193 {
194         g_return_if_fail (GST_IS_DREAMAUDIOSOURCE (self));
195         GEnumValue *val = g_enum_get_value (G_ENUM_CLASS (g_type_class_ref (GST_TYPE_DREAMAUDIOSOURCE_INPUT_MODE)), mode);
196         if (!val)
197         {
198                 GST_ERROR_OBJECT (self, "no such input_mode %i!", mode);
199                 goto out;
200         }
201         const gchar *value_nick = val->value_nick;
202
203         g_mutex_lock (&self->mutex);
204         if (!self->encoder || !self->encoder->fd)
205         {
206                 self->input_mode = mode;
207                 goto out;
208         }
209         int int_mode = mode;
210         int ret = ioctl(self->encoder->fd, AENC_SET_SOURCE, &int_mode);
211         if (ret != 0)
212         {
213                 GST_WARNING_OBJECT (self, "can't set input mode to %s (%i) error: %s", value_nick, mode, strerror(errno));
214                 goto out;
215         }
216         GST_INFO_OBJECT (self, "successfully set input mode to %s (%i)", value_nick, mode);
217         self->input_mode = mode;
218 out:
219         g_mutex_unlock (&self->mutex);
220         return;
221 }
222
223 GstDreamAudioSourceInputMode gst_dreamaudiosource_get_input_mode (GstDreamAudioSource *self)
224 {
225         GstDreamAudioSourceInputMode result;
226         g_return_val_if_fail (GST_IS_DREAMAUDIOSOURCE (self), -1);
227         GST_OBJECT_LOCK (self);
228         result =self->input_mode;
229         GST_OBJECT_UNLOCK (self);
230         return result;
231 }
232
233 gboolean
234 gst_dreamaudiosource_plugin_init (GstPlugin *plugin)
235 {
236         GST_DEBUG_CATEGORY_INIT (dreamaudiosource_debug, "dreamaudiosource", 0, "dreamaudiosource");
237         return gst_element_register (plugin, "dreamaudiosource", GST_RANK_PRIMARY, GST_TYPE_DREAMAUDIOSOURCE);
238 }
239
240 static void
241 gst_dreamaudiosource_init (GstDreamAudioSource * self)
242 {
243         self->encoder = NULL;
244         self->descriptors_available = 0;
245         self->input_mode = DEFAULT_INPUT_MODE;
246
247         self->buffer_size = DEFAULT_BUFFER_SIZE;
248         g_queue_init (&self->current_frames);
249         self->readthread = NULL;
250
251         g_mutex_init (&self->mutex);
252         g_cond_init (&self->cond);
253         READ_SOCKET (self) = -1;
254         WRITE_SOCKET (self) = -1;
255
256         gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
257         gst_base_src_set_live (GST_BASE_SRC (self), TRUE);
258
259         self->encoder = NULL;
260         self->encoder_clock = NULL;
261
262 #ifdef dump
263         self->dumpfd = open("/media/hdd/movie/dreamaudiosource.dump", O_WRONLY | O_CREAT | O_TRUNC);
264         GST_DEBUG_OBJECT (self, "dumpfd = %i (%s)", self->dumpfd, (self->dumpfd > 0) ? "OK" : strerror(errno));
265 #endif
266 }
267
268 static gboolean gst_dreamaudiosource_encoder_init (GstDreamAudioSource * self)
269 {
270         GST_LOG_OBJECT (self, "initializating encoder...");
271         self->encoder = malloc(sizeof(EncoderInfo));
272
273         if (!self->encoder) {
274                 GST_ERROR_OBJECT (self,"out of space");
275                 return FALSE;
276         }
277
278         char fn_buf[32];
279         sprintf(fn_buf, "/dev/aenc%d", 0);
280         self->encoder->fd = open(fn_buf, O_RDWR | O_SYNC);
281         if (self->encoder->fd <= 0) {
282                 GST_ERROR_OBJECT (self,"cannot open device %s (%s)", fn_buf, strerror(errno));
283                 free(self->encoder);
284                 self->encoder = NULL;
285                 return FALSE;
286         }
287
288         self->encoder->buffer = malloc(ABUFSIZE);
289         if (!self->encoder->buffer) {
290                 GST_ERROR_OBJECT(self,"cannot alloc buffer");
291                 return FALSE;
292         }
293
294         self->encoder->cdb = (unsigned char *)mmap (0, AMMAPSIZE, PROT_READ, MAP_PRIVATE, self->encoder->fd, 0);
295
296         if (!self->encoder->cdb || self->encoder->cdb == MAP_FAILED) {
297                 GST_ERROR_OBJECT(self, "cannot alloc buffer: %s (%i)", strerror(errno), errno);
298                 self->encoder->cdb = NULL;
299                 return FALSE;
300         }
301
302         int control_sock[2];
303         if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
304         {
305                 GST_ERROR_OBJECT(self, "cannot create control sockets: %s (%i)", strerror(errno), errno);
306                 return FALSE;
307         }
308         READ_SOCKET (self) = control_sock[0];
309         WRITE_SOCKET (self) = control_sock[1];
310         fcntl (READ_SOCKET (self), F_SETFL, O_NONBLOCK);
311         fcntl (WRITE_SOCKET (self), F_SETFL, O_NONBLOCK);
312
313         self->memtrack_list = NULL;
314         self->encoder->used_range_min = UINT32_MAX;
315         self->encoder->used_range_max = 0;
316
317         self->audio_info.samplerate = DEFAULT_SAMPLERATE;
318         gst_dreamaudiosource_set_bitrate (self, self->audio_info.bitrate);
319         gst_dreamaudiosource_set_input_mode (self, self->input_mode);
320
321 #ifdef PROVIDE_CLOCK
322         self->encoder_clock = gst_dreamsource_clock_new ("GstDreamAudioSourceClock", self->encoder->fd);
323         GST_DEBUG_OBJECT (self, "self->encoder_clock = %" GST_PTR_FORMAT, self->encoder_clock);
324         GST_OBJECT_FLAG_SET (self, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
325 #endif
326
327         GST_LOG_OBJECT (self, "encoder %s successfully initialized", fn_buf);
328         return TRUE;
329 }
330
331 static void gst_dreamaudiosource_encoder_release (GstDreamAudioSource * self)
332 {
333         GST_LOG_OBJECT (self, "releasing encoder...");
334         if (self->encoder) {
335                 if (self->encoder->buffer)
336                         free(self->encoder->buffer);
337                 if (self->encoder->cdb)
338                         munmap(self->encoder->cdb, AMMAPSIZE);
339                 if (self->encoder->fd)
340                         close(self->encoder->fd);
341                 free(self->encoder);
342         }
343         self->encoder = NULL;
344         close (READ_SOCKET (self));
345         close (WRITE_SOCKET (self));
346         READ_SOCKET (self) = -1;
347         WRITE_SOCKET (self) = -1;
348         if (self->encoder_clock) {
349                 gst_object_unref (self->encoder_clock);
350                 self->encoder_clock = NULL;
351         }
352 }
353
354 static void
355 gst_dreamaudiosource_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
356 {
357         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (object);
358
359         switch (prop_id) {
360                 case ARG_BITRATE:
361                         gst_dreamaudiosource_set_bitrate(self, g_value_get_int (value));
362                         break;
363                 case ARG_INPUT_MODE:
364                              gst_dreamaudiosource_set_input_mode (self, g_value_get_enum (value));
365                         break;
366                 default:
367                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
368                         break;
369         }
370 }
371
372 static void
373 gst_dreamaudiosource_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
374 {
375         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (object);
376
377         switch (prop_id) {
378                 case ARG_BITRATE:
379                         g_value_set_int (value, self->audio_info.bitrate);
380                         break;
381                 case ARG_INPUT_MODE:
382                         g_value_set_enum (value, gst_dreamaudiosource_get_input_mode (self));
383                         break;
384                 default:
385                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
386                         break;
387         }
388 }
389
390 static GstCaps *
391 gst_dreamaudiosource_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
392 {
393         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (bsrc);
394         GstPadTemplate *pad_template;
395         GstCaps *caps;
396
397         pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS(self), "src");
398         g_return_val_if_fail (pad_template != NULL, NULL);
399
400         if (self->encoder == NULL) {
401                 GST_DEBUG_OBJECT (self, "encoder not opened -> use template caps");
402                 caps = gst_pad_template_get_caps (pad_template);
403         }
404         else
405         {
406                 caps = gst_caps_make_writable(gst_pad_template_get_caps (pad_template));
407         }
408
409         GST_DEBUG_OBJECT (self, "return caps %" GST_PTR_FORMAT, caps);
410         return caps;
411 }
412
413 static gboolean gst_dreamaudiosource_query (GstBaseSrc * bsrc, GstQuery * query)
414 {
415         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (bsrc);
416         gboolean ret = TRUE;
417         switch (GST_QUERY_TYPE (query)) {
418                 case GST_QUERY_LATENCY:{
419                         if (self->audio_info.samplerate) {
420                                 GstClockTime min, max;
421
422                                 g_mutex_lock (&self->mutex);
423                                 min = gst_util_uint64_scale_ceil (GST_SECOND, 1000, self->audio_info.samplerate);
424                                 g_mutex_unlock (&self->mutex);
425
426                                 max = self->buffer_size * min;
427
428                                 gst_query_set_latency (query, TRUE, min, max);
429                                 GST_DEBUG_OBJECT (bsrc, "set LATENCY QUERY %" GST_PTR_FORMAT, query);
430                                 ret = TRUE;
431                         } else {
432                                 ret = FALSE;
433                         }
434                         break;
435                 }
436                 default:
437                         ret = GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query);
438                         break;
439         }
440         return ret;
441 }
442
443 static gboolean gst_dreamaudiosource_unlock (GstBaseSrc * bsrc)
444 {
445         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (bsrc);
446         GST_DEBUG_OBJECT (self, "stop creating buffers");
447         g_mutex_lock (&self->mutex);
448         self->flushing = TRUE;
449         GST_DEBUG_OBJECT (self, "set flushing TRUE");
450         g_cond_signal (&self->cond);
451         g_mutex_unlock (&self->mutex);
452         return TRUE;
453 }
454
455 static gboolean gst_dreamaudiosource_unlock_stop (GstBaseSrc * bsrc)
456 {
457         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (bsrc);
458         GST_DEBUG_OBJECT (self, "stop flushing...");
459         g_mutex_lock (&self->mutex);
460         self->flushing = FALSE;
461         g_queue_foreach (&self->current_frames, (GFunc) gst_buffer_unref, NULL);
462         g_queue_clear (&self->current_frames);
463         g_mutex_unlock (&self->mutex);
464         return TRUE;
465 }
466
467 static void gst_dreamaudiosource_free_buffer (struct _buffer_memorytracker * memtrack)
468 {
469         GstDreamAudioSource * self = memtrack->self;
470         GST_OBJECT_LOCK(self);
471         GST_TRACE_OBJECT (self, "freeing %" GST_PTR_FORMAT " uiOffset=%i uiLength=%i used_range_min=%i", memtrack->buffer, memtrack->uiOffset, memtrack->uiLength, self->encoder->used_range_min);
472         GList *list = g_list_first (self->memtrack_list);
473         guint abs_minimum = UINT32_MAX;
474         guint abs_maximum = 0;
475         struct _buffer_memorytracker * mt;
476         int count = 0;
477         self->memtrack_list = g_list_remove(list, memtrack);
478         free(memtrack);
479         if (self->encoder)
480         {
481                 list = g_list_first (self->memtrack_list);
482                 while (list) {
483                         mt = list->data;
484                         if (abs_minimum > 0 && mt->uiOffset < abs_minimum)
485                                 abs_minimum = mt->uiOffset;
486                         if (mt->uiOffset+mt->uiLength > abs_maximum)
487                                 abs_maximum = mt->uiOffset+mt->uiLength;
488                         count++;
489                         list = g_list_next (list);
490                 }
491                 GST_TRACE_OBJECT (self, "new abs_minimum=%i, abs_maximum=%i", abs_minimum, abs_maximum);
492                 self->encoder->used_range_min = abs_minimum;
493                 self->encoder->used_range_max = abs_maximum;
494         }
495         GST_OBJECT_UNLOCK(self);
496 }
497
498 static void gst_dreamaudiosource_read_thread_func (GstDreamAudioSource * self)
499 {
500         EncoderInfo *enc = self->encoder;
501         GstDreamSourceReadthreadState state = READTHREADSTATE_NONE;
502         GstBuffer *readbuf = NULL;
503
504         if (!enc) {
505                 GST_WARNING_OBJECT (self, "encoder device not opened!");
506                 return;
507         }
508
509         GST_DEBUG_OBJECT (self, "enter read thread");
510
511         GstMessage *message;
512         GValue val = { 0 };
513
514         message = gst_message_new_stream_status (GST_OBJECT_CAST (self), GST_STREAM_STATUS_TYPE_ENTER, GST_ELEMENT_CAST (GST_OBJECT_PARENT(self)));
515         g_value_init (&val, GST_TYPE_G_THREAD);
516         g_value_set_boxed (&val, self->readthread);
517         gst_message_set_stream_status_object (message, &val);
518         g_value_unset (&val);
519         GST_DEBUG_OBJECT (self, "posting ENTER stream status");
520         gst_element_post_message (GST_ELEMENT_CAST (self), message);
521         GstClockTime clock_time, base_time;
522         gboolean discont = TRUE;
523
524         while (TRUE) {
525                 {
526                         if (state == READTHREADSTATE_STOP)
527                                 goto stop_running;
528
529                         struct pollfd rfd[2];
530                         int timeout, nfds;
531
532                         rfd[0].fd = READ_SOCKET (self);
533                         rfd[0].events = POLLIN | POLLERR | POLLHUP | POLLPRI;
534                         rfd[1].revents = 0;
535                         rfd[1].events = POLLIN;
536                         nfds = 1;
537                         timeout = 0;
538
539                         if (state <= READTRREADSTATE_PAUSED)
540                                 timeout = 200;
541                         else if (state == READTRREADSTATE_RUNNING && self->descriptors_available == 0)
542                         {
543                                 rfd[1].fd = enc->fd;
544                                 self->descriptors_count = 0;
545                                 timeout = 200;
546                                 nfds = 2;
547                         }
548
549                         int ret = poll(rfd, nfds, timeout);
550
551                         if (G_UNLIKELY (ret == -1))
552                         {
553                                 GST_ERROR_OBJECT (self, "SELECT ERROR!");
554                                 goto stop_running;
555                         }
556                         else if ( ret == 0 && self->descriptors_available == 0 )
557                         {
558                                 g_mutex_lock (&self->mutex);
559                                 gst_clock_get_internal_time(self->encoder_clock);
560                                 if (self->flushing)
561                                 {
562                                         GST_DEBUG_OBJECT (self, "FLUSHING!");
563                                         g_cond_signal (&self->cond);
564                                         g_mutex_unlock (&self->mutex);
565                                         continue;
566                                 }
567                                 g_mutex_unlock (&self->mutex);
568                                 GST_DEBUG_OBJECT (self, "SELECT TIMEOUT");
569                                 //!!! TODO generate valid dummy payload
570                                 discont = TRUE;
571                                 if (self->dts_offset != GST_CLOCK_TIME_NONE)
572                                         readbuf = gst_buffer_new();
573                         }
574                         else if ( rfd[0].revents )
575                         {
576                                 char command;
577                                 READ_COMMAND (self, command, ret);
578                                 switch (command) {
579                                         case CONTROL_STOP:
580                                                 GST_DEBUG_OBJECT (self, "CONTROL_STOP!");
581                                                 state = READTHREADSTATE_STOP;
582                                                 break;
583                                         case CONTROL_PAUSE:
584                                                 GST_DEBUG_OBJECT (self, "CONTROL_PAUSE!");
585                                                 state = READTRREADSTATE_PAUSED;
586                                                 break;
587                                         case CONTROL_RUN:
588                                                 GST_DEBUG_OBJECT (self, "CONTROL_RUN");
589                                                 state = READTRREADSTATE_RUNNING;
590                                                 break;
591                                         default:
592                                                 GST_ERROR_OBJECT (self, "illegal control socket command %c received!", command);
593                                 }
594                                 continue;
595                         }
596                         else if ( G_LIKELY(rfd[1].revents & POLLIN) )
597                         {
598                                 clock_time = gst_clock_get_internal_time (self->encoder_clock);
599                                 base_time = gst_element_get_base_time(GST_ELEMENT(self));
600                                 int rlen = read(enc->fd, enc->buffer, ABUFSIZE);
601                                 if (rlen <= 0 || rlen % ABDSIZE ) {
602                                         if ( errno == 512 )
603                                                 goto stop_running;
604                                         GST_WARNING_OBJECT (self, "read error %s (%i)", strerror(errno), errno);
605                                         goto stop_running;
606                                 }
607                                 self->descriptors_available = rlen / ABDSIZE;
608                                 GST_LOG_OBJECT (self, "encoder buffer was empty, %d descriptors available", self->descriptors_available);
609                         }
610                 }
611
612                 while (self->descriptors_count < self->descriptors_available)
613                 {
614                         GstClockTime encoder_pts = GST_CLOCK_TIME_NONE;
615                         GstClockTime result_pts = GST_CLOCK_TIME_NONE;
616
617                         off_t offset = self->descriptors_count * ABDSIZE;
618                         AudioBufferDescriptor *desc = (AudioBufferDescriptor*)(&enc->buffer[offset]);
619
620                         uint32_t f = desc->stCommon.uiFlags;
621
622                         if (G_UNLIKELY (f & CDB_FLAG_METADATA))
623                         {
624                                 GST_LOG_OBJECT (self, "CDB_FLAG_METADATA... skip outdated packet");
625                                 self->descriptors_count = self->descriptors_available;
626                                 continue;
627                         }
628
629                         GST_LOG_OBJECT (self, "descriptors_count=%d, descriptors_available=%d\tuiOffset=%d, uiLength=%d", self->descriptors_count, self->descriptors_available, desc->stCommon.uiOffset, desc->stCommon.uiLength);
630
631                         if (self->encoder->used_range_min == UINT32_MAX)
632                                 self->encoder->used_range_min = desc->stCommon.uiOffset;
633
634                         if (self->encoder->used_range_max == 0)
635                                 self->encoder->used_range_max = desc->stCommon.uiOffset+desc->stCommon.uiLength;
636
637 //                      if (desc->stCommon.uiOffset < self->encoder->used_range_max && desc->stCommon.uiOffset+desc->stCommon.uiLength > self->encoder->used_range_min)
638 //                      {
639 //                              GST_WARNING_OBJECT (self, "encoder overwrites buffer memory that is still in use! uiOffset=%i uiLength=%i used_range_min=%i used_range_max=%i", desc->stCommon.uiOffset, desc->stCommon.uiLength, self->encoder->used_range_min, self->encoder->used_range_max);
640 //                              self->descriptors_count++;
641 //                              readbuf = gst_buffer_new();
642 //                              continue;
643 //                      }
644
645                         // uiDTS since kernel driver booted
646                         if (f & CDB_FLAG_PTS_VALID)
647                         {
648                                 encoder_pts = MPEGTIME_TO_GSTTIME(desc->stCommon.uiPTS);
649                                 GST_LOG_OBJECT (self, "f & CDB_FLAG_PTS_VALID && encoder's uiPTS=%" GST_TIME_FORMAT"", GST_TIME_ARGS(encoder_pts));
650
651                                 g_mutex_lock (&self->mutex);
652                                 if (G_UNLIKELY (self->dts_offset == GST_CLOCK_TIME_NONE))
653                                 {
654 #if 0 // set to 0 to always wait for audio to become valid, don't rely on video pts
655                                         if (self->dreamvideosrc)
656                                         {
657                                                 guint64 videosource_dts_offset;
658                                                 g_signal_emit_by_name(self->dreamvideosrc, "get-dts-offset", &videosource_dts_offset);
659                                                 if (videosource_dts_offset != GST_CLOCK_TIME_NONE)
660                                                 {
661                                                         GST_DEBUG_OBJECT (self, "use DREAMVIDEOSOURCE's dts_offset=%" GST_TIME_FORMAT "", GST_TIME_ARGS (videosource_dts_offset) );
662                                                         self->dts_offset = videosource_dts_offset;
663                                                 }
664                                         }
665 #endif
666                                         if (self->dts_offset == GST_CLOCK_TIME_NONE)
667                                         {
668                                                 self->dts_offset = encoder_pts;
669                                                 GST_DEBUG_OBJECT (self, "use mpeg stream pts as dts_offset=%" GST_TIME_FORMAT" (%lld)", GST_TIME_ARGS (self->dts_offset), desc->stCommon.uiPTS);
670                                         }
671                                 }
672                                 g_mutex_unlock (&self->mutex);
673                         }
674
675                         if (G_UNLIKELY (self->dts_offset == GST_CLOCK_TIME_NONE))
676                         {
677                                 GST_DEBUG_OBJECT (self, "dts_offset is still unknown, skipping frame...");
678                                 self->descriptors_count++;
679                                 break;
680                         }
681
682                         if (encoder_pts != GST_CLOCK_TIME_NONE)
683                         {
684                                 GstClockTime pts_clock_time = encoder_pts - self->dts_offset;
685                                 GstClockTime internal, external;
686                                 GstClockTime rate_n, rate_d;
687                                 GstClockTimeDiff diff;
688
689                                 gst_clock_get_calibration (self->encoder_clock, &internal, &external, &rate_n, &rate_d);
690
691                                 if (internal > pts_clock_time) {
692                                         diff = internal - pts_clock_time;
693                                         diff = gst_util_uint64_scale (diff, rate_n, rate_d);
694                                         pts_clock_time = external - diff;
695                                 } else {
696                                         diff = pts_clock_time - internal;
697                                         diff = gst_util_uint64_scale (diff, rate_n, rate_d);
698                                         pts_clock_time = external + diff;
699                                 }
700
701                                 if ( pts_clock_time >= base_time )
702                                         result_pts = pts_clock_time - base_time;
703                                 else
704                                         GST_DEBUG_OBJECT (self, "pts_clock_time < base_time, skipping frame...");
705
706 #define extra_timestamp_debug
707 #ifdef extra_timestamp_debug
708                                 GstClockTime my_int_time = gst_clock_get_internal_time(self->encoder_clock);
709                                 GstClockTime pipeline_int_time = GST_CLOCK_TIME_NONE;
710                                 GstClock *elemclk = gst_element_get_clock (GST_ELEMENT (self));
711                                 if (elemclk)
712                                 {
713                                         pipeline_int_time = gst_clock_get_internal_time(elemclk);
714                                         gst_object_unref (elemclk);
715                                 }
716
717                                 GST_LOG_OBJECT (self, "post-calibration\n"
718                                 "  %" GST_TIME_FORMAT " =base_time       %" GST_TIME_FORMAT " =clock_time\n"
719                                 "  %" GST_TIME_FORMAT " =encoder_pts     %" GST_TIME_FORMAT " =pts_clock_time     %" GST_TIME_FORMAT " =result_pts\n"
720                                 "  %" GST_TIME_FORMAT " =internal        %" GST_TIME_FORMAT " =external           %" GST_TIME_FORMAT " =diff                %" PRId64 "/%" PRId64 " =rate\n"
721                                 "  %" GST_TIME_FORMAT " =my_int_time     %" GST_TIME_FORMAT " =pipeline_int_time"
722                                 ,
723                                 GST_TIME_ARGS (base_time), GST_TIME_ARGS (clock_time),
724                                 GST_TIME_ARGS (encoder_pts), GST_TIME_ARGS (pts_clock_time), GST_TIME_ARGS (result_pts),
725                                 GST_TIME_ARGS (internal), GST_TIME_ARGS (external), GST_TIME_ARGS (diff), rate_n, rate_d,
726                                 GST_TIME_ARGS (my_int_time), GST_TIME_ARGS (pipeline_int_time)
727                                 );
728 #endif
729                         }
730
731                         struct _buffer_memorytracker * memtrack = malloc(sizeof(struct _buffer_memorytracker));
732                         if (G_UNLIKELY (!memtrack))
733                         {
734                                 GST_ERROR_OBJECT (self, "can't allocate buffer_memorytracker");
735                                 goto stop_running;
736                         }
737
738                         if (readbuf)
739                         {
740                                 GST_INFO_OBJECT (self, "LAST BUFFER WAS INCOMPLETE... appending");
741                                 GstBuffer *append_buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, enc->cdb, AMMAPSIZE, desc->stCommon.uiOffset, desc->stCommon.uiLength, memtrack, (GDestroyNotify) gst_dreamaudiosource_free_buffer);
742                                 readbuf = gst_buffer_append (readbuf, append_buffer);
743                         }
744                         else
745                         {
746                                 GST_OBJECT_LOCK (self);
747                                 readbuf = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, enc->cdb, AMMAPSIZE, desc->stCommon.uiOffset, desc->stCommon.uiLength, memtrack, (GDestroyNotify) gst_dreamaudiosource_free_buffer);
748                                 if (desc->stCommon.uiLength == 0)
749                                 {
750                                         GST_WARNING_OBJECT (self, "ZERO SIZE BUFFER");
751                                 }
752                                 memtrack->self = self;
753                                 memtrack->buffer = readbuf;
754                                 memtrack->uiOffset = desc->stCommon.uiOffset;
755                                 memtrack->uiLength = desc->stCommon.uiLength;
756                                 self->memtrack_list = g_list_append(self->memtrack_list, memtrack);
757                                 GST_OBJECT_UNLOCK (self);
758                         }
759                         if (result_pts != GST_CLOCK_TIME_NONE)
760                         {
761                                 GST_BUFFER_PTS(readbuf) = result_pts;
762                                 GST_BUFFER_DTS(readbuf) = result_pts;
763                         }
764 #ifdef dump
765                         int wret = write(self->dumpfd, (unsigned char*)(enc->cdb + desc->stCommon.uiOffset), desc->stCommon.uiLength);
766                         GST_LOG_OBJECT (self, "read=%i dumped=%i gst_buffer_get_size=%" G_GSIZE_FORMAT " ", desc->stCommon.uiLength, wret, gst_buffer_get_size (readbuf) );
767 #endif
768                         self->descriptors_count++;
769                         break;
770                 }
771
772                 if (self->descriptors_count == self->descriptors_available)
773                 {
774                         GST_LOG_OBJECT (self, "self->descriptors_count == self->descriptors_available -> release %i consumed descriptors", self->descriptors_count);
775                         if (state == READTHREADSTATE_STOP)
776                                 GST_DEBUG_OBJECT (self, "readthread stopping, don't write to fd anymore!");
777                         /* release consumed descs */
778                         else if (write(enc->fd, &self->descriptors_count, sizeof(self->descriptors_count)) != sizeof(self->descriptors_count)) {
779                                 GST_WARNING_OBJECT (self, "release consumed descs write error!");
780                                 goto stop_running;
781                         }
782                         self->descriptors_available = 0;
783                 }
784
785                 if (readbuf)
786                 {
787                         g_mutex_lock (&self->mutex);
788                         if (gst_buffer_get_size (readbuf) && !self->flushing)
789                         {
790                                 while (g_queue_get_length (&self->current_frames) >= self->buffer_size)
791                                 {
792                                         GstBuffer * oldbuf = g_queue_pop_head (&self->current_frames);
793                                         GST_WARNING_OBJECT (self, "dropping %" GST_PTR_FORMAT " because of queue overflow! buffers count=%i", oldbuf, g_queue_get_length (&self->current_frames));
794                                         gst_buffer_unref(oldbuf);
795                                         GST_BUFFER_FLAG_SET ((GstBuffer *) g_queue_peek_head (&self->current_frames), GST_BUFFER_FLAG_DISCONT);
796                                 }
797                                 if (discont)
798                                 {
799                                         GST_BUFFER_FLAG_SET (readbuf, GST_BUFFER_FLAG_DISCONT);
800                                         discont = FALSE;
801                                 }
802                                 g_queue_push_tail (&self->current_frames, readbuf);
803                                 GST_INFO_OBJECT (self, "read %" GST_PTR_FORMAT " to queue... buffers count=%i", readbuf, g_queue_get_length (&self->current_frames));
804                         }
805                         else
806                         {
807                                 GST_INFO_OBJECT (self, "dropping %" GST_PTR_FORMAT " because %s", readbuf, self->flushing?"FLUSHING":"size = 0");
808                                 gst_buffer_unref(readbuf);
809                         }
810                         g_cond_signal (&self->cond);
811                         g_mutex_unlock (&self->mutex);
812                         readbuf = NULL;
813                 }
814         }
815
816         g_assert_not_reached ();
817         return;
818
819         stop_running:
820         {
821                 g_mutex_unlock (&self->mutex);
822                 g_cond_signal (&self->cond);
823                 GST_DEBUG ("stop running, exit thread");
824                 message = gst_message_new_stream_status (GST_OBJECT_CAST (self), GST_STREAM_STATUS_TYPE_LEAVE, GST_ELEMENT_CAST (GST_OBJECT_PARENT(self)));
825                 g_value_init (&val, GST_TYPE_G_THREAD);
826                 g_value_set_boxed (&val, self->readthread);
827                 gst_message_set_stream_status_object (message, &val);
828                 g_value_unset (&val);
829                 GST_DEBUG_OBJECT (self, "posting LEAVE stream status");
830                 gst_element_post_message (GST_ELEMENT_CAST (self), message);
831                 return;
832         }
833 }
834
835 static GstFlowReturn
836 gst_dreamaudiosource_create (GstPushSrc * psrc, GstBuffer ** outbuf)
837 {
838         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (psrc);
839
840         GST_LOG_OBJECT (self, "new buffer requested. queue has %i buffers", g_queue_get_length (&self->current_frames));
841
842         g_mutex_lock (&self->mutex);
843         while (g_queue_is_empty (&self->current_frames) && !self->flushing)
844         {
845                 GST_INFO_OBJECT (self, "waiting for buffer from encoder");
846                 g_cond_wait (&self->cond, &self->mutex);
847         }
848
849         *outbuf = g_queue_pop_head (&self->current_frames);
850         g_mutex_unlock (&self->mutex);
851
852         if (*outbuf)
853         {
854                 GST_INFO_OBJECT (self, "pushing %" GST_PTR_FORMAT ". queue has %i buffers", *outbuf, g_queue_get_length (&self->current_frames));
855                 return GST_FLOW_OK;
856         }
857         GST_INFO_OBJECT (self, "FLUSHING");
858         return GST_FLOW_FLUSHING;
859 }
860
861 static GstStateChangeReturn gst_dreamaudiosource_change_state (GstElement * element, GstStateChange transition)
862 {
863         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (element);
864         GstStateChangeReturn sret = GST_STATE_CHANGE_SUCCESS;
865         int ret;
866
867         switch (transition) {
868                 case GST_STATE_CHANGE_NULL_TO_READY:
869                 {
870                         if (!gst_dreamaudiosource_encoder_init (self))
871                         {
872                                 GError *err = g_error_new (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ, "Can't initialize encoder device");
873                                 GstMessage *msg = gst_message_new_error (GST_OBJECT (self), err, NULL);
874                                 gst_element_post_message (element, msg);
875                                 g_error_free (err);
876                                 return GST_STATE_CHANGE_FAILURE;
877                         }
878                         GST_DEBUG_OBJECT (self, "GST_STATE_CHANGE_NULL_TO_READY");
879                         break;
880                 }
881                 case GST_STATE_CHANGE_READY_TO_PAUSED:
882                         GST_LOG_OBJECT (self, "GST_STATE_CHANGE_READY_TO_PAUSED");
883                         self->dreamvideosrc = gst_bin_get_by_name_recurse_up(GST_BIN(GST_ELEMENT_PARENT(self)), "dreamvideosource0");
884                         if (self->dreamvideosrc)
885                         {
886                                 gint videobitrate = 0;
887                                 g_object_get (G_OBJECT (self->dreamvideosrc), "bitrate", &videobitrate, NULL);
888                                 gfloat x = videobitrate/100.0;
889                                 GST_DEBUG_OBJECT (self, "bitrate/100.0 = %f", x);
890                                 self->buffer_size = (gint)((-0.0026)*x*x) + (gint)(1.0756*x) + DEFAULT_BUFFER_SIZE; // empirically approximated polynom
891                                 GST_INFO_OBJECT (self, "%" GST_PTR_FORMAT "'s bitrate=%i -> set internal buffer_size to %i", self->dreamvideosrc, videobitrate, self->buffer_size);
892                         }
893                         self->dts_offset = GST_CLOCK_TIME_NONE;
894 #ifdef PROVIDE_CLOCK
895                         gst_element_post_message (element, gst_message_new_clock_provide (GST_OBJECT_CAST (element), self->encoder_clock, TRUE));
896 #endif
897                         self->flushing = TRUE;
898                         self->readthread = g_thread_try_new ("dreamaudiosrc-read", (GThreadFunc) gst_dreamaudiosource_read_thread_func, self, NULL);
899                         GST_DEBUG_OBJECT (self, "started readthread @%p", self->readthread);
900                         break;
901                 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
902                         g_mutex_lock (&self->mutex);
903                         GST_LOG_OBJECT (self, "GST_STATE_CHANGE_PAUSED_TO_PLAYING");
904                         GstClock *pipeline_clock = gst_element_get_clock (GST_ELEMENT (self));
905                         if (pipeline_clock)
906                         {
907                                 if (pipeline_clock != self->encoder_clock)
908                                 {
909                                         gst_clock_set_master (self->encoder_clock, pipeline_clock);
910                                         GST_DEBUG_OBJECT (self, "slaved %" GST_PTR_FORMAT "to pipeline_clock %" GST_PTR_FORMAT "", self->encoder_clock, pipeline_clock);
911                                 }
912                                 else
913                                         GST_DEBUG_OBJECT (self, "encoder_clock is master clock");
914                         }
915                                 else
916                                         GST_WARNING_OBJECT (self, "no pipeline clock!");
917                         ret = ioctl(self->encoder->fd, AENC_START);
918                         if ( ret != 0 )
919                                 goto fail;
920                         self->descriptors_available = 0;
921                         CLEAR_COMMAND (self);
922                         g_mutex_unlock (&self->mutex);
923                         break;
924                 default:
925                         break;
926         }
927
928         if (GST_ELEMENT_CLASS (parent_class)->change_state)
929                 sret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
930
931         switch (transition) {
932                 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
933                         g_mutex_lock (&self->mutex);
934                         SEND_COMMAND (self, CONTROL_RUN);
935                         GST_INFO_OBJECT (self, "started encoder!");
936                         g_mutex_unlock (&self->mutex);
937                         break;
938                 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
939                         g_mutex_lock (&self->mutex);
940                         GST_DEBUG_OBJECT (self, "GST_STATE_CHANGE_PLAYING_TO_PAUSED self->descriptors_count=%i self->descriptors_available=%i", self->descriptors_count, self->descriptors_available);
941                         SEND_COMMAND (self, CONTROL_PAUSE);
942                         if (self->descriptors_count < self->descriptors_available)
943                                 self->descriptors_count = self->descriptors_available;
944                         if (self->descriptors_count)
945                                 write(self->encoder->fd, &self->descriptors_count, sizeof(self->descriptors_count));
946                         ret = ioctl(self->encoder->fd, AENC_STOP);
947                         if ( ret != 0 )
948                                 goto fail;
949 #ifdef PROVIDE_CLOCK
950                         gst_clock_set_master (self->encoder_clock, NULL);
951 #endif
952                         GST_INFO_OBJECT (self, "stopped encoder!");
953                         g_mutex_unlock (&self->mutex);
954                         break;
955                 case GST_STATE_CHANGE_PAUSED_TO_READY:
956                         GST_DEBUG_OBJECT (self,"GST_STATE_CHANGE_PAUSED_TO_READY");
957 #ifdef PROVIDE_CLOCK
958                         gst_element_post_message (element, gst_message_new_clock_lost (GST_OBJECT_CAST (element), self->encoder_clock));
959                         gst_clock_set_calibration (self->encoder_clock, 0, 0, 1, 1);
960 #endif
961                         GST_DEBUG_OBJECT (self, "stopping readthread @%p...", self->readthread);
962                         SEND_COMMAND (self, CONTROL_STOP);
963                         g_thread_join (self->readthread);
964                         if (self->dreamvideosrc)
965                                 gst_object_unref(self->dreamvideosrc);
966                         self->dreamvideosrc = NULL;
967                         break;
968                 case GST_STATE_CHANGE_READY_TO_NULL:
969                         gst_dreamaudiosource_encoder_release (self);
970                         GST_DEBUG_OBJECT (self,"GST_STATE_CHANGE_READY_TO_NULL");
971                         break;
972                 default:
973                         break;
974         }
975
976         return sret;
977 fail:
978         GST_ERROR_OBJECT(self,"can't perform encoder ioctl! error: %s (%i)", strerror(errno), errno);
979         g_mutex_unlock (&self->mutex);
980         return GST_STATE_CHANGE_FAILURE;
981 }
982
983 static void
984 gst_dreamaudiosource_dispose (GObject * gobject)
985 {
986         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (gobject);
987 #ifdef dump
988         close(self->dumpfd);
989 #endif
990         g_list_free(self->memtrack_list);
991         g_mutex_clear (&self->mutex);
992         g_cond_clear (&self->cond);
993         GST_DEBUG_OBJECT (self, "disposed");
994         G_OBJECT_CLASS (parent_class)->dispose (gobject);
995 }
996
997 #ifdef PROVIDE_CLOCK
998 static GstClock *gst_dreamaudiosource_provide_clock (GstElement * element)
999 {
1000         GstDreamAudioSource *self = GST_DREAMAUDIOSOURCE (element);
1001
1002         if (!self->encoder || self->encoder->fd < 0)
1003         {
1004                 GST_DEBUG_OBJECT (self, "encoder device not started, can't provide clock!");
1005                 return NULL;
1006         }
1007
1008         return GST_CLOCK_CAST (gst_object_ref (self->encoder_clock));
1009 }
1010
1011 #endif
1012