From 9c519f325590fcf4dfcfe30798fddb5a102dc389 Mon Sep 17 00:00:00 2001 From: Thierry Bultel Date: Wed, 11 Jul 2018 17:42:22 +0200 Subject: fixed the support for 4a framework Changed the api call for getting an audio role. Mapped the stop/start operations to mute/unmute actions on role verb. No longer uses a gstreamer pipeline for the sound output, since it is 4a-softmixer that performs the read/write loop instead. Change-Id: I91e4f801a0da866c50b2218622e810d7350ab8a9 Signed-off-by: Thierry Bultel --- binding/radio-binding.c | 92 +++++++++++++++++---------- binding/radio_impl_kingfisher.c | 134 +++++++++++++++++++++++++++------------- 2 files changed, 152 insertions(+), 74 deletions(-) (limited to 'binding') diff --git a/binding/radio-binding.c b/binding/radio-binding.c index 3f34150..5f67567 100644 --- a/binding/radio-binding.c +++ b/binding/radio-binding.c @@ -30,8 +30,6 @@ #include "radio_impl_rtlsdr.h" #include "radio_impl_kingfisher.h" -#define RADIO_MODE_NAME_MAXLEN 8 - static radio_impl_ops_t *radio_impl_ops; static struct afb_event freq_event; @@ -103,7 +101,6 @@ static void rds(struct afb_req request) } ret_json = json_object_new_object(); - rds = radio_impl_ops->get_rds_info(); json_object_object_add(ret_json, "rds", json_object_new_string(rds?rds:"")); free(rds); @@ -391,7 +388,6 @@ static void stereo_mode(struct afb_req request) const char *value = afb_req_value(request, "value"); int valid = 0; radio_stereo_mode_t mode; - char mode_name[RADIO_MODE_NAME_MAXLEN]; if(value) { if(!strcasecmp(value, "mono")) { @@ -423,8 +419,8 @@ static void stereo_mode(struct afb_req request) } ret_json = json_object_new_object(); mode = radio_impl_ops->get_stereo_mode(); - snprintf(mode_name, RADIO_MODE_NAME_MAXLEN, "%s", mode == MONO ? "mono" : "stereo"); - json_object_object_add(ret_json, "mode", json_object_new_string(mode_name)); + + json_object_object_add(ret_json, "mode", json_object_new_string(mode == MONO ? "mono" : "stereo")); afb_req_success(request, ret_json, NULL); } @@ -495,32 +491,54 @@ static int init() char *output = NULL; #ifdef HAVE_4A_FRAMEWORK - json_object *response; - + json_object *response = NULL; json_object *jsonData = json_object_new_object(); - json_object_object_add(jsonData, "audio_role", json_object_new_string("Radio")); - json_object_object_add(jsonData, "endpoint_type", json_object_new_string("sink")); - rc = afb_service_call_sync("ahl-4a", "stream_open", jsonData, &response); - if(!rc) { - json_object *valJson = NULL; - json_object *val = NULL; - - rc = json_object_object_get_ex(response, "response", &valJson); - if(rc) { - rc = json_object_object_get_ex(valJson, "device_uri", &val); - if(rc) { - const char *jres_pcm = json_object_get_string(val); - char *p; - if((p = strchr(jres_pcm, ':'))) { - output = strdup(p + 1); - } - } - } - } else { - AFB_ERROR("afb_service_call_sync failed\n"); - return rc; + + json_object_object_add(jsonData, "action", json_object_new_string("open")); + rc = afb_service_call_sync("ahl-4a", "get_roles", NULL, &response); + if (rc < 0) { + AFB_ERROR("Failed to query 4A about roles"); + goto failed; + } + AFB_NOTICE("4A: available roles are '%s'", json_object_get_string(response)); + json_object_put(response); + + rc = afb_service_call_sync("ahl-4a", "radio", jsonData, &response); + if (rc < 0) { + AFB_ERROR("Failed to query 'radio' role to 4A"); + goto failed; + } + + json_object *valJson = NULL; + json_object *val = NULL; + + rc = json_object_object_get_ex(response, "response", &valJson); + if (rc == 0) { + AFB_ERROR("Reply from 4A is missing a 'response' field"); + goto failed_malformed; } -#endif + + rc = json_object_object_get_ex(valJson, "device_uri", &val); + if (rc == 0) { + AFB_ERROR("Reply from 4A is missing a 'device_uri' field"); + goto failed_malformed; + } + + const char *jres_pcm = json_object_get_string(val); + char * p = strchr(jres_pcm, ':'); + + if (p == NULL) { + AFB_ERROR("Unable to parse 'device_uri' value field"); + rc = -1; + goto failed_malformed; + } + + if (asprintf(&output, "hw:%s", p + 1) < 0) { + AFB_ERROR("Insufficient memory"); + rc = -1; + goto failed_malformed; + } +#endif /* HAVE_4A_FRAMEWORK */ // Initialize event structures freq_event = afb_daemon_make_event("frequency"); @@ -534,11 +552,23 @@ static int init() radio_impl_ops = &kf_impl_ops; rc = radio_impl_ops->init(output); } + if (rc != 0) { + AFB_ERROR("No radio device found, exiting"); + goto failed; + } + if(rc == 0) { - printf("%s found\n", radio_impl_ops->name); + AFB_NOTICE("%s found\n", radio_impl_ops->name); radio_impl_ops->set_frequency_callback(freq_callback, NULL); } free(output); + +#ifdef HAVE_4A_FRAMEWORK +failed_malformed: + json_object_put(response); +#endif /* HAVE_4A_FRAMEWORK */ + +failed: return rc; } diff --git a/binding/radio_impl_kingfisher.c b/binding/radio_impl_kingfisher.c index e183c1b..7775d5b 100644 --- a/binding/radio_impl_kingfisher.c +++ b/binding/radio_impl_kingfisher.c @@ -22,7 +22,11 @@ #include #include #include +#include +#ifndef HAVE_4A_FRAMEWORK #include +#endif /* !HAVE_4A_FRAMEWORK */ + #include #include "radio_impl.h" @@ -30,9 +34,15 @@ #define SI_NODE "/sys/firmware/devicetree/base/si468x@0/compatible" #define SI_INIT "/usr/bin/si_init" #define SI_CTL "/usr/bin/si_ctl" +#define SI_CTL_CMDLINE_MAXLEN 128 +#define SI_CTL_OUTPUT_MAXLEN 128 -#define GST_SINK_OPT_LEN 128 -#define GST_PIPELINE_LEN 256 +#ifndef HAVE_4A_FRAMEWORK +#define GST_SINK_OPT_LEN 128 +#define GST_PIPELINE_LEN 256 +// GStreamer state +static GstElement *pipeline; +#endif /* !HAVE_4A_FRAMEWORK */ // Structure to describe FM band plans, all values in Hz. typedef struct { @@ -57,8 +67,8 @@ static int scan_valid_snr_threshold = 128; static int scan_valid_rssi_threshold = 128; static bool scanning = false; -// GStreamer state -static GstElement *pipeline; +// stream state + static bool running; static void (*freq_callback)(uint32_t, void*); @@ -73,10 +83,12 @@ static int kf_init(const char *output) int conf_file_present = 0; struct stat statbuf; char *value_str; - char cmd[128]; + char cmd[SI_CTL_CMDLINE_MAXLEN]; int rc; +#ifndef HAVE_4A_FRAMEWORK char output_sink_opt[GST_SINK_OPT_LEN]; char gst_pipeline_str[GST_PIPELINE_LEN]; +#endif /* !HAVE_4A_FRAMEWORK */ if(present) return 0; @@ -156,7 +168,8 @@ static int kf_init(const char *output) AFB_INFO("Using FM Bandplan: %s", known_fm_band_plans[bandplan].name); current_frequency = kf_get_min_frequency(BAND_FM); - sprintf(cmd, + snprintf(cmd, + sizeof(cmd), "%s /dev/i2c-12 0x65 -b fm -p %s -t %d -u %d -c %d", SI_CTL, known_fm_band_plans[bandplan].name, @@ -169,6 +182,7 @@ static int kf_init(const char *output) return -1; } +#ifndef HAVE_4A_FRAMEWORK // Initialize GStreamer gst_init(NULL, NULL); @@ -185,17 +199,11 @@ static int kf_init(const char *output) // NOTE: If muting without pausing is desired, it can likely be done // by adding a "volume" element to the pipeline before the sink, // and setting the volume to 0 to mute. -#ifdef HAVE_4A_FRAMEWORK - rc = snprintf(gst_pipeline_str, - GST_PIPELINE_LEN, - "alsasrc device=hw:radio ! queue ! audioconvert ! audioresample ! alsasink%s", - output_sink_opt); -#else // Use PulseAudio output for compatibility with audiomanager / module_router rc = snprintf(gst_pipeline_str, - GST_PIPELINE_LEN, - "alsasrc device=hw:radio ! queue ! audioconvert ! audioresample ! pulsesink stream-properties=\"props,media.role=radio\""); -#endif + sizeof(gst_pipeline_str), + "alsasrc device=hw:radio ! queue ! audioconvert ! audioresample ! pulsesink stream-properties=\"props,media.role=radio\""); + if(rc >= GST_PIPELINE_LEN) { AFB_ERROR("pipeline string too long"); return -1; @@ -208,6 +216,7 @@ static int kf_init(const char *output) // Start pipeline in paused state gst_element_set_state(pipeline, GST_STATE_PAUSED); +#endif /* !HAVE_4A_FRAMEWORK */ present = true; return 0; @@ -220,7 +229,7 @@ static uint32_t kf_get_frequency(void) static void kf_set_frequency(uint32_t frequency) { - char cmd[128]; + char cmd[SI_CTL_CMDLINE_MAXLEN]; int rc; if(!present) @@ -234,7 +243,7 @@ static void kf_set_frequency(uint32_t frequency) return; kf_scan_stop(); - sprintf(cmd, "%s /dev/i2c-12 0x65 -c %d", SI_CTL, frequency / 1000); + snprintf(cmd, sizeof(cmd), "%s /dev/i2c-12 0x65 -c %d", SI_CTL, frequency / 1000); rc = system(cmd); if(rc == 0) current_frequency = frequency; @@ -259,14 +268,14 @@ static char * kf_get_rds_info(void) { if (scanning) goto done; - snprintf(cmd, SI_CTL_CMDLINE_MAXLEN, "%s /dev/i2c-12 0x65 -m", SI_CTL); + snprintf(cmd, sizeof(cmd), "%s /dev/i2c-12 0x65 -m", SI_CTL); fp = popen(cmd, "r"); if(fp == NULL) { fprintf(stderr, "Could not run: %s!\n", cmd); goto done; } - // Look for "Name:" in output - while (fgets(line, SI_CTL_OUTPUT_MAXLEN, fp) != NULL) { + /* Look for "Name:" in output */ + while (fgets(line, sizeof(line), fp) != NULL) { char* nS = strstr(line, "Name:"); char * end; @@ -274,15 +283,16 @@ static char * kf_get_rds_info(void) { continue; end = nS+strlen("Name:"); - /* remove the trailing \n */ + /* remove the trailing '\n' */ end[strlen(end)-1] = '\0'; rds = strdup(end); break; } - // Make sure si_ctl has finished + /* Make sure si_ctl has finished */ pclose(fp); + done: return rds; } @@ -336,29 +346,64 @@ static void kf_start(void) if(!present) return; - if(!running) { - // Start pipeline - gst_element_set_state(pipeline, GST_STATE_PLAYING); - running = true; + if (running) + return; + +#ifdef HAVE_4A_FRAMEWORK + int rc; + json_object *response; + + json_object *jsonData = json_object_new_object(); + json_object_object_add(jsonData, "action", json_object_new_string("unmute")); + rc = afb_service_call_sync("ahl-4a", "radio", jsonData, &response); + if (rc == 0) { + AFB_INFO("Muted\n"); + json_object_put(response); + } + else { + AFB_ERROR("afb_service_call_sync failed\n"); } +#else /* !HAVE_4A_FRAMEWORK */ + // Start pipeline + gst_element_set_state(pipeline, GST_STATE_PLAYING); +#endif /* !HAVE_4A_FRAMEWORK */ + running = true; } static void kf_stop(void) { - GstEvent *event; - - if(present && running) { - // Stop pipeline - running = false; - gst_element_set_state(pipeline, GST_STATE_PAUSED); - - // Flush pipeline - // This seems required to avoid stutters on starts after a stop - event = gst_event_new_flush_start(); - gst_element_send_event(GST_ELEMENT(pipeline), event); - event = gst_event_new_flush_stop(TRUE); - gst_element_send_event(GST_ELEMENT(pipeline), event); + if (!present) + return; + + if (!running) + return; + +#ifdef HAVE_4A_FRAMEWORK + int rc; + json_object *response; + + json_object *jsonData = json_object_new_object(); + json_object_object_add(jsonData, "action", json_object_new_string("mute")); + rc = afb_service_call_sync("ahl-4a", "radio", jsonData, &response); + if (rc == 0) { + AFB_INFO("UnMuted\n"); + json_object_put(response); } + else { + AFB_ERROR("afb_service_call_sync failed\n"); + } +#else /* !HAVE_4A_FRAMEWORK */ + gst_element_set_state(pipeline, GST_STATE_PAUSED); + GstEvent *event; + + // Flush pipeline + // This seems required to avoid stutters on starts after a stop + event = gst_event_new_flush_start(); + gst_element_send_event(GST_ELEMENT(pipeline), event); + event = gst_event_new_flush_stop(TRUE); + gst_element_send_event(GST_ELEMENT(pipeline), event); +#endif /* !HAVE_4A_FRAMEWORK */ + running = false; } static void kf_scan_start(radio_scan_direction_t direction, @@ -366,8 +411,8 @@ static void kf_scan_start(radio_scan_direction_t direction, void *data) { int rc; - char cmd[128]; - char line[128]; + char cmd[SI_CTL_CMDLINE_MAXLEN]; + char line[SI_CTL_OUTPUT_MAXLEN]; uint32_t new_frequency = 0; FILE *fp; @@ -378,14 +423,17 @@ static void kf_scan_start(radio_scan_direction_t direction, return; scanning = true; - sprintf(cmd, "%s /dev/i2c-12 0x65 -l %s", SI_CTL, direction == SCAN_FORWARD ? "up" : "down"); + snprintf(cmd, + SI_CTL_CMDLINE_MAXLEN, + "%s /dev/i2c-12 0x65 -l %s", + SI_CTL, direction == SCAN_FORWARD ? "up" : "down"); fp = popen(cmd, "r"); if(fp == NULL) { AFB_ERROR("Could not run: %s!", cmd); return; } // Look for "Frequency:" in output - while(fgets(line, 128, fp) != NULL) { + while(fgets(line, SI_CTL_OUTPUT_MAXLEN, fp) != NULL) { if(strncmp("Frequency:", line, 10) == 0) { new_frequency = atoi(line + 10); //AFB_DEBUG("%s: got new_frequency = %d", __FUNCTION__, new_frequency); -- cgit 1.2.3-korg