summaryrefslogtreecommitdiff
path: root/firmware/target/hosted/maemo/pcm-gstreamer.c
diff options
context:
space:
mode:
authorThomas Jarosch <tomj@simonv.com>2011-02-08 20:05:25 +0000
committerThomas Jarosch <tomj@simonv.com>2011-02-08 20:05:25 +0000
commit5f037ac015e6d76d030a163753db5ff58cdff49b (patch)
treef5eb7dcdc0e0c3e373227e45061c1d99a14a0819 /firmware/target/hosted/maemo/pcm-gstreamer.c
parent4d129044390a087b6193b6ce63e035b2550b3ce4 (diff)
downloadrockbox-5f037ac015e6d76d030a163753db5ff58cdff49b.tar.gz
rockbox-5f037ac015e6d76d030a163753db5ff58cdff49b.zip
Initial maemo platform support
Adds Nokia N900, N810 and N800 support. Features: - Introduce maemo specific platform defines - Play audio in silent mode - Stop playback on incoming calls - Battery level readout - Bluetooth headset support - Save CPU by disabling screen updates if the display is off or the app doesn't have input focus - N900: GStreamer audio backend Kudos to kugel for the code review. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29248 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/hosted/maemo/pcm-gstreamer.c')
-rw-r--r--firmware/target/hosted/maemo/pcm-gstreamer.c459
1 files changed, 459 insertions, 0 deletions
diff --git a/firmware/target/hosted/maemo/pcm-gstreamer.c b/firmware/target/hosted/maemo/pcm-gstreamer.c
new file mode 100644
index 0000000000..e3e40f0619
--- /dev/null
+++ b/firmware/target/hosted/maemo/pcm-gstreamer.c
@@ -0,0 +1,459 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2010 by Thomas Jarosch
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22
23#include "autoconf.h"
24
25#include <stdbool.h>
26#include "config.h"
27#include "debug.h"
28#include "sound.h"
29#include "audiohw.h"
30#include "system.h"
31#include "settings.h"
32#include "debug.h"
33
34#include "playback.h"
35#include "kernel.h"
36
37#include <SDL.h>
38#include <glib.h>
39#include <gst/gst.h>
40#include <gst/app/gstappsrc.h>
41#include <linux/types.h>
42
43/* Maemo5: N900 specific libplayback support */
44#include <libplayback/playback.h>
45#include <dbus/dbus-glib.h>
46#include <dbus/dbus-glib-lowlevel.h>
47#include "maemo-thread.h"
48
49#ifdef HAVE_RECORDING
50#include "audiohw.h"
51#ifdef HAVE_SPDIF_IN
52#include "spdif.h"
53#endif
54#endif
55
56#include "pcm.h"
57#include "pcm_sampr.h"
58
59/*#define LOGF_ENABLE*/
60#include "logf.h"
61
62#ifdef DEBUG
63#include <stdio.h>
64extern bool debug_audio;
65#endif
66
67#if CONFIG_CODEC == SWCODEC
68
69/* Declarations for libplayblack */
70pb_playback_t *playback = NULL;
71void playback_state_req_handler(pb_playback_t *pb,
72 enum pb_state_e req_state,
73 pb_req_t *ext_req,
74 void *data);
75void playback_state_req_callback(pb_playback_t *pb,
76 enum pb_state_e granted_state,
77 const char *reason,
78 pb_req_t *req,
79 void *data);
80bool playback_granted = false;
81
82/* Gstreamer related vars */
83GstCaps *gst_audio_caps = NULL;
84GstElement *gst_pipeline = NULL;
85GstElement *gst_appsrc = NULL;
86GstElement *gst_volume = NULL;
87GstElement *gst_pulsesink = NULL;
88GstBus *gst_bus = NULL;
89static int bus_watch_id = 0;
90GMainLoop *pcm_loop = NULL;
91
92static __u8* pcm_data = NULL;
93static size_t pcm_data_size = 0;
94
95static int inside_feed_data = 0;
96
97void pcm_play_lock(void)
98{
99}
100
101void pcm_play_unlock(void)
102{
103}
104
105void pcm_dma_apply_settings(void)
106{
107}
108
109void pcm_play_dma_start(const void *addr, size_t size)
110{
111 pcm_data = (__u8 *) addr;
112 pcm_data_size = size;
113
114 if (playback_granted)
115 {
116 /* Start playing now */
117 if (!inside_feed_data)
118 gst_element_set_state (GST_ELEMENT(gst_pipeline), GST_STATE_PLAYING);
119 else
120 DEBUGF("ERROR: dma_start called while inside feed_data\n");
121 } else
122 {
123 /* N900: Request change to playing state */
124 pb_playback_req_state (playback,
125 PB_STATE_PLAY,
126 playback_state_req_callback,
127 NULL);
128 }
129}
130
131void pcm_play_dma_stop(void)
132{
133 if (inside_feed_data)
134 g_signal_emit_by_name (gst_appsrc, "end-of-stream", NULL);
135 else
136 gst_element_set_state (GST_ELEMENT(gst_pipeline), GST_STATE_NULL);
137}
138
139void pcm_play_dma_pause(bool pause)
140{
141 if (inside_feed_data)
142 {
143 if (pause)
144 g_signal_emit_by_name (gst_appsrc, "end-of-stream", NULL);
145 else
146 DEBUGF("ERROR: Called dma_pause(0) while inside feed_data\n");
147 } else
148 {
149 if (pause)
150 gst_element_set_state (GST_ELEMENT(gst_pipeline), GST_STATE_NULL);
151 else
152 gst_element_set_state (GST_ELEMENT(gst_pipeline), GST_STATE_PLAYING);
153 }
154}
155
156size_t pcm_get_bytes_waiting(void)
157{
158 return pcm_data_size;
159}
160
161static void feed_data(GstElement * appsrc, guint size_hint, void *unused)
162{
163 (void)size_hint;
164 (void)unused;
165
166 /* Make sure we don't trigger a gst_element_set_state() call
167 from inside gstreamer's stream thread as it will deadlock */
168 inside_feed_data = 1;
169
170 pcm_play_get_more_callback((void **)&pcm_data, &pcm_data_size);
171
172 if (pcm_data_size != 0)
173 {
174 GstBuffer *buffer = gst_buffer_new ();
175 GstFlowReturn ret;
176
177 GST_BUFFER_DATA (buffer) = pcm_data;
178 GST_BUFFER_SIZE (buffer) = pcm_data_size;
179
180 g_signal_emit_by_name (appsrc, "push-buffer", buffer, &ret);
181 gst_buffer_unref (buffer);
182
183 if (ret != 0)
184 DEBUGF("push-buffer error result: %d\n", ret);
185 } else
186 {
187 DEBUGF("feed_data: No Data.\n");
188 g_signal_emit_by_name (appsrc, "end-of-stream", NULL);
189 }
190
191 inside_feed_data = 0;
192}
193
194const void * pcm_play_dma_get_peak_buffer(int *count)
195{
196 uintptr_t addr = (uintptr_t)pcm_data;
197 *count = pcm_data_size / 4;
198 return (void *)((addr + 2) & ~3);
199}
200
201
202static gboolean
203gst_bus_message (GstBus * bus, GstMessage * message, void *unused)
204{
205 (void)bus;
206 (void)unused;
207
208 DEBUGF(" [gst] got BUS message %s\n",
209 gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
210
211 switch (GST_MESSAGE_TYPE (message)) {
212 case GST_MESSAGE_ERROR:
213 {
214 GError *err;
215 gchar *debug;
216 gst_message_parse_error (message, &err, &debug);
217
218 DEBUGF("[gst] Received error: Src: %s, msg: %s\n", GST_MESSAGE_SRC_NAME(message), err->message);
219
220 g_error_free (err);
221 g_free (debug);
222 }
223
224 g_main_loop_quit (pcm_loop);
225 break;
226 case GST_MESSAGE_EOS:
227 gst_element_set_state (GST_ELEMENT(gst_pipeline), GST_STATE_NULL);
228 break;
229 case GST_MESSAGE_STATE_CHANGED:
230 {
231 GstState old_state, new_state;
232
233 gst_message_parse_state_changed (message, &old_state, &new_state, NULL);
234 DEBUGF("[gst] Element %s changed state from %s to %s.\n",
235 GST_MESSAGE_SRC_NAME(message),
236 gst_element_state_get_name (old_state),
237 gst_element_state_get_name (new_state));
238 break;
239 }
240 default:
241 break;
242 }
243
244 return TRUE;
245}
246
247void maemo_configure_appsrc(void)
248{
249 /* Block push-buffer until there is enough room */
250 g_object_set (G_OBJECT(gst_appsrc), "block", TRUE, NULL);
251
252 g_object_set(G_OBJECT(gst_appsrc), "format", GST_FORMAT_BYTES, NULL);
253
254 gst_audio_caps = gst_caps_new_simple("audio/x-raw-int", "width", G_TYPE_INT, (gint)16, "depth", G_TYPE_INT, (gint)16, "channels" ,G_TYPE_INT, (gint)2,
255 "signed",G_TYPE_BOOLEAN,1,
256 "rate",G_TYPE_INT,44100,"endianness",G_TYPE_INT,(gint)1234,NULL);
257
258 g_object_set (G_OBJECT(gst_appsrc), "caps", gst_audio_caps, NULL);
259
260 gst_app_src_set_stream_type(GST_APP_SRC(gst_appsrc),
261 GST_APP_STREAM_TYPE_STREAM);
262
263 /* configure the appsrc, we will push data into the appsrc from the
264 * mainloop. */
265 g_signal_connect (gst_appsrc, "need-data", G_CALLBACK (feed_data), NULL);
266}
267
268/* Init libplayback: Grant access rights to
269 play audio while the phone is in silent mode */
270void maemo_init_libplayback(void)
271{
272 DBusConnection *session_bus_raw = (DBusConnection*)osso_get_dbus_connection(maemo_osso_ctx);
273
274 playback = pb_playback_new_2(session_bus_raw,
275 PB_CLASS_MEDIA,
276 PB_FLAG_AUDIO,
277 PB_STATE_STOP,
278 playback_state_req_handler,
279 NULL);
280
281 pb_playback_set_stream(playback, "Playback Stream");
282}
283
284/**
285 * Gets called by the policy framework if an important
286 * event arrives: Incoming calls etc.
287 */
288void maemo_tell_rockbox_to_stop_audio(void)
289{
290 sim_enter_irq_handler();
291 queue_broadcast(SYS_CALL_INCOMING, 0);
292 sim_exit_irq_handler();
293
294 osso_system_note_infoprint(maemo_osso_ctx, "Stopping rockbox playback", NULL);
295}
296
297void playback_state_req_handler(pb_playback_t *pb,
298 enum pb_state_e req_state,
299 pb_req_t *ext_req,
300 void *data)
301{
302 (void)pb;
303 (void)ext_req;
304 (void)data;
305
306 DEBUGF("External state change request: state: %s, data: %p\n",
307 pb_state_to_string(req_state), data);
308
309 if (req_state == PB_STATE_STOP && playback_granted)
310 {
311 DEBUGF("Stopping playback, might be an incoming call\n");
312
313 playback_granted = false;
314 maemo_tell_rockbox_to_stop_audio();
315 }
316}
317
318/**
319 * Callback for our own state change request.
320 */
321void playback_state_req_callback(pb_playback_t *pb, enum pb_state_e granted_state, const char *reason, pb_req_t *req, void *data)
322{
323 (void)data;
324 (void)reason;
325
326 DEBUGF("State request callback: granted_state: %s, reason: %s\n",
327 pb_state_to_string(granted_state), reason);
328
329 /* We are allowed to play audio */
330 if (granted_state == PB_STATE_PLAY)
331 {
332 DEBUGF("Playback granted. Start playing...\n");
333 playback_granted = true;
334 if (!inside_feed_data)
335 gst_element_set_state (GST_ELEMENT(gst_pipeline), GST_STATE_PLAYING);
336 } else
337 {
338 DEBUGF("Can't start playing. Throwing away play request\n");
339
340 playback_granted = false;
341 maemo_tell_rockbox_to_stop_audio();
342 }
343
344 pb_playback_req_completed(pb, req);
345}
346
347void pcm_play_dma_init(void)
348{
349 maemo_init_libplayback();
350
351 GMainContext *ctx = g_main_loop_get_context(maemo_main_loop);
352 pcm_loop = g_main_loop_new (ctx, true);
353
354 gst_init (NULL, NULL);
355
356 gst_pipeline = gst_pipeline_new ("rockbox");
357
358 gst_appsrc = gst_element_factory_make ("appsrc", NULL);
359 gst_volume = gst_element_factory_make ("volume", NULL);
360 gst_pulsesink = gst_element_factory_make ("pulsesink", NULL);
361
362 /* Connect elements */
363 gst_bin_add_many (GST_BIN (gst_pipeline),
364 gst_appsrc, gst_volume, gst_pulsesink, NULL);
365 gst_element_link_many (gst_appsrc, gst_volume, gst_pulsesink, NULL);
366
367 /* Connect to gstreamer bus of the pipeline */
368 gst_bus = gst_pipeline_get_bus (GST_PIPELINE (gst_pipeline));
369 bus_watch_id = gst_bus_add_watch (gst_bus, (GstBusFunc) gst_bus_message, NULL);
370
371 maemo_configure_appsrc();
372}
373
374void pcm_shutdown_gstreamer(void)
375{
376 /* Try to stop playing */
377 gst_element_set_state (GST_ELEMENT(gst_pipeline), GST_STATE_NULL);
378
379 /* Make sure we are really stopped. This should return almost instantly,
380 so we wait up to ten seconds and just continue otherwise */
381 gst_element_get_state (GST_ELEMENT(gst_pipeline), NULL, NULL, GST_SECOND * 10);
382
383 g_source_remove (bus_watch_id);
384 g_object_unref(gst_bus);
385 gst_bus = NULL;
386
387 gst_object_unref (gst_pipeline);
388 gst_pipeline = NULL;
389
390 /* Shutdown libplayback and gstreamer */
391 pb_playback_destroy (playback);
392 gst_deinit();
393
394 g_main_loop_quit(pcm_loop);
395 g_main_loop_unref (pcm_loop);
396}
397
398void pcm_postinit(void)
399{
400}
401
402void pcm_set_mixer_volume(int volume)
403{
404 /* gstreamer volume range is from 0.00 to 1.00 */
405 gdouble gst_vol = (gdouble)(volume - VOLUME_MIN) / (gdouble)VOLUME_RANGE;
406
407 g_object_set (G_OBJECT(gst_volume), "volume", gst_vol, NULL);
408}
409
410
411#ifdef HAVE_RECORDING
412void pcm_rec_lock(void)
413{
414}
415
416void pcm_rec_unlock(void)
417{
418}
419
420void pcm_rec_dma_init(void)
421{
422}
423
424void pcm_rec_dma_close(void)
425{
426}
427
428void pcm_rec_dma_start(void *start, size_t size)
429{
430 (void)start;
431 (void)size;
432}
433
434void pcm_rec_dma_stop(void)
435{
436}
437
438const void * pcm_rec_dma_get_peak_buffer(void)
439{
440 return NULL;
441}
442
443void audiohw_set_recvol(int left, int right, int type)
444{
445 (void)left;
446 (void)right;
447 (void)type;
448}
449
450#ifdef HAVE_SPDIF_IN
451unsigned long spdif_measure_frequency(void)
452{
453 return 0;
454}
455#endif
456
457#endif /* HAVE_RECORDING */
458
459#endif /* CONFIG_CODEC == SWCODEC */