diff options
64 files changed, 1208 insertions, 4824 deletions
diff --git a/.gitmodules b/.gitmodules index 5d921d3..ef375d5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "afb-utilities"] path = afb-utilities url = https://gerrit.automotivelinux.org/gerrit/apps/app-afb-helpers-submodule +[submodule "controller"] + path = controller + url = https://gerrit.automotivelinux.org/gerrit/p/apps/app-controller-submodule.git @@ -2,5 +2,5 @@ host=gerrit.automotivelinux.org port=29418 project=apps/agl-service-audio-4a -defaultbranch=master +defaultbranch=eel diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f42d5a..3d6fe3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,5 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.5) -# Do not change this file, config is located into conf.d include(${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/config.cmake) diff --git a/afb-utilities b/afb-utilities -Subproject 88307badb86a1252b0286e29ac3c1165bbee680 +Subproject 43ec9716bf83d8a6e5ff15909705cb1adc3c189 diff --git a/ahl-binding/CMakeLists.txt b/ahl-binding/CMakeLists.txt index 0dee304..bd2be0c 100644 --- a/ahl-binding/CMakeLists.txt +++ b/ahl-binding/CMakeLists.txt @@ -20,30 +20,36 @@ PROJECT_TARGET_ADD(audiohighlevel) # Define project Targets - ADD_LIBRARY(${TARGET_NAME} MODULE ahl-binding.c ahl-deviceenum.c ahl-config.c ahl-json.c) + ADD_LIBRARY(${TARGET_NAME} MODULE + config_entry.cpp + role.cpp + interrupt.cpp + ahl-binding.cpp + ) # Binder exposes a unique public entry point SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES - PREFIX "afb-" - LABELS "BINDINGV2" - LINK_FLAGS ${BINDINGS_LINK_FLAG} + PREFIX "afb-" + LABELS "BINDING" + LINK_FLAGS ${BINDINGS_LINK_FLAG} OUTPUT_NAME ${TARGET_NAME} ) - # Change default OPenAPI file - SET_OPENAPI_FILENAME("ahl-apidef") - # Define target includes TARGET_INCLUDE_DIRECTORIES(${TARGET_NAME} PUBLIC ${GLIB_PKG_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../controller/ctl-lib + ${CMAKE_CURRENT_SOURCE_DIR}/../afb-utilities ) # Library dependencies (include updates automatically) # Find package for GLIB does not seem to export TARGET_LINK_LIBRARIES(${TARGET_NAME} - ahl-policy - ahl-utilities - afb-utilities + #ahl-policy + + afb-helpers + ctl-utilities ${GLIB_PKG_LIBRARIES} ${link_libraries} ) diff --git a/ahl-binding/ahl-apidef.h b/ahl-binding/ahl-apidef.h deleted file mode 100644 index a9cb6dd..0000000 --- a/ahl-binding/ahl-apidef.h +++ /dev/null @@ -1,237 +0,0 @@ - -static const char _afb_description_v2_ahl_4a[] = - "{\"openapi\":\"3.0.0\",\"info\":{\"description\":\"Audio high level API " - "for AGL applications\",\"title\":\"audiohighlevel\",\"version\":\"1.0\"," - "\"x-binding-c-generator\":{\"api\":\"ahl-4a\",\"version\":2,\"prefix\":\"" - "audiohlapi_\",\"postfix\":\"\",\"start\":null,\"onevent\":\"AhlOnEvent\"" - ",\"init\":\"AhlBindingInit\",\"scope\":\"\",\"private\":false,\"noconcur" - "rency\":false}},\"servers\":[{\"url\":\"ws://{host}:{port}/api/audiohl\"" - ",\"description\":\"Audio high level API for AGL applications.\",\"variab" - "les\":{\"host\":{\"default\":\"localhost\"},\"port\":{\"default\":\"1234" - "\"}},\"x-afb-events\":[{\"$ref\":\"#/components/schemas/afb-event\"}]}]," - "\"components\":{\"schemas\":{\"afb-reply\":{\"$ref\":\"#/components/sche" - "mas/afb-reply-v2\"},\"afb-event\":{\"$ref\":\"#/components/schemas/afb-e" - "vent-v2\"},\"afb-reply-v2\":{\"title\":\"Generic response.\",\"type\":\"" - "object\",\"required\":[\"jtype\",\"request\"],\"properties\":{\"jtype\":" - "{\"type\":\"string\",\"const\":\"afb-reply\"},\"request\":{\"type\":\"ob" - "ject\",\"required\":[\"status\"],\"properties\":{\"status\":{\"type\":\"" - "string\"},\"info\":{\"type\":\"string\"},\"token\":{\"type\":\"string\"}" - ",\"uuid\":{\"type\":\"string\"},\"reqid\":{\"type\":\"string\"}}},\"resp" - "onse\":{\"type\":\"object\"}}},\"afb-event-v2\":{\"type\":\"object\",\"r" - "equired\":[\"jtype\",\"event\"],\"properties\":{\"jtype\":{\"type\":\"st" - "ring\",\"const\":\"afb-event\"},\"event\":{\"type\":\"string\"},\"data\"" - ":{\"type\":\"object\"}}},\"endpoint_info\":{\"type\":\"object\",\"requir" - "ed\":[\"endpoint_id\",\"type\",\"device_name\",\"device_uri\"],\"propert" - "ies\":{\"endpoint_id\":{\"type\":\"int\"},\"type\":{\"type\":\"enum\"},\"" - "device_name\":{\"type\":\"string\"},\"device_uri_type\":{\"type\":\"stri" - "ng\"}}},\"stream_info\":{\"type\":\"object\",\"required\":[\"stream_id\"" - ",\"state\",\"mute\",\"endpoint_info\"],\"properties\":{\"stream_id\":{\"" - "type\":\"int\"},\"state\":{\"type\":\"int\"},\"mute\":{\"type\":\"int\"}" - ",\"device_uri\":{\"type\":\"string\"},\"$ref\":\"#/components/schemas/en" - "dpoint_info\"}}},\"x-permissions\":{\"streamcontrol\":{\"permission\":\"" - "urn:AGL:permission:audio:public:streamcontrol\"},\"endpointcontrol\":{\"" - "permission\":\"urn:AGL:permission:audio:public:endpointcontrol\"},\"audi" - "ostream\":{\"permission\":\"urn:AGL:permission:audio:public:audiostream\"" - "},\"soundevent\":{\"permission\":\"urn:AGL:permission:audio:public:sound" - "event\"}},\"responses\":{\"200\":{\"description\":\"A complex object arr" - "ay response\",\"content\":{\"application/json\":{\"schema\":{\"$ref\":\"" - "#/components/schemas/afb-reply\"}}}},\"400\":{\"description\":\"Invalid " - "arguments\"}}},\"paths\":{\"/get_endpoints\":{\"description\":\"Retrieve" - " array of available audio endpoints\",\"get\":{\"parameters\":[{\"in\":\"" - "query\",\"name\":\"audio_role\",\"required\":true,\"schema\":{\"type\":\"" - "string\"}},{\"in\":\"query\",\"name\":\"endpoint_type\",\"required\":tru" - "e,\"schema\":{\"type\":\"enum\"}}],\"responses\":{\"200\":{\"$ref\":\"#/" - "components/responses/200\",\"response\":{\"description\":\"Array of endp" - "oint info structures\",\"type\":\"array\",\"items\":{\"$ref\":\"#/compon" - "ents/schemas/endpoint_info\"}}},\"400\":{\"$ref\":\"#/components/respons" - "es/400\"}}}},\"/stream_open\":{\"description\":\"Request opening a strea" - "m\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions/au" - "diostream\"},\"parameters\":[{\"in\":\"query\",\"name\":\"audio_role\",\"" - "required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"na" - "me\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"enum\"}}" - ",{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":false,\"schema\"" - ":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/re" - "sponses/200\",\"response\":{\"description\":\"Stream information structu" - "re\",\"$ref\":\"#/components/schemas/stream_info\"}},\"400\":{\"$ref\":\"" - "#/components/responses/400\"}}}},\"/stream_close\":{\"description\":\"Re" - "quest closing a stream\",\"get\":{\"x-permissions\":{\"$ref\":\"#/compon" - "ents/x-permissions/audiostream\"},\"parameters\":[{\"in\":\"query\",\"na" - "me\":\"stream_id\",\"required\":false,\"schema\":{\"type\":\"int\"}}],\"" - "responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{" - "\"$ref\":\"#/components/responses/400\"}}}},\"/set_stream_state\":{\"des" - "cription\":\"Change stream active and/or mute state\",\"get\":{\"x-permi" - "ssions\":{\"$ref\":\"#/components/x-permissions/streamcontrol\"},\"param" - "eters\":[{\"in\":\"query\",\"name\":\"stream_id\",\"required\":false,\"s" - "chema\":{\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"state\",\"requi" - "red\":false,\"schema\":{\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"" - "mute\",\"required\":false,\"schema\":{\"type\":\"int\"}}],\"responses\":" - "{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$ref\":\"#" - "/components/responses/400\"}}}},\"/get_stream_info\":{\"description\":\"" - "Retrieve stream information\",\"get\":{\"parameters\":[{\"in\":\"query\"" - ",\"name\":\"stream_id\",\"required\":true,\"schema\":{\"type\":\"int\"}}" - "],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\"resp" - "onse\":{\"description\":\"Stream information structure\",\"$ref\":\"#/co" - "mponents/schemas/stream_info\"}},\"400\":{\"$ref\":\"#/components/respon" - "ses/400\"}}}},\"/volume\":{\"description\":\"Set or get volume on endpoi" - "nt\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions/e" - "ndpointcontrol\"},\"parameters\":[{\"in\":\"query\",\"name\":\"endpoint_" - "type\",\"required\":true,\"schema\":{\"type\":\"enum\"}},{\"in\":\"query" - "\",\"name\":\"endpoint_id\",\"required\":true,\"schema\":{\"type\":\"int" - "\"}},{\"in\":\"query\",\"name\":\"volume\",\"required\":false,\"schema\"" - ":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components" - "/responses/200\"},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"" - "/get_endpoint_info\":{\"description\":\"Retrieve endpoint information in" - "cluding its properties\",\"get\":{\"parameters\":[{\"in\":\"query\",\"na" - "me\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"enum\"}}" - ",{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":true,\"schema\"" - ":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/re" - "sponses/200\"},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/p" - "roperty\":{\"description\":\"Set/get endpoint property value\",\"get\":{" - "\"x-permissions\":{\"$ref\":\"#/components/x-permissions/endpointcontrol" - "\"},\"parameters\":[{\"in\":\"query\",\"name\":\"endpoint_type\",\"requi" - "red\":true,\"schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"" - "endpoint_id\",\"required\":true,\"schema\":{\"type\":\"int\"}},{\"in\":\"" - "query\",\"name\":\"property_name\",\"required\":true,\"schema\":{\"type\"" - ":\"string\"}},{\"in\":\"query\",\"name\":\"value\",\"required\":false,\"" - "schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/co" - "mponents/responses/200\"},\"400\":{\"$ref\":\"#/components/responses/400" - "\"}}}},\"/get_list_actions\":{\"description\":\"Retrieve a list of suppo" - "rted actions for a particular audio role\",\"get\":{\"parameters\":[{\"i" - "n\":\"query\",\"name\":\"audio_role\",\"required\":true,\"schema\":{\"ty" - "pe\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/respo" - "nses/200\"},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/post" - "_action\":{\"description\":\"Post sound or audio device related action e" - "vent (extendable mechanism)\",\"get\":{\"x-permissions\":{\"$ref\":\"#/c" - "omponents/x-permissions/soundevent\"},\"parameters\":[{\"in\":\"query\"," - "\"name\":\"action_name\",\"required\":true,\"schema\":{\"type\":\"string" - "\"}},{\"in\":\"query\",\"name\":\"audio_role\",\"required\":true,\"schem" - "a\":{\"type\":\"string\"}},{\"in\":\"query\",\"name\":\"media_name\",\"r" - "equired\":false,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"na" - "me\":\"action_context\",\"required\":false,\"schema\":{\"type\":\"object" - "\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"" - "400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/event_subscription" - "\":{\"description\":\"Subscribe to audio high level events\",\"get\":{\"" - "parameters\":[{\"in\":\"query\",\"name\":\"events\",\"required\":true,\"" - "schema\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}},{\"in\":\"" - "query\",\"name\":\"subscribe\",\"required\":true,\"schema\":{\"type\":\"" - "int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"" - "},\"400\":{\"$ref\":\"#/components/responses/400\"}}}}}}" -; - -static const struct afb_auth _afb_auths_v2_ahl_4a[] = { - { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:audiostream" }, - { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:streamcontrol" }, - { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:endpointcontrol" }, - { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:soundevent" } -}; - - void audiohlapi_get_endpoints(struct afb_req req); - void audiohlapi_stream_open(struct afb_req req); - void audiohlapi_stream_close(struct afb_req req); - void audiohlapi_set_stream_state(struct afb_req req); - void audiohlapi_get_stream_info(struct afb_req req); - void audiohlapi_volume(struct afb_req req); - void audiohlapi_get_endpoint_info(struct afb_req req); - void audiohlapi_property(struct afb_req req); - void audiohlapi_get_list_actions(struct afb_req req); - void audiohlapi_post_action(struct afb_req req); - void audiohlapi_event_subscription(struct afb_req req); - -static const struct afb_verb_v2 _afb_verbs_v2_ahl_4a[] = { - { - .verb = "get_endpoints", - .callback = audiohlapi_get_endpoints, - .auth = NULL, - .info = "Retrieve array of available audio endpoints", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "stream_open", - .callback = audiohlapi_stream_open, - .auth = &_afb_auths_v2_ahl_4a[0], - .info = "Request opening a stream", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "stream_close", - .callback = audiohlapi_stream_close, - .auth = &_afb_auths_v2_ahl_4a[0], - .info = "Request closing a stream", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "set_stream_state", - .callback = audiohlapi_set_stream_state, - .auth = &_afb_auths_v2_ahl_4a[1], - .info = "Change stream active and/or mute state", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "get_stream_info", - .callback = audiohlapi_get_stream_info, - .auth = NULL, - .info = "Retrieve stream information", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "volume", - .callback = audiohlapi_volume, - .auth = &_afb_auths_v2_ahl_4a[2], - .info = "Set or get volume on endpoint", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "get_endpoint_info", - .callback = audiohlapi_get_endpoint_info, - .auth = NULL, - .info = "Retrieve endpoint information including its properties", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "property", - .callback = audiohlapi_property, - .auth = &_afb_auths_v2_ahl_4a[2], - .info = "Set/get endpoint property value", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "get_list_actions", - .callback = audiohlapi_get_list_actions, - .auth = NULL, - .info = "Retrieve a list of supported actions for a particular audio role", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "post_action", - .callback = audiohlapi_post_action, - .auth = &_afb_auths_v2_ahl_4a[3], - .info = "Post sound or audio device related action event (extendable mechanism)", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "event_subscription", - .callback = audiohlapi_event_subscription, - .auth = NULL, - .info = "Subscribe to audio high level events", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = NULL, - .callback = NULL, - .auth = NULL, - .info = NULL, - .session = 0 - } -}; - -const struct afb_binding_v2 afbBindingV2 = { - .api = "ahl-4a", - .specification = _afb_description_v2_ahl_4a, - .info = "Audio high level API for AGL applications", - .verbs = _afb_verbs_v2_ahl_4a, - .preinit = NULL, - .init = AhlBindingInit, - .onevent = AhlOnEvent, - .noconcurrency = 0 -}; - diff --git a/ahl-binding/ahl-apidef.json b/ahl-binding/ahl-apidef.json deleted file mode 100644 index 35ffae6..0000000 --- a/ahl-binding/ahl-apidef.json +++ /dev/null @@ -1,448 +0,0 @@ -{ - "openapi": "3.0.0", - "info": { - "description": "Audio high level API for AGL applications", - "title": "audiohighlevel", - "version": "1.0", - "x-binding-c-generator": { - "api": "ahl-4a", - "version": 2, - "prefix": "audiohlapi_", - "postfix": "", - "start": null, - "onevent": "AhlOnEvent", - "init": "AhlBindingInit", - "scope": "", - "private": false, - "noconcurrency": false - } - }, - "servers": [ - { - "url": "ws://{host}:{port}/api/audiohl", - "description": "Audio high level API for AGL applications.", - "variables": { - "host": { - "default": "localhost" - }, - "port": { - "default": "1234" - } - }, - "x-afb-events": [ - { - "$ref": "#/components/schemas/afb-event" - } - ] - } - ], - "components": { - "schemas": { - "afb-reply": { - "$ref": "#/components/schemas/afb-reply-v2" - }, - "afb-event": { - "$ref": "#/components/schemas/afb-event-v2" - }, - "afb-reply-v2": { - "title": "Generic response.", - "type": "object", - "required": ["jtype", "request"], - "properties": { - "jtype": { - "type": "string", - "const": "afb-reply" - }, - "request": { - "type": "object", - "required": ["status"], - "properties": { - "status": { - "type": "string" - }, - "info": { - "type": "string" - }, - "token": { - "type": "string" - }, - "uuid": { - "type": "string" - }, - "reqid": { - "type": "string" - } - } - }, - "response": { - "type": "object" - } - } - }, - "afb-event-v2": { - "type": "object", - "required": ["jtype", "event"], - "properties": { - "jtype": { - "type": "string", - "const": "afb-event" - }, - "event": { - "type": "string" - }, - "data": { - "type": "object" - } - } - }, - "endpoint_info": { - "type": "object", - "required": [ "endpoint_id", "type", "device_name", "device_uri" ], - "properties": { - "endpoint_id": { "type": "int" }, - "type": { "type": "enum" }, - "device_name": { "type": "string" }, - "device_uri_type": { "type": "string" } - } - }, - "stream_info": { - "type": "object", - "required": [ "stream_id", "state", "mute", "endpoint_info" ], - "properties": { - "stream_id": { "type": "int" }, - "state": { "type": "int" }, - "mute": { "type": "int" }, - "device_uri": { "type": "string" }, - "$ref": "#/components/schemas/endpoint_info" - } - } - }, - "x-permissions": { - "streamcontrol": { "permission": "urn:AGL:permission:audio:public:streamcontrol"}, - "endpointcontrol": { "permission": "urn:AGL:permission:audio:public:endpointcontrol"}, - "audiostream": { "permission": "urn:AGL:permission:audio:public:audiostream"}, - "soundevent": {"permission": "urn:AGL:permission:audio:public:soundevent"} - }, - "responses": { - "200": { - "description": "A complex object array response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/afb-reply" - } - } - } - }, - "400": { "description": "Invalid arguments" } - } - }, - "paths": { - "/get_endpoints": { - "description": "Retrieve array of available audio endpoints", - "get": { - "parameters": [ - { - "in": "query", - "name": "audio_role", - "required": true, - "schema": { "type": "string" } - }, - { - "in": "query", - "name": "endpoint_type", - "required": true, - "schema": { "type": "enum" } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200", - "response": { - "description": "Array of endpoint info structures", - "type": "array", - "items": { "$ref": "#/components/schemas/endpoint_info"} - } - }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/stream_open": { - "description": "Request opening a stream", - "get": { - "x-permissions": { "$ref": "#/components/x-permissions/audiostream" }, - "parameters": [ - { - "in": "query", - "name": "audio_role", - "required": true, - "schema": { "type": "string" } - }, - { - "in": "query", - "name": "endpoint_type", - "required": true, - "schema": { "type": "enum" } - }, - { - "in": "query", - "name": "endpoint_id", - "required": false, - "schema": { "type": "int" } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200", - "response": { - "description": "Stream information structure", - "$ref": "#/components/schemas/stream_info" - } - }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/stream_close": { - "description": "Request closing a stream", - "get": { - "x-permissions": { "$ref": "#/components/x-permissions/audiostream" }, - "parameters": [ - { - "in": "query", - "name": "stream_id", - "required": false, - "schema": { "type": "int" } - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/set_stream_state": { - "description": "Change stream active and/or mute state", - "get": { - "x-permissions": { - "$ref": "#/components/x-permissions/streamcontrol" - }, - "parameters": [ - { - "in": "query", - "name": "stream_id", - "required": false, - "schema": {"type": "int"} - }, - { - "in": "query", - "name": "state", - "required": false, - "schema": {"type": "int"} - }, - { - "in": "query", - "name": "mute", - "required": false, - "schema": {"type": "int"} - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/get_stream_info": { - "description": "Retrieve stream information", - "get": { - "parameters": [ - { - "in": "query", - "name": "stream_id", - "required": true, - "schema": {"type": "int"} - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200", - "response": { - "description": "Stream information structure", - "$ref": "#/components/schemas/stream_info" - } - }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/volume": { - "description": "Set or get volume on endpoint", - "get": { - "x-permissions": { "$ref": "#/components/x-permissions/endpointcontrol" }, - "parameters": [ - { - "in": "query", - "name": "endpoint_type", - "required": true, - "schema": { "type": "enum" } - }, - { - "in": "query", - "name": "endpoint_id", - "required": true, - "schema": { "type": "int" } - }, - { - "in": "query", - "name": "volume", - "required": false, - "schema": { "type": "string" } - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/get_endpoint_info": { - "description": "Retrieve endpoint information including its properties", - "get": { - "parameters": [ - { - "in": "query", - "name": "endpoint_type", - "required": true, - "schema": { "type": "enum" } - }, - { - "in": "query", - "name": "endpoint_id", - "required": true, - "schema": { "type": "int" } - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/property": { - "description": "Set/get endpoint property value", - "get": { - "x-permissions": { "$ref": "#/components/x-permissions/endpointcontrol" }, - "parameters": [ - { - "in": "query", - "name": "endpoint_type", - "required": true, - "schema": { "type": "enum" } - }, - { - "in": "query", - "name": "endpoint_id", - "required": true, - "schema": { "type": "int" } - }, - { - "in": "query", - "name": "property_name", - "required": true, - "schema": { "type": "string" } - }, - { - "in": "query", - "name": "value", - "required": false, - "schema": { "type": "string" } - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/get_list_actions": { - "description": "Retrieve a list of supported actions for a particular audio role", - "get": { - "parameters": [ - { - "in": "query", - "name": "audio_role", - "required": true, - "schema": { "type": "string" } - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/post_action": { - "description": "Post sound or audio device related action event (extendable mechanism)", - "get": { - "x-permissions": { "$ref": "#/components/x-permissions/soundevent" }, - "parameters": [ - { - "in": "query", - "name": "action_name", - "required": true, - "schema": { "type": "string" } - }, - { - "in": "query", - "name": "audio_role", - "required": true, - "schema": { "type": "string" } - }, - { - "in": "query", - "name": "media_name", - "required": false, - "schema": { "type": "string"} - }, - { - "in": "query", - "name": "action_context", - "required": false, - "schema": { "type": "object" } - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/event_subscription": { - "description": "Subscribe to audio high level events", - "get": { - "parameters": [ - { - "in": "query", - "name": "events", - "required": true, - "schema": { "type": "array", - "items": { "type": "string" } - } - }, - { - "in": "query", - "name": "subscribe", - "required": true, - "schema": { "type": "int" } - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - } - } -} diff --git a/ahl-binding/ahl-binding.c b/ahl-binding/ahl-binding.c deleted file mode 100644 index a8b7702..0000000 --- a/ahl-binding/ahl-binding.c +++ /dev/null @@ -1,1276 +0,0 @@ -/* - * Copyright (C) 2017 "Audiokinetic Inc" - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> -#include <string.h> - -#include "ahl-binding.h" -#include "ahl-apidef.h" // Generated from JSON OpenAPI -#include "wrap-json.h" -#include "ahl-policy.h" -#include "ahl-json.h" - -// Global high-level binding context -AHLCtxT g_AHLCtx; - -static EndpointTypeT EndpointTypeToEnum(char * in_pEndpointTypeStr) -{ - if (in_pEndpointTypeStr == NULL) { - return ENDPOINTTYPE_MAXVALUE; - } - else if (strcasecmp(in_pEndpointTypeStr,AHL_ENDPOINTTYPE_SOURCE)==0) { - return ENDPOINTTYPE_SOURCE; - } - else if (strcasecmp(in_pEndpointTypeStr,AHL_ENDPOINTTYPE_SINK)==0) { - return ENDPOINTTYPE_SINK; - } - else - return ENDPOINTTYPE_MAXVALUE; -} - -static StreamStateT StreamStateToEnum(char * in_pStreamStateStr) -{ - if (in_pStreamStateStr == NULL) { - return STREAM_STATE_MAXVALUE; - } - else if (strcasecmp(in_pStreamStateStr,AHL_STREAM_STATE_IDLE)==0) { - return STREAM_STATE_IDLE; - } - else if (strcasecmp(in_pStreamStateStr,AHL_STREAM_STATE_RUNNING)==0) { - return STREAM_STATE_RUNNING; - } - else if (strcasecmp(in_pStreamStateStr,AHL_STREAM_STATE_PAUSED)==0) { - return STREAM_STATE_PAUSED; - } - else - return STREAM_STATE_MAXVALUE; -} - -static streamID_t CreateNewStreamID() -{ - streamID_t newID = g_AHLCtx.nextStreamID; - g_AHLCtx.nextStreamID++; - return newID; -} - -static EndpointInfoT * GetEndpointInfoWithRole(endpointID_t in_endpointID, EndpointTypeT in_endpointType, RoleInfoT * in_pRole) -{ - EndpointInfoT * pEndpointInfo = NULL; - GPtrArray * pDeviceArray = NULL; - if (in_endpointType == ENDPOINTTYPE_SOURCE){ - pDeviceArray = in_pRole->pSourceEndpoints; - } - else { - pDeviceArray = in_pRole->pSinkEndpoints; - } - g_assert_nonnull(pDeviceArray); - - for (int j = 0; j < pDeviceArray->len; j++) { - EndpointInfoT * pCurEndpointInfo = g_ptr_array_index(pDeviceArray,j); - g_assert_nonnull(pCurEndpointInfo); - if (pCurEndpointInfo->endpointID == in_endpointID) { - pEndpointInfo = pCurEndpointInfo; - break; - } - } - - return pEndpointInfo; -} - -static EndpointInfoT * GetEndpointInfo(endpointID_t in_endpointID, EndpointTypeT in_endpointType) -{ - EndpointInfoT * pEndpointInfo = NULL; - - GHashTableIter iter; - gpointer key, value; - g_hash_table_iter_init (&iter, g_AHLCtx.policyCtx.pRoleInfo); - while (pEndpointInfo == NULL && g_hash_table_iter_next (&iter, &key, &value)) - { - RoleInfoT * pRoleInfo = (RoleInfoT*)value; - pEndpointInfo = GetEndpointInfoWithRole(in_endpointID,in_endpointType,pRoleInfo); - } - - return pEndpointInfo; -} - -static StreamInfoT * GetStream(streamID_t in_streamID) -{ - if (g_AHLCtx.policyCtx.pStreams == NULL) - return NULL; - - return g_hash_table_lookup(g_AHLCtx.policyCtx.pStreams,GINT_TO_POINTER(&in_streamID)); -} - -static RoleInfoT * GetRole(char * in_pAudioRoleName) -{ - if (g_AHLCtx.policyCtx.pRoleInfo == NULL) - return NULL; - - return g_hash_table_lookup(g_AHLCtx.policyCtx.pRoleInfo,in_pAudioRoleName); -} - -static int CloseStream(AHLClientCtxT * in_pClientCtx, streamID_t streamID,struct afb_req * pReq) { - StreamInfoT * pStreamInfo = GetStream(streamID); - if (pStreamInfo == NULL) { - AFB_ERROR("Specified stream not currently active stream_id -> %d",streamID); - return AHL_FAIL; - } - -#ifndef AHL_DISCONNECT_POLICY - json_object *pPolicyStreamJ = NULL; - int err = StreamInfoToJSON(pStreamInfo, &pPolicyStreamJ); - if (err == AHL_POLICY_UTIL_FAIL) - { - AFB_ERROR("Audio policy violation, Unable to get JSON object for Policy_CloseStream"); - return AHL_FAIL; - } - int policyAllowed = Policy_CloseStream(pPolicyStreamJ); - if (policyAllowed == AHL_POLICY_REJECT) - { - AFB_ERROR("Close stream not allowed in current context"); - return AHL_FAIL; - } -#endif - // Unsubscribe client from stream events - if (pReq != NULL) { - char streamEventName[128]; - snprintf(streamEventName,128,"ahl_streamstate_%d",streamID); - int iValid = afb_event_is_valid(pStreamInfo->streamStateEvent); - if (iValid) { - err = afb_req_unsubscribe(*pReq,pStreamInfo->streamStateEvent); - if (err) { - AFB_ERROR("Could not unsubscribe state change event for streamID:%i with AudioRole: %s", streamID, pStreamInfo->pRoleName); - return AHL_FAIL; - } - } - } - - // Remove from stream list (if present) - if (g_AHLCtx.policyCtx.pStreams) - g_hash_table_remove(g_AHLCtx.policyCtx.pStreams,GINT_TO_POINTER(&pStreamInfo->streamID)); - free(pStreamInfo); - pStreamInfo = NULL; - - // Find index for cases where there are multiple streams per client - // Remove from client context stream ID and endpoint ID access rights - if (in_pClientCtx->pStreamAccessList) { - for (int i = 0; i < in_pClientCtx->pStreamAccessList->len ; i++) { - streamID_t iID = g_array_index(in_pClientCtx->pStreamAccessList,streamID_t,i); - if (iID == streamID) { - g_array_remove_index(in_pClientCtx->pStreamAccessList, i); - } - } - } - - return AHL_SUCCESS; -} - -static int CloseAllClientStreams(AHLClientCtxT * in_pClientCtx, struct afb_req * pReq) -{ - g_assert_nonnull(in_pClientCtx); - if (in_pClientCtx->pStreamAccessList != NULL) { - while( in_pClientCtx->pStreamAccessList->len ) - { - streamID_t streamID = g_array_index(in_pClientCtx->pStreamAccessList,streamID_t,0); - int err = CloseStream(in_pClientCtx,streamID,pReq); - if (err) { - return err; - } - } - } - - return AHL_SUCCESS; -} - -static AHLClientCtxT * AllocateClientContext() -{ - AHLClientCtxT * pClientCtx = malloc(sizeof(AHLClientCtxT)); - pClientCtx->pStreamAccessList = g_array_new(FALSE, TRUE, sizeof(streamID_t)); - return pClientCtx; -} - -static void TerminateClientContext(void * ptr) -{ - AHLClientCtxT * pClientCtx = (AHLClientCtxT *) ptr; - if (pClientCtx != NULL) { - CloseAllClientStreams(pClientCtx,NULL); - - if (pClientCtx->pStreamAccessList) { - g_array_free( pClientCtx->pStreamAccessList, TRUE); - pClientCtx->pStreamAccessList = NULL; - } - - free(pClientCtx); - } -} - -static int CheckStreamAccessControl(AHLClientCtxT * pClientCtx, streamID_t streamID) -{ - int iAccessControl = AHL_ACCESS_CONTROL_DENIED; - if (pClientCtx && pClientCtx->pStreamAccessList) { - for (int i = 0; i < pClientCtx->pStreamAccessList->len ; i++) { - streamID_t iID = g_array_index(pClientCtx->pStreamAccessList,streamID_t,i); - if (iID == streamID) { - iAccessControl = AHL_ACCESS_CONTROL_GRANTED; - } - } - } - return iAccessControl; -} - -static int CreateEvents() -{ - g_AHLCtx.policyCtx.propertyEvent = afb_daemon_make_event(AHL_ENDPOINT_PROPERTY_EVENT); - int err = !afb_event_is_valid(g_AHLCtx.policyCtx.propertyEvent); - if (err) { - AFB_ERROR("Could not create endpoint property change event"); - return AHL_FAIL; - } - - g_AHLCtx.policyCtx.volumeEvent = afb_daemon_make_event(AHL_ENDPOINT_VOLUME_EVENT); - err = !afb_event_is_valid(g_AHLCtx.policyCtx.volumeEvent); - if (err) { - AFB_ERROR("Could not create endpoint volume change event"); - return AHL_FAIL; - } - - g_AHLCtx.policyCtx.postActionEvent = afb_daemon_make_event(AHL_POST_ACTION_EVENT); - err = !afb_event_is_valid(g_AHLCtx.policyCtx.postActionEvent); - if (err) { - AFB_ERROR("Could not create post action event call event"); - return AHL_FAIL; - } - - return AHL_SUCCESS; -} - -static void AhlBindingTerm() -{ -#ifndef AHL_DISCONNECT_POLICY - // Policy termination - Policy_Term(); -#endif - - // Roles - if (g_AHLCtx.policyCtx.pRoleInfo != NULL) { - GHashTableIter iter; - gpointer key, value; - g_hash_table_iter_init(&iter, g_AHLCtx.policyCtx.pRoleInfo); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - RoleInfoT * pRole = (RoleInfoT *)value; - if (pRole) - { - if(pRole->pRoleName) { - g_free(pRole->pRoleName); - pRole->pRoleName = NULL; - } - // Actions - if (pRole->pActionList) { - for (int i = 0; i < pRole->pActionList->len; i++) - { - g_ptr_array_remove_index( pRole->pActionList, i ); // Free char * is called by GLib - } - } - // Source endpoints - if (pRole->pSourceEndpoints) { - g_ptr_array_unref(pRole->pSourceEndpoints); - } - // Sink endpoints - if (pRole->pSinkEndpoints) { - g_ptr_array_unref(pRole->pSinkEndpoints); - } - free(pRole); - } - } - g_hash_table_remove_all(g_AHLCtx.policyCtx.pRoleInfo); - g_hash_table_destroy(g_AHLCtx.policyCtx.pRoleInfo); - g_AHLCtx.policyCtx.pRoleInfo = NULL; - } - - if (g_AHLCtx.policyCtx.pStreams) { - GHashTableIter iter; - gpointer key, value; - g_hash_table_iter_init (&iter, g_AHLCtx.policyCtx.pStreams); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - if (value) - free(value); - } - g_hash_table_remove_all(g_AHLCtx.policyCtx.pStreams); - g_hash_table_destroy(g_AHLCtx.policyCtx.pStreams); - g_AHLCtx.policyCtx.pStreams = NULL; - } - - if (g_AHLCtx.policyCtx.pHALList) { - g_ptr_array_free(g_AHLCtx.policyCtx.pHALList,TRUE); - g_AHLCtx.policyCtx.pHALList = NULL; - } - - AFB_INFO("Audio high-level binding termination success"); -} - -// Binding initialization -PUBLIC int AhlBindingInit() -{ - memset(&g_AHLCtx,0,sizeof(g_AHLCtx)); - - // Register exit function - atexit(AhlBindingTerm); - - // Create AGL Events - int err = CreateEvents(); - if(err) { - // Error messages already reported inside CreateEvents - return AHL_FAIL; - } - - // Parse high-level binding JSON configuration file (will build device lists) - err = ParseHLBConfig(); - if(err) { - // Error messages already reported inside ParseHLBConfig - return AHL_FAIL; - } - -#ifndef AHL_DISCONNECT_POLICY - // Policy initialization - AFB_DEBUG("Audio high-level policy initialization"); - err = Policy_Init(); - if(err == AHL_POLICY_REJECT) { - //Error messages already reported inside PolicyInit - return AHL_FAIL; - } - - // Call policy for initalization of all source + sink endpoints for all audio Roles - GHashTableIter iter; - gpointer key, value; - g_hash_table_iter_init (&iter, g_AHLCtx.policyCtx.pRoleInfo); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - RoleInfoT * pRoleInfo = (RoleInfoT*)value; - if (pRoleInfo->pSourceEndpoints){ - // for all source endpoints - for (int j = 0; j < pRoleInfo->pSourceEndpoints->len; j++) { - EndpointInfoT * pCurEndpointInfo = g_ptr_array_index(pRoleInfo->pSourceEndpoints,j); - g_assert_nonnull(pCurEndpointInfo); - json_object *pInPolicyEndpointJ = NULL; - err = EndpointInfoToJSON(pCurEndpointInfo, &pInPolicyEndpointJ); - if (err) { - AFB_ERROR("Unable to create source endpoint JSON object from endpoint info for endpoint_id:%d error:%s ",pCurEndpointInfo->endpointID, wrap_json_get_error_string(err)); - return AHL_FAIL; - } - else - { - json_object * pOutPolicyEndpointJ = NULL; - err = Policy_Endpoint_Init(pInPolicyEndpointJ,&pOutPolicyEndpointJ); - if (err == AHL_POLICY_REJECT) { - AFB_WARNING("Policy source endpoint properties initalization failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type); - // continue - } - json_object_put(pInPolicyEndpointJ); - err = UpdateEndpointInfo(pCurEndpointInfo,pOutPolicyEndpointJ); - if (err) { - AFB_ERROR("Policy source endpoint properties update failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type); - return AHL_FAIL; - } - // json_object_put(pOutPolicyEndpointJ); - } - } - } - - if (pRoleInfo->pSinkEndpoints){ - // for all sink endpoints - for (int j = 0; j < pRoleInfo->pSinkEndpoints->len; j++) { - EndpointInfoT * pCurEndpointInfo = g_ptr_array_index(pRoleInfo->pSinkEndpoints,j); - g_assert_nonnull(pCurEndpointInfo); - json_object *pInPolicyEndpointJ = NULL; - err = EndpointInfoToJSON(pCurEndpointInfo, &pInPolicyEndpointJ); - if (err) { - AFB_ERROR("Unable to create sink endpoint JSON object from endpoint info for endpoint_id:%d error:%s ",pCurEndpointInfo->endpointID, wrap_json_get_error_string(err)); - return AHL_FAIL; - } - else - { - json_object *pOutPolicyEndpointJ = NULL; - err = Policy_Endpoint_Init(pInPolicyEndpointJ,&pOutPolicyEndpointJ); - if (err == AHL_POLICY_REJECT) { - AFB_WARNING("Policy sink endpoint properties initalization failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type); - // continue - } - json_object_put(pInPolicyEndpointJ); - err = UpdateEndpointInfo(pCurEndpointInfo,pOutPolicyEndpointJ); - if (err) { - AFB_ERROR("Policy sink endpoint properties update failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type); - return AHL_FAIL; - } - //json_object_put(pOutPolicyEndpointJ); - } - } - } - } -#endif // AHL_DISCONNECT_POLICY - - // Initialize list of active streams - g_AHLCtx.policyCtx.pStreams = g_hash_table_new(g_int_hash, g_int_equal); - if(g_AHLCtx.policyCtx.pStreams == NULL) - { - AFB_ERROR("Unable to create Active Stream List"); - return AHL_FAIL; - } - AFB_DEBUG("Audio high-level binding initialization success"); - return AHL_SUCCESS; -} - -PUBLIC void AhlOnEvent(const char *evtname, json_object *eventJ) -{ - AFB_DEBUG("AHL received event %s", evtname); - - // TODO: Handle event from the policy to update internal information (currently not possible since within the same binding) - -#ifndef AHL_DISCONNECT_POLICY - // Temp: currently forward to policy to handle events (they will be received directly when disconnected into separate binding) - Policy_OnEvent(evtname, eventJ); -#endif -} - -PUBLIC void audiohlapi_get_endpoints(struct afb_req req) -{ - json_object *devicesJ = NULL; - json_object *deviceJ = NULL; - json_object *queryJ = NULL; - char * audioRole = NULL; - char * pEndpointTypeStr = NULL; - EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:s,s:s}", "audio_role", &audioRole,"endpoint_type",&pEndpointTypeStr); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - endpointType = EndpointTypeToEnum(pEndpointTypeStr); - - RoleInfoT * pRole = GetRole(audioRole); - if ( pRole == NULL ) - { - afb_req_fail_f(req, "Invalid arguments", "Requested audio role:%s does not exist in current configuration -> %s", audioRole, json_object_get_string(queryJ)); - return; - } - else - { - devicesJ = json_object_new_array(); - GPtrArray * pDeviceArray = NULL; - if (endpointType == ENDPOINTTYPE_SOURCE) - pDeviceArray = pRole->pSourceEndpoints; - else - pDeviceArray = pRole->pSinkEndpoints; - if (pDeviceArray) { - int iNumberDevices = pDeviceArray->len; - for ( int j = 0 ; j < iNumberDevices; j++) - { - EndpointInfoT * pEndpointInfo = g_ptr_array_index(pDeviceArray,j); - if (pEndpointInfo) { - JSONPublicPackageEndpoint(pEndpointInfo,&deviceJ); - json_object_array_add(devicesJ, deviceJ); - } - } - } - } - - afb_req_success(req, devicesJ, "List of endpoints"); -} - -PUBLIC void audiohlapi_stream_open(struct afb_req req) -{ - json_object *streamInfoJ = NULL; - StreamInfoT * pStreamInfo = NULL; - json_object *queryJ = NULL; - char * audioRole = NULL; - char * endpointTypeStr = NULL; - EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; - endpointID_t endpointID = AHL_UNDEFINED; - EndpointInfoT * pEndpointInfo = NULL; - EndpointSelectionModeT endpointSelMode = ENDPOINTSELMODEMAXVALUE; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:s,s:s,s?i}", "audio_role", &audioRole, "endpoint_type", &endpointTypeStr, "endpoint_id", &endpointID); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - endpointType = EndpointTypeToEnum(endpointTypeStr); - - // Check if there is already an existing context for this client - AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure - if (pClientCtx == NULL) - { - pClientCtx = AllocateClientContext(); - afb_req_context_set(req, pClientCtx, TerminateClientContext); - } - - RoleInfoT * pRole = GetRole(audioRole); - if ( pRole == NULL ) - { - afb_req_fail_f(req, "Invalid audio role", "Audio role was not found in configuration -> %s",audioRole); - return; - } - - GPtrArray * pDeviceArray = NULL; - if (endpointType == ENDPOINTTYPE_SOURCE){ - pDeviceArray = pRole->pSourceEndpoints; - } - else{ - pDeviceArray = pRole->pSinkEndpoints; - } - if (pDeviceArray == NULL || pDeviceArray->len == 0) { - afb_req_fail_f(req, "No available devices", "No available devices for role:%s and device type:%s",audioRole,endpointTypeStr); - return; - } - - if (endpointID == AHL_UNDEFINED) - { - // Assign a device based on configuration priority (first in the list for requested role and endpoint type) - pEndpointInfo = g_ptr_array_index(pDeviceArray,0); - endpointSelMode = ENDPOINTSELMODE_AUTO; - } - else{ - endpointSelMode = ENDPOINTSELMODE_MANUAL; - // Find specified endpoint ID in list of devices - int iNumberDevices = pDeviceArray->len; - for ( int j = 0 ; j < iNumberDevices; j++) - { - pEndpointInfo = g_ptr_array_index(pDeviceArray,j); - if (pEndpointInfo && pEndpointInfo->endpointID == endpointID) { - break; - } - pEndpointInfo = NULL; - } - } - - if (pEndpointInfo == NULL) { - afb_req_fail_f(req, "Endpoint not available", "Specified endpoint not available for role:%s and device type:%d endpoint id %d",audioRole,endpointType,endpointID); - return; - } - - pStreamInfo = (StreamInfoT*) malloc(sizeof(StreamInfoT)); - memset(pStreamInfo,0,sizeof(StreamInfoT)); - - // Create stream - pStreamInfo->streamID = CreateNewStreamID(); // create new ID - pStreamInfo->streamState = STREAM_STATE_IDLE; - pStreamInfo->streamMute = STREAM_UNMUTED; - pStreamInfo->pEndpointInfo = pEndpointInfo; - pStreamInfo->endpointSelMode = endpointSelMode; - // Directly from role config for now, but could be programmatically overriden in the future - pStreamInfo->pRoleName = pRole->pRoleName; - pStreamInfo->iPriority = pRole->iPriority; - pStreamInfo->eInterruptBehavior = pRole->eInterruptBehavior; - -#ifndef AHL_DISCONNECT_POLICY - // Call policy to verify whether creating a new audio stream is allowed in current context and possibly take other actions - json_object *pPolicyStreamJ = NULL; - err = StreamInfoToJSON(pStreamInfo,&pPolicyStreamJ); - if (err) - { - afb_req_fail(req, "Cannot convert stream info to JSON", "Unable to get JSON object for Policy_OpenStream"); - return; - } - - int policyAllowed = Policy_OpenStream(pPolicyStreamJ); - if (policyAllowed == AHL_POLICY_REJECT) - { - afb_req_fail_f(req, "Audio policy violation", "Open stream for role: %s on endpoint_id:%d not allowed in current context",pRole->pRoleName,pEndpointInfo->endpointID); - return; - } -#endif - - char streamEventName[128]; - snprintf(streamEventName,128,"ahl_streamstate_%d",pStreamInfo->streamID); - - pStreamInfo->streamStateEvent = afb_daemon_make_event(streamEventName); - err = !afb_event_is_valid(pStreamInfo->streamStateEvent); - if (err) { - afb_req_fail_f(req, "Stream event creation failure", "Could not create stream specific state change event: %s",streamEventName); - return; - } - - err = afb_req_subscribe(req,pStreamInfo->streamStateEvent); - if (err) { - afb_req_fail_f(req, "Stream event subscription failure", "Could not subscribe to stream specific state change event for event name: %s",streamEventName); - return; - } - - // Add to client context stream ID access rights - g_array_append_val(pClientCtx->pStreamAccessList, pStreamInfo->streamID); - - // Push stream on active stream list - if (g_AHLCtx.policyCtx.pStreams) - g_hash_table_insert( g_AHLCtx.policyCtx.pStreams, GINT_TO_POINTER(&pStreamInfo->streamID), pStreamInfo ); - - // Package and return stream information to client - JSONPublicPackageStream(pStreamInfo,&streamInfoJ); - - afb_req_success(req, streamInfoJ, "Stream info structure"); -} - -PUBLIC void audiohlapi_stream_close(struct afb_req req) -{ - json_object *queryJ = NULL; - streamID_t streamID = AHL_UNDEFINED; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s?i}", "stream_id", &streamID); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - - // Check if there is already an existing context for this client - AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure - if (pClientCtx == NULL) - { - afb_req_fail(req, "Missing client info context", "No client context associated with the request (is there an opened stream by this client?)"); - return; - } - - if (streamID == AHL_UNDEFINED) { - AFB_DEBUG("StreamID not specified. Closing all stream for client"); - err = CloseAllClientStreams(pClientCtx,&req); - if (err) { - afb_req_fail(req, "Error closing streams", "Streams cannot close"); - return; - } - } - else { - AFB_DEBUG("Closing streamID:%d",streamID); - err = CloseStream(pClientCtx,streamID,&req); - if (err) { - afb_req_fail_f(req, "Error closing stream", "Specified stream cannot close stream_id -> %d",streamID); - return; - } - } - - afb_req_success(req, NULL, "Stream close completed"); -} - -static int SetStreamState(AHLClientCtxT * in_pClientCtx,struct afb_req * pReq, streamID_t streamID, char * pStreamStateStr, int iMuteValue) { - - StreamInfoT * pStreamInfo = GetStream(streamID); - if (pStreamInfo == NULL) { - afb_req_fail_f(*pReq, "Stream not found", "Specified stream not found stream_id -> %d",streamID); - return AHL_FAIL; - } - - // Verify that this client can control the stream - int iStreamAccessControl = CheckStreamAccessControl( in_pClientCtx, streamID ); - if (iStreamAccessControl == AHL_ACCESS_CONTROL_DENIED) - { - afb_req_fail_f(*pReq, "Access control denied", "Current client is not the owner of streamID:%d", streamID); - return AHL_FAIL; - } - - if (pStreamStateStr != NULL) { - StreamStateT streamState = StreamStateToEnum(pStreamStateStr); -#ifndef AHL_DISCONNECT_POLICY - json_object *pPolicyStreamJ = NULL; - int err = StreamInfoToJSON(pStreamInfo, &pPolicyStreamJ); - if (err == AHL_POLICY_UTIL_FAIL) - { - afb_req_fail_f(*pReq, "JSON packaging error", "Unable to create stream JSON object for Policy_SetStreamState for streamID:%d",streamID); - return AHL_FAIL; - } - - json_object *paramJ= json_object_new_int(streamState); - json_object_object_add(pPolicyStreamJ, "arg_stream_state", paramJ); - - int policyAllowed = Policy_SetStreamState(pPolicyStreamJ); - if (policyAllowed == AHL_POLICY_REJECT) - { - afb_req_fail_f(*pReq, "Audio policy violation", "Change stream state not allowed by policy in current context for streamID:%d",streamID); - return AHL_FAIL; - } -#else - // Simulate that policy returns target state (accepted) - pStreamInfo->streamState = streamState; -#endif - } - -#ifndef AHL_DISCONNECT_POLICY - json_object *pPolicyStreamJ = NULL; - int err = StreamInfoToJSON(pStreamInfo, &pPolicyStreamJ); - if (err == AHL_POLICY_UTIL_FAIL) - { - afb_req_fail_f((*pReq), "JSON packaging error", "Unable to create stream JSON object for Policy_SetStreamMute for streamID:%d",streamID); - return AHL_FAIL; - } - - json_object *paramJ= json_object_new_int(iMuteValue); - json_object_object_add(pPolicyStreamJ, "mute_state", paramJ); - - int policyAllowed = Policy_SetStreamMute(pPolicyStreamJ); - if (policyAllowed == AHL_POLICY_REJECT) - { - afb_req_fail_f(*pReq, "Audio policy violation", "Mute stream not allowed by policy in current context for streamID:%d",streamID); - return AHL_FAIL; - } -#else - // Simulate that policy returns target state (accepted) - pStreamInfo->streamMute = (StreamMuteT)(*piMuteValue); -#endif - - return AHL_SUCCESS; -} - - PUBLIC void audiohlapi_set_stream_state(struct afb_req req) - { - json_object *queryJ = NULL; - streamID_t streamID = AHL_UNDEFINED; - char * streamStateStr = NULL; - int iMuteValue = 0; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s?i,s?s,s?i}", "stream_id", &streamID,"state",&streamStateStr,"mute",&iMuteValue); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - - // Check if there is already an existing context for this client - AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure - if (pClientCtx == NULL) - { - afb_req_fail(req, "Missing client info context", "No client context associated with the request (is there an opened stream by this client?)"); - return; - } - - if (streamID == AHL_UNDEFINED) { - // All stream for this client - if (pClientCtx->pStreamAccessList != NULL) { - for (int i = 0; i < pClientCtx->pStreamAccessList->len; i++) - { - streamID_t curStreamID = g_array_index(pClientCtx->pStreamAccessList,streamID_t,i); - err = SetStreamState(pClientCtx,&req,curStreamID,streamStateStr,iMuteValue); - if (err) { - // Error displayed within SetStreamState - return; - } - } - } - } - else { - err = SetStreamState(pClientCtx,&req,streamID,streamStateStr,iMuteValue); - if (err) { - // Error displayed within SetStreamState - return; - } - } - - afb_req_success(req, NULL, "Set stream state"); - } - - PUBLIC void audiohlapi_get_stream_info(struct afb_req req) - { - json_object *queryJ = NULL; - streamID_t streamID = AHL_UNDEFINED; - json_object * streamInfoJ = NULL; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:i}", "stream_id", &streamID); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - - StreamInfoT * pStreamInfo = GetStream(streamID); - if (pStreamInfo == NULL) { - afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID); - return; - } - - JSONPublicPackageStream(pStreamInfo,&streamInfoJ); - - afb_req_success(req, streamInfoJ, "Get stream info completed"); - } - -PUBLIC void audiohlapi_volume(struct afb_req req) -{ - json_object *queryJ = NULL; - endpointID_t endpointID = AHL_UNDEFINED; - char * pEndpointTypeStr = NULL; - EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; - char * volumeStr = NULL; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:s,s:i,s?s}", "endpoint_type", &pEndpointTypeStr,"endpoint_id",&endpointID,"volume",&volumeStr); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - endpointType = EndpointTypeToEnum(pEndpointTypeStr); - - EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType); - if (pEndpointInfo == NULL) - { - afb_req_fail_f(req, "Endpoint not found", "Endpoint information not found for endpoint_id:%d type:%d",endpointID,endpointType); - return; - } - - if (volumeStr != NULL) { -#ifndef AHL_DISCONNECT_POLICY - json_object *pPolicyEndpointJ = NULL; - err = EndpointInfoToJSON(pEndpointInfo, &pPolicyEndpointJ); - if (err == AHL_POLICY_UTIL_FAIL) - { - afb_req_fail_f(req, "JSON packaging error", "Unable to create endpoint JSON object for Policy_SetVolume for endpoint_id:%d type:%d",endpointID,endpointType); - return; - } - - json_object *paramJ= json_object_new_string(volumeStr); - json_object_object_add(pPolicyEndpointJ, "arg_volume", paramJ); - - json_object * pPolicyVolumeReply = NULL; - int policyAllowed = Policy_SetVolume(pPolicyEndpointJ,&pPolicyVolumeReply); - if (!policyAllowed) - { - afb_req_fail_f(req, "Audio policy violation", "Set volume not allowed by policy in current context for endpoint_id:%d type:%d",endpointID,endpointType); - return; - } - - err = wrap_json_unpack(pPolicyVolumeReply,"{s:i}","volume",&pEndpointInfo->iVolume); - if (err) { - afb_req_fail_f(req, "Invalid policy reply", "Policy volume change reply for endpoint_id:%d type:%d not a valid JSON object=%s", endpointID,endpointType,json_object_get_string(pPolicyVolumeReply)); - return; - } -#else - // Simulate that policy returns target state (accepted) - pEndpointInfo->iVolume = atoi(volumeStr); -#endif - } - - json_object * volumeJ = json_object_new_int(pEndpointInfo->iVolume); - - afb_req_success(req, volumeJ, "Set/get volume completed"); -} - -PUBLIC void audiohlapi_get_endpoint_info(struct afb_req req) -{ - json_object *queryJ = NULL; - endpointID_t endpointID = AHL_UNDEFINED; - char * pEndpointTypeStr = NULL; - EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:s,s:i}", "endpoint_type", &pEndpointTypeStr,"endpoint_id",&endpointID); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - endpointType = EndpointTypeToEnum(pEndpointTypeStr); - - EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType); - if (pEndpointInfo == NULL) - { - afb_req_fail_f(req, "Endpoint not found", "Endpoint information not found for id:%d type%d",endpointID,endpointType); - return; - } - - json_object *endpointInfoJ = NULL; - EndpointInfoToJSON(pEndpointInfo,&endpointInfoJ); - - afb_req_success(req, endpointInfoJ, "Retrieved endpoint information and properties"); -} - -PUBLIC void audiohlapi_property(struct afb_req req) -{ - json_object *queryJ = NULL; - endpointID_t endpointID = AHL_UNDEFINED; - char * pEndpointTypeStr = NULL; - EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; - char * propertyName = NULL; - json_object * propValueJ = NULL; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:s,s:i,s:s,s?o}", "endpoint_type", &pEndpointTypeStr,"endpoint_id",&endpointID,"property_name",&propertyName,"value",&propValueJ); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - endpointType = EndpointTypeToEnum(pEndpointTypeStr); - - EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType); - if (pEndpointInfo == NULL) - { - afb_req_fail_f(req, "Endpoint not found", "Endpoint information not found for id:%d type:%d",endpointID,endpointType); - return; - } - - if (propValueJ != NULL) { - #ifndef AHL_DISCONNECT_POLICY - json_object *pPolicyEndpointJ = NULL; - err = EndpointInfoToJSON(pEndpointInfo, &pPolicyEndpointJ); - if (err == AHL_POLICY_UTIL_FAIL) - { - afb_req_fail_f(req, "JSON packaging error", "Unable to create endpoint JSON object for Policy_SetProperty for endpoint_id:%d type:%d",endpointID,endpointType); - return; - } - - json_object *paramJ= json_object_new_string(propertyName); - json_object_object_add(pPolicyEndpointJ, "arg_property_name", paramJ); - json_object_object_add(pPolicyEndpointJ, "arg_property_value", propValueJ); - - // Call policy to allow custom policy actions in current context - json_object * pPropertyReply = NULL; - int policyAllowed = Policy_SetProperty(pPolicyEndpointJ,&pPropertyReply); - if (!policyAllowed) - { - afb_req_fail_f(req, "Policy violation", "Policy rejected set property control in current context for endpointID: %d type:%d",endpointID,endpointType); - return; - } - - json_object * pPropReplyValue = NULL; - err = wrap_json_unpack(pPropertyReply,"{s:o}","value",&pPropReplyValue); - if (err) { - afb_req_fail_f(req, "JSON parse error", "Policy returned property value for endpointID:%d type:%d not a valid JSON object=%s", endpointID,endpointType,json_object_get_string(pPropertyReply)); - return; - } - if (pEndpointInfo->pPropTable && pPropReplyValue) { - json_object_get(pPropReplyValue); - g_hash_table_insert(pEndpointInfo->pPropTable, propertyName, pPropReplyValue); - } - #else - // Simulate that policy returns target state (accepted) - if (pEndpointInfo->pPropTable && propValueJ) - json_object_get(propValueJ); - g_hash_table_insert(pEndpointInfo->pPropTable, propertyName, propValueJ); - #endif - } - - // Retrieve cached property value - json_object * propertyValJ = (json_object *)g_hash_table_lookup(pEndpointInfo->pPropTable,propertyName); - if (propertyValJ == NULL) { - afb_req_fail_f(req, "Property information not found", "Property information not found for property:%s endpointID:%d type:%d",propertyName,endpointID,endpointType); - return; - } - - json_object_get(propertyValJ); // Increase ref count so that framework does not free our JSON object - - afb_req_success(req, propertyValJ, "Set/get property completed"); -} - -PUBLIC void audiohlapi_get_list_actions(struct afb_req req) -{ - json_object *queryJ = NULL; - char * audioRole = NULL; - json_object * roleActionsJ = NULL; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:s}", "audio_role",&audioRole); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - - // Build and return list of actions for specific audio role - RoleInfoT * pRole = GetRole(audioRole); - if ( pRole == NULL ) - { - afb_req_fail_f(req, "Invalid audio role", "Audio role was not found in configuration -> %s",audioRole); - return; - } - - roleActionsJ = json_object_new_array(); - if (pRole->pActionList) { - int iNumberActions = pRole->pActionList->len; - for ( int i = 0 ; i < iNumberActions; i++) - { - char * pActionName = g_ptr_array_index(pRole->pActionList,i); - json_object * actionJ = json_object_new_string(pActionName); - json_object_array_add(roleActionsJ, actionJ); - } - } - - afb_req_success(req, roleActionsJ, "Retrieved action list for audio role"); -} - -PUBLIC void audiohlapi_post_action(struct afb_req req) -{ - json_object *queryJ = NULL; - char * actionName = NULL; - char * audioRole = NULL; - char * mediaName = NULL; - json_object *actionContext = NULL; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:s,s:s,s?s,s?o}", "action_name", &actionName,"audio_role",&audioRole,"media_name",&mediaName,"action_context",&actionContext); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - - // Verify if known action for audio role - RoleInfoT * pRole = GetRole(audioRole); - if ( pRole == NULL ) - { - afb_req_fail_f(req, "Invalid audio role", "Audio role was not found in configuration -> %s",audioRole); - return; - } - - // Check to find specific action - int iActionFound = 0; - if (pRole->pActionList) { - int iNumberActions = pRole->pActionList->len; - char * pTargetActionName = NULL; - for ( int i = 0 ; i < iNumberActions; i++) - { - pTargetActionName = g_ptr_array_index(pRole->pActionList,i); - if ( strcasecmp(pTargetActionName,actionName)==0) { - iActionFound = 1; - break; - } - } - } - - if (!iActionFound) { - afb_req_fail_f(req, "Action not found for audio role", "Action -> %s not found for role:%s",actionName,audioRole); - return; - } - - // Disable this to avoid problems (temporary solution) -// #ifndef AHL_DISCONNECT_POLICY -// // Call policy to allow custom policy actions in current context (e.g. cancel playback) -// json_object * pActionInfo = NULL; -// err = wrap_json_pack(&pActionInfo, "{s:s,s:s,s?s,s?o}", "action_name", &actionName,"audio_role",&audioRole,"media_name",&mediaName,"action_context",&actionContext); -// if (err) { -// afb_req_fail_f(req, "Invalid arguments", "Could not create action JSON object arguments"); -// return; -// } -// json_object_get(pActionInfo); -// int policyAllowed = Policy_PostAction(pActionInfo); -// if (!policyAllowed) -// { -// afb_req_fail_f(req, "Audio policy violation", "Post sound action not allowed by policy in current context for action:%s and audiorole:%s",actionName,audioRole); -// return; -// } -// #endif - - afb_req_success(req, NULL, "Posted sound action"); - } - -PUBLIC void audiohlapi_event_subscription(struct afb_req req) -{ - json_object *queryJ = NULL; - json_object * eventArrayJ = NULL; - int iSubscribe = 1; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:o,s:i}", "events", &eventArrayJ,"subscribe",&iSubscribe); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - - json_type jType = json_object_get_type(eventArrayJ); - int iNumEvents = 0; - if(jType == json_type_array) - { - iNumEvents = json_object_array_length(eventArrayJ); - } - for (int i = 0; i < iNumEvents; i++) - { - char * pEventName = NULL; - json_object * jEvent = json_object_array_get_idx(eventArrayJ,i); - pEventName = (char *)json_object_get_string(jEvent); - if(pEventName == NULL) { - afb_req_fail(req, "JSON arguments parse error", "Empty event"); - return; - } - else if(!strcasecmp(pEventName, AHL_ENDPOINT_PROPERTY_EVENT)) { - if (iSubscribe) - afb_req_subscribe(req, g_AHLCtx.policyCtx.propertyEvent); - else - afb_req_unsubscribe(req, g_AHLCtx.policyCtx.propertyEvent); - } - else if(!strcasecmp(pEventName, AHL_ENDPOINT_VOLUME_EVENT)) { - if (iSubscribe) - afb_req_subscribe(req, g_AHLCtx.policyCtx.volumeEvent); - else - afb_req_unsubscribe(req, g_AHLCtx.policyCtx.volumeEvent); - } - else if(!strcasecmp(pEventName, AHL_POST_ACTION_EVENT)) { - if (iSubscribe) - afb_req_subscribe(req, g_AHLCtx.policyCtx.postActionEvent); - else - afb_req_unsubscribe(req, g_AHLCtx.policyCtx.postActionEvent); - } - else { - afb_req_fail_f(req, "JSON arguments parse error", "Invalid event:%s",pEventName); - return; - } - } - - afb_req_success(req, NULL, "Event subscription update finished"); -} - -// Since the policy is currently in the same binding, it cannot raise events on its own -// This is a first step toward isolation, when policy is migrated in its own binding it can simply raise AGL events -// This binding will register for these policy events and will execute the code below upon event reception -PUBLIC void audiohlapi_raise_event(json_object * pEventDataJ) -{ - char * pEventName = NULL; - - int err = wrap_json_unpack(pEventDataJ,"{s:s}","event_name", &pEventName); - if(err) - { - AFB_ERROR("Unable to retrieve event name %s",json_object_get_string(pEventDataJ)); - return; - } - - if(strcasecmp(pEventName, AHL_ENDPOINT_PROPERTY_EVENT)==0) { - char * pAudioRole = NULL; - char * pPropertyName = NULL; - endpointID_t endpointID = AHL_UNDEFINED; - EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; - json_object * propValueJ = NULL; - int err = wrap_json_unpack(pEventDataJ,"{s:i,s:i,s:s,s:o,s:s}", - "endpoint_id", &endpointID, - "endpoint_type", &endpointType, - "property_name", &pPropertyName, - "value",&propValueJ, - "audio_role", &pAudioRole); - if(err) - { - AFB_ERROR("Unable to unpack property event for event: %s",pEventName); - return; - } - RoleInfoT * pRole = GetRole(pAudioRole); - if ( pRole == NULL ){ - AFB_ERROR("Requested audio role does not exist in current configuration -> %s", pAudioRole); - return; - } - EndpointInfoT * pEndpointInfo = GetEndpointInfoWithRole(endpointID,endpointType,pRole); - // update property value - if ((pEndpointInfo!=NULL) && (pEndpointInfo->pPropTable!=NULL)) - { - json_type jType = json_object_get_type(propValueJ); - switch (jType) { - case json_type_double: - g_hash_table_insert(pEndpointInfo->pPropTable, pPropertyName, json_object_new_double(json_object_get_double(propValueJ))); - break; - case json_type_int: - g_hash_table_insert(pEndpointInfo->pPropTable, pPropertyName, json_object_new_int(json_object_get_int(propValueJ))); - break; - case json_type_string: - g_hash_table_insert(pEndpointInfo->pPropTable, pPropertyName, json_object_new_string(json_object_get_string(propValueJ))); - break; - default: - AFB_ERROR("Invalid property argument property value not a valid JSON object query=%s", json_object_get_string(propValueJ)); - return ; - } - } - // Remove event name from object - json_object_object_del(pEventDataJ,"event_name"); - afb_event_push(g_AHLCtx.policyCtx.propertyEvent,pEventDataJ); - } - else if(strcasecmp(pEventName, AHL_ENDPOINT_VOLUME_EVENT)==0) { - char * pAudioRole = NULL; - endpointID_t endpointID = AHL_UNDEFINED; - EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; - int iVolume = 0; - int err = wrap_json_unpack(pEventDataJ,"{s:i,s:i,s:i,s:s}", - "endpoint_id", &endpointID, - "endpoint_type", &endpointType, - "value",&iVolume, - "audio_role", &pAudioRole); - if(err) - { - AFB_ERROR("Unable to unpack volume event data"); - return; - } - RoleInfoT * pRole = GetRole(pAudioRole); - if ( pRole == NULL ){ - AFB_ERROR("Requested audio role does not exist in current configuration -> %s", pAudioRole); - return; - } - EndpointInfoT * pEndpointInfo = GetEndpointInfoWithRole(endpointID,endpointType,pRole); - // update volume value - if(pEndpointInfo) - { - pEndpointInfo->iVolume = iVolume; - } - else - { - AFB_ERROR("Unable to find endpoint id:%d type:%d for role:%s",endpointID,endpointType,pAudioRole); - } - // Remove event name from object - json_object_object_del(pEventDataJ,"event_name"); - afb_event_push(g_AHLCtx.policyCtx.volumeEvent,pEventDataJ); - } - else if(strcasecmp(pEventName, AHL_POST_ACTION_EVENT)==0) { - // Remove event name from object - json_object_object_del(pEventDataJ,"event_name"); - // BUG: This crashes... - afb_event_push(g_AHLCtx.policyCtx.postActionEvent,pEventDataJ); - } - else if(strcasecmp(pEventName, AHL_STREAM_STATE_EVENT)==0) { - streamID_t streamID = AHL_UNDEFINED; - StreamEventT streamEvent = STREAM_EVENT_MAXVALUE; - int err = wrap_json_unpack(pEventDataJ,"{s:i,s:i}", - "stream_id", &streamID, - "state_event", &streamEvent); - if(err) - { - AFB_ERROR("Unable to unpack stream event data for event %s",pEventName); - return; - } - - StreamInfoT * pStreamInfo = GetStream(streamID); - if (pStreamInfo == NULL) { - AFB_ERROR("Specified stream not currently active stream_id -> %d",streamID); - return; - } - - // update streamstate value - switch (streamEvent) { - case STREAM_EVENT_START: - pStreamInfo->streamState = STREAM_STATE_RUNNING; - break; - case STREAM_EVENT_STOP: - pStreamInfo->streamState = STREAM_STATE_IDLE; - break; - case STREAM_EVENT_PAUSE: - pStreamInfo->streamState = STREAM_STATE_PAUSED; - break; - case STREAM_EVENT_RESUME: - pStreamInfo->streamState = STREAM_STATE_RUNNING; - break; - case STREAM_EVENT_MUTED: - pStreamInfo->streamMute = STREAM_MUTED; - break; - case STREAM_EVENT_UNMUTED: - pStreamInfo->streamMute = STREAM_UNMUTED; - break; - default: - AFB_ERROR("Unknown stream event"); - } - - // Remove event name from object - json_object_object_del(pEventDataJ,"event_name"); - afb_event_push(pStreamInfo->streamStateEvent,pEventDataJ); - } - else { - AFB_ERROR("Unknown event name %s",pEventName); - } -}
\ No newline at end of file diff --git a/ahl-binding/ahl-binding.cpp b/ahl-binding/ahl-binding.cpp new file mode 100644 index 0000000..37e6847 --- /dev/null +++ b/ahl-binding/ahl-binding.cpp @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Loïc Collignon <loic.collignon@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <algorithm> +#include "ahl-binding.hpp" + +afb_dynapi* AFB_default; // BUG: Is it possible to get rid of this ? + +/** + * @brief Callback invoked on new api creation. + * @param[in] handle Handle to the new api. + * @return Status code, zero if success. + */ +int ahl_api_create(void*, struct afb_dynapi* handle) +{ + return ahl_binding_t::instance().preinit(handle); +} + +/** + * @brief Entry point for dynamic API. + * @param[in] handle Handle to start with for API creation. + * @return Status code, zero if success. + */ +int afbBindingVdyn(afb_dynapi* handle) +{ + using namespace std::placeholders; + assert(handle != nullptr); + + AFB_default = handle; + + return afb_dynapi_new_api( + handle, + HL_API_NAME, + HL_API_INFO, + 1, + ahl_api_create, + nullptr + ); +} + +/** + * @brief Callback invoked when API enter the init phase. + * @return Status code, zero if success. + */ +int ahl_api_init(afb_dynapi*) +{ + return ahl_binding_t::instance().init(); +} + +/** + * @brief Callback invoked when an event is received. + * @param[in] e Event's name. + * @param[in] o Event's args. + */ +void ahl_api_on_event(afb_dynapi*, const char* e, struct json_object* o) +{ + ahl_binding_t::instance().event(e, o); +} + +/** + * @brief Callback invoked when a 'roles' section is found in config file. + * @param[in] o Config section to handle. + * @return Status code, zero if success. + */ +int ahl_api_config_roles(afb_dynapi*, CtlSectionT*, json_object* o) +{ + return ahl_binding_t::instance().parse_roles_config(o); +} + +/** + * @brief Callback invoked when clients call the verb 'get_roles'. + * @param[in] req Request to handle. + */ +void ahl_api_get_roles(afb_request* req) +{ + ahl_binding_t::instance().get_roles(req); +} + +/** + * @brief Callback invoked when clients call a 'role' verb. + * @param[in] req Request to handle. + * + * Handle dynamic verbs based on role name ('multimedia', 'navigation', ...) + */ +void ahl_api_role(afb_request* req) +{ + role_t* role = (role_t*)req->vcbdata; + assert(role != nullptr); + + role->invoke(req); +} + +/** + * @brief Default constructor. + */ +ahl_binding_t::ahl_binding_t() + : handle_{nullptr} +{ +} + +/** + * @brief Get the singleton instance. + * @return The unique instance. + */ +ahl_binding_t& ahl_binding_t::instance() +{ + static ahl_binding_t s; + return s; +} + +/** + * @brief This method is called during the pre-init phase of loading the binding. + * @param[in] handle Handle to the api. + * @return Status code, zero if success. + */ +int ahl_binding_t::preinit(afb_dynapi* handle) +{ + handle_ = handle; + + try + { + load_static_verbs(); + load_controller_configs(); + + if (afb_dynapi_on_event(handle_, ahl_api_on_event)) + throw std::runtime_error("Failed to register event handler callback."); + + if (afb_dynapi_on_init(handle_, ahl_api_init)) + throw std::runtime_error("Failed to register init handler callback."); + } + catch(std::exception& e) + { + AFB_DYNAPI_ERROR(handle, "%s", e.what()); + return -1; + } + + return 0; +} + +/** + * @brief Initialize the API. + */ +int ahl_binding_t::init() +{ + using namespace std::placeholders; + + if (afb_dynapi_require_api(handle_, HAL_MGR_API, 1)) + { + AFB_DYNAPI_ERROR(handle_, "Failed to require '%s' API!", HAL_MGR_API); + return -1; + } + AFB_DYNAPI_NOTICE(handle_, "Required '%s' API found!", HAL_MGR_API); + + if (afb_dynapi_require_api(handle_, "smixer", 1)) + { + AFB_DYNAPI_ERROR(handle_, "Failed to require 'smixer' API!"); + return -1; + } + AFB_DYNAPI_NOTICE(handle_, "Required 'smixer' API found!"); + + afb_dynapi_seal(handle_); + AFB_DYNAPI_NOTICE(handle_, "API is now sealed!"); + + if (update_streams()) return -1; + return 0; +} + +/** + * @brief Update audio roles definition by binding to streams. + * @return Status code, zero if success. + */ +int ahl_binding_t::update_streams() +{ + json_object* loaded = nullptr; + json_object* response = nullptr; + size_t i = 0, j = 0; + size_t hals_count = 0, streams_count = 0; + + if (afb_dynapi_call_sync(handle_, "4a-hal-manager", "loaded", json_object_new_object(), &loaded)) + { + AFB_DYNAPI_ERROR(handle_, "Failed to call 'loaded' verb on '4a-hal-manager' API!"); + if (loaded) AFB_DYNAPI_NOTICE(handle_, "%s", json_object_to_json_string(loaded)); + return -1; + } + response = json_object_object_get(loaded, "response"); + hals_count = json_object_array_length(response); + + for(i = 0; i < hals_count; ++i) + { + json_object* info = nullptr; + json_object* streams = nullptr; + const char* halname = json_object_get_string(json_object_array_get_idx(response, i)); + AFB_DYNAPI_DEBUG(handle_, "Found an active HAL: %s", halname); + + if (afb_dynapi_call_sync(handle_, halname, "info", json_object_new_object(), &info)) + { + AFB_DYNAPI_ERROR(handle_, "Failed to call 'info' verb on '%s' API!", halname); + if (info) AFB_DYNAPI_NOTICE(handle_, "%s", json_object_to_json_string(info)); + return -1; + } + + streams = json_object_object_get(json_object_object_get(info, "response"), "streams"); + streams_count = json_object_array_length(streams); + for(j = 0; j < streams_count; ++j) + { + update_stream( + halname, + json_object_get_string(json_object_object_get(json_object_array_get_idx(streams, j), "name")), + json_object_get_string(json_object_object_get(json_object_array_get_idx(streams, j), "cardId")) + ); + } + + json_object_put(info); + } + json_object_put(loaded); + + return 0; +} + +/** + * @brief Update the stream info for audio roles. + * @param[in] halname The hal on which the stream is. + * @param[in] stream The name of the stream. + * @param[in] deviceid The device ID to return when opening an audio role. + */ +void ahl_binding_t::update_stream(std::string halname, std::string stream, std::string deviceid) +{ + for(auto& r : roles_) + { + if(r.stream() == stream) + { + if (r.device_uri().size()) + AFB_DYNAPI_WARNING(handle_, "Multiple stream with same name: '%s'.", stream.c_str()); + else + { + r.device_uri(deviceid); + r.hal(halname); + } + } + } +} + +void ahl_binding_t::event(std::string name, json_object* arg) +{ + AFB_DYNAPI_DEBUG(handle_, "Event '%s' received with the following arg: %s", name.c_str(), json_object_to_json_string(arg)); +} + +void ahl_binding_t::load_static_verbs() +{ + if (afb_dynapi_add_verb( + handle_, + "get_roles", + "Retrieve array of available audio roles", + ahl_api_get_roles, + nullptr, + nullptr, + AFB_SESSION_NONE_V2)) + { + throw std::runtime_error("Failed to add 'get_role' verb to the API."); + } +} + +void ahl_binding_t::load_controller_configs() +{ + char* dir_list = getenv("CONTROL_CONFIG_PATH"); + if (!dir_list) dir_list = strdup(CONTROL_CONFIG_PATH); + struct json_object* config_files = CtlConfigScan(dir_list, "policy"); + if (!config_files) throw std::runtime_error("No config files found!"); + + // Only one file should be found this way, but read all just in case + size_t config_files_count = json_object_array_length(config_files); + for(size_t i = 0; i < config_files_count; ++i) + { + config_entry_t file {json_object_array_get_idx(config_files, i)}; + + if(load_controller_config(file.filepath()) < 0) + { + std::stringstream ss; + ss << "Failed to load config file '" + << file.filename() + << "' from '" + << file.fullpath() + << "'!"; + throw std::runtime_error(ss.str()); + } + } +} + +int ahl_binding_t::load_controller_config(const std::string& path) +{ + CtlConfigT* controller_config; + + controller_config = CtlLoadMetaData(handle_, path.c_str()); + if (!controller_config) + { + AFB_DYNAPI_ERROR(handle_, "Failed to load controller from config file!"); + return -1; + } + + static CtlSectionT controller_sections[] = + { + {.key = "plugins", .uid = nullptr, .info = nullptr, .loadCB = PluginConfig, .handle = nullptr, .actions = nullptr}, + {.key = "onload", .uid = nullptr, .info = nullptr, .loadCB = OnloadConfig, .handle = nullptr, .actions = nullptr}, + {.key = "controls", .uid = nullptr, .info = nullptr, .loadCB = ControlConfig, .handle = nullptr, .actions = nullptr}, + {.key = "events", .uid = nullptr, .info = nullptr, .loadCB = EventConfig, .handle = nullptr, .actions = nullptr}, + {.key = "roles", .uid = nullptr, .info = nullptr, .loadCB = ahl_api_config_roles, .handle = nullptr, .actions = nullptr }, + {.key = nullptr, .uid = nullptr, .info = nullptr, .loadCB = nullptr, .handle = nullptr, .actions = nullptr} + }; + + CtlLoadSections(handle_, controller_config, controller_sections); + + return 0; +} + +int ahl_binding_t::parse_roles_config(json_object* o) +{ + assert(o != nullptr); + assert(json_object_is_type(o, json_type_array)); + + if (roles_.size()) return 0; // Roles already added, ignore. + + size_t count = json_object_array_length(o); + roles_.reserve(count); + for(size_t i = 0; i < count; ++i) + { + json_object* jr = json_object_array_get_idx(o, i); + assert(jr != nullptr); + + roles_.push_back(role_t(jr)); + role_t& r = roles_[roles_.size() - 1]; + if(create_api_verb(&r)) + return -1; + } + + return 0; +} + +int ahl_binding_t::create_api_verb(role_t* r) +{ + AFB_DYNAPI_NOTICE(handle_, "New audio role: %s", r->uid().c_str()); + + if (afb_dynapi_add_verb( + handle_, + r->uid().c_str(), + r->description().c_str(), + ahl_api_role, + r, + nullptr, + AFB_SESSION_NONE_V2)) + { + AFB_DYNAPI_ERROR(handle_, "Failed to add '%s' verb to the API.", + r->uid().c_str()); + return -1; + } + + return 0; +} + +void ahl_binding_t::get_roles(afb_request* req) +{ + json_object* result = json_object_new_array(); + for(const auto& r : roles_) + json_object_array_add(result, json_object_new_string(r.uid().c_str())); + afb_request_success(req, result, nullptr); +} + +const std::vector<role_t> ahl_binding_t::roles() const +{ + return roles_; +} + +afb_dynapi* ahl_binding_t::handle() const +{ + return handle_; +} diff --git a/ahl-binding/ahl-binding.h b/ahl-binding/ahl-binding.h deleted file mode 100644 index 6ceb040..0000000 --- a/ahl-binding/ahl-binding.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2017 "Audiokinetic Inc" - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef AHL_BINDING_INCLUDE -#define AHL_BINDING_INCLUDE - -//#define AHL_DISCONNECT_POLICY // define for debugging HLB in standalone only - -#include <json-c/json.h> -#include <glib.h> -#define AFB_BINDING_VERSION 2 -#include <afb/afb-binding.h> -#include "ahl-interface.h" -#include "ahl-policy-utils.h" - -#ifndef PUBLIC - #define PUBLIC -#endif - -#define AHL_SUCCESS 0 -#define AHL_WARNING 1 -#define AHL_FAIL -1 - -#define AHL_ACCESS_CONTROL_GRANTED 1 -#define AHL_ACCESS_CONTROL_DENIED 0 - -#define AHL_STR_MAX_LENGTH 256 - -typedef struct EndpointInfo -{ - endpointID_t endpointID; // Unique endpoint ID (per type) - EndpointTypeT type; // Source or sink device - char * gsDeviceName; // Unique device card name - char * gsDisplayName; // Application display name - char * gsDeviceURI; // Associated URI - char * gsDeviceDomain; // Device URI domain (e.g. alsa or pulse) - char * pRoleName; // Role assigned to this endpoint - DeviceURITypeT deviceURIType; // Device URI type (includes audio domain information) - char * gsHALAPIName; // HAL associated with the device (for volume control) - AlsaDeviceInfoT alsaInfo; // ALSA specific device information - AudioFormatT format; // Preferred audio format supported (later could be array of supported formats) - int iVolume; // Storage for current endpoint volume (policy effected). - GHashTable * pPropTable; // Storage for array of properties (policy effected) -} EndpointInfoT; - -typedef struct StreamInfo { - streamID_t streamID; // Stream unique ID - EndpointInfoT * pEndpointInfo; // Associated endpoint information (reference) - StreamStateT streamState; // Stream activity state - StreamMuteT streamMute; // Stream mute state - struct afb_event streamStateEvent; // Stream specific event for stream state changes - EndpointSelectionModeT endpointSelMode; // Automatic (priority based) or manual endpoint selection - char * pRoleName; // Role string identifier (from role config but could be programatically overriden later) - int iPriority; // Role normalized priority (0-100) (from role config but could be programatically overriden later) - InterruptBehaviorT eInterruptBehavior; // Role behavior when interrupting lower priority streams (from role config but could be programatically overriden later) -} StreamInfoT; - -typedef struct RoleInfo { - char * pRoleName; // Role string identifier - int iPriority; // Role normalized priority (0-100) - InterruptBehaviorT eInterruptBehavior; // Role behavior when interrupting lower priority streams - GPtrArray * pActionList; // List of supported actions for the role (gchar*) - GPtrArray * pSourceEndpoints; // Source endpoints info (EndpointInfoT*) - GPtrArray * pSinkEndpoints; // Sink endpoints info (EndpointInfoT*) -} RoleInfoT; - -// Parts of the context that are visible to the policy (for state based decisions) -typedef struct AHLPolicyCtx { - GHashTable * pRoleInfo; // Hash table of role information structure (RoleInfoT*) accessed by role name - GHashTable * pStreams; // List of active streams (StreamInfoT*) accessed by streamID - GPtrArray * pHALList; // List of HAL dependencies - // TODO: Events need to be sent directly by HLB when separation with policy complete - struct afb_event propertyEvent; // AGL event used when property changes - struct afb_event volumeEvent; // AGL event used when volume changes - struct afb_event postActionEvent; // AGL event used on post action call -} AHLPolicyCtxT; - -// Global binding context -typedef struct AHLCtx { - AHLPolicyCtxT policyCtx; - endpointID_t nextSourceEndpointID; // Counter to assign new ID - endpointID_t nextSinkEndpointID; // Counter to assign new ID - endpointID_t nextStreamID; // Counter to assign new ID -} AHLCtxT; - -// Client specific binding context -typedef struct AHLClientCtx { - GArray * pStreamAccessList; // List of streams that client has control over -} AHLClientCtxT; - -// ahl-binding.c -PUBLIC int AhlBindingInit(); -PUBLIC void AhlOnEvent(const char *evtname, json_object *eventJ); - -// ahl-deviceenum.c -int EnumerateDevices(json_object * in_jDeviceArray, char * in_pAudioRole, EndpointTypeT in_deviceType, GPtrArray * out_pEndpointArray); -EndpointInfoT * InitEndpointInfo(); -void TermEndpointInfo( gpointer data ); -// ahl-config.c -int ParseHLBConfig(); -// ahl-policy.c -#ifndef AHL_DISCONNECT_POLICY -PUBLIC void audiohlapi_raise_event(json_object *EventDataJ); -#endif - -#endif // AHL_BINDING_INCLUDE diff --git a/ahl-binding/ahl-binding.hpp b/ahl-binding/ahl-binding.hpp new file mode 100644 index 0000000..66fce84 --- /dev/null +++ b/ahl-binding/ahl-binding.hpp @@ -0,0 +1,78 @@ +#pragma once + +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Loïc Collignon <loic.collignon@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <exception> +#include <functional> +#include <sstream> +#include <vector> +#include <list> +#include <map> +#include <cassert> + +#include "config_entry.hpp" +#include "role.hpp" + +#define HL_API_NAME "ahl-4a" +#define HL_API_INFO "Audio high level API for AGL applications" +#define HAL_MGR_API "4a-hal-manager" + +extern "C" { + + #define AFB_BINDING_VERSION 0 + #include <afb/afb-binding.h> + #include <string.h> + #include <ctl-config.h> +}; + +class ahl_binding_t +{ + using role_action = std::function<void(afb_request*, std::string, std::string, json_object*)>; + +private: + afb_dynapi* handle_; + std::vector<role_t> roles_; + + explicit ahl_binding_t(); + + void load_static_verbs(); + + + void load_controller_configs(); + int load_controller_config(const std::string& path); + int update_streams(); + void update_stream(std::string hal, std::string stream, std::string deviceuri); + int create_api_verb(role_t* r); + + void policy_open(afb_request* req, const role_t& role); + +public: + static ahl_binding_t& instance(); + int preinit(afb_dynapi* handle); + int init(); + void event(std::string name, json_object* arg); + void get_roles(afb_request* req); + + const std::vector<role_t> roles() const; + afb_dynapi* handle() const; + + void audiorole(afb_request* req); + int parse_roles_config(json_object* o); +}; + + diff --git a/ahl-binding/ahl-config.c b/ahl-binding/ahl-config.c deleted file mode 100644 index 0bd16f2..0000000 --- a/ahl-binding/ahl-config.c +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (C) 2017 "Audiokinetic Inc" - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> -#include <string.h> -#include <json-c/json.h> -#include "wrap-json.h" -#include "filescan-utils.h" - -#include "ahl-binding.h" - -extern AHLCtxT g_AHLCtx; - -static InterruptBehaviorT InterruptBehaviorToEnum(char * in_pInterruptBehaviorStr) -{ - g_assert_nonnull(in_pInterruptBehaviorStr); - if (strcasecmp(in_pInterruptBehaviorStr,AHL_INTERRUPTBEHAVIOR_CONTINUE)==0) { - return INTERRUPTBEHAVIOR_CONTINUE; - } - else if (strcasecmp(in_pInterruptBehaviorStr,AHL_INTERRUPTBEHAVIOR_CANCEL)==0) { - return INTERRUPTBEHAVIOR_CANCEL; - } - else if (strcasecmp(in_pInterruptBehaviorStr,AHL_INTERRUPTBEHAVIOR_PAUSE)==0) { - return INTERRUPTBEHAVIOR_PAUSE; - } - else - return INTERRUPTBEHAVIOR_MAXVALUE; -} - -static json_object* CtlConfigScan(const char *dirList, const char *prefix) { - char controlFile [CONTROL_MAXPATH_LEN]; - strncpy(controlFile, prefix, CONTROL_MAXPATH_LEN); - strncat(controlFile, GetBinderName(), CONTROL_MAXPATH_LEN); - - // search for default dispatch config file - json_object* responseJ = ScanForConfig(dirList, CTL_SCAN_RECURSIVE, controlFile, "-config.json"); - - return responseJ; -} - -static char* CtlConfigSearch(const char *dirList, const char *prefix) { - int index, err; - - // search for default dispatch config file - json_object* responseJ = CtlConfigScan (dirList, prefix); - if (!responseJ) return NULL; - - // We load 1st file others are just warnings - for (index = 0; index < json_object_array_length(responseJ); index++) { - json_object *entryJ = json_object_array_get_idx(responseJ, index); - - char *filename; - char*fullpath; - err = wrap_json_unpack(entryJ, "{s:s, s:s !}", "fullpath", &fullpath, "filename", &filename); - if (err) { - AFB_ERROR("CTL-INIT HOOPs invalid JSON entry= %s", json_object_get_string(entryJ)); - return NULL; - } - - if (index == 0) { - char filepath[CONTROL_MAXPATH_LEN]; - strncpy(filepath, fullpath, sizeof (filepath)); - strncat(filepath, "/", sizeof (filepath)); - strncat(filepath, filename, sizeof (filepath)); - return (strdup(filepath)); - } - } - // no config found - return NULL; -} - -int ParseHLBConfig() { - char * versionStr = NULL; - json_object * jAudioRoles = NULL; - json_object * jHALList = NULL; - char * policyModule = NULL; - - const char *dirList=getenv("AAAA_CONFIG_PATH"); - if (!dirList) dirList=CONTROL_CONFIG_PATH; - - const char *configfile_path =CtlConfigSearch(dirList, "ahl-"); - if (!configfile_path) { - AFB_ERROR("Error: No ahl-* config found invalid JSON %s ", dirList); - return AHL_FAIL; - } - - AFB_NOTICE("High-level config file -> %s\n", configfile_path); - - // Open configuration file - json_object *config_JFile=json_object_from_file(configfile_path); - if(config_JFile == NULL) - { - AFB_ERROR("Error: Can't open configuration file -> %s",configfile_path); - return AHL_FAIL; - } - - int err = wrap_json_unpack(config_JFile, "{s:s,s:s,s:o,s:o}", "version", &versionStr,"policy_module", &policyModule,"audio_roles",&jAudioRoles,"hal_list",&jHALList); - if (err) { - AFB_ERROR("Invalid configuration file -> %s", configfile_path); - return AHL_FAIL; - } - AFB_INFO("High-level audio API version: %s", "1.0.0"); - AFB_INFO("Config version: %s", versionStr); - AFB_INFO("Policy module: %s", policyModule); - - int iHALListLength = 0; - int iNumberOfRoles = 0; - - if(jHALList) - { - json_type jTypeHalList = json_object_get_type(jHALList); - if(jTypeHalList ==json_type_array) - { - iHALListLength = json_object_array_length(jHALList); - } - } - - if(jAudioRoles) - { - json_type jTypeAudioRole = json_object_get_type(jAudioRoles); - if(jTypeAudioRole ==json_type_array) - { - iNumberOfRoles = json_object_array_length(jAudioRoles); - } - } - g_AHLCtx.policyCtx.pHALList = g_ptr_array_new_with_free_func(g_free); - g_AHLCtx.policyCtx.pRoleInfo = g_hash_table_new(g_str_hash, g_str_equal); - - for (int i = 0; i < iHALListLength; i++) - { - char * pHAL = NULL; - json_object * jHAL = json_object_array_get_idx(jHALList,i); - if (jHAL) { - pHAL = (char *)json_object_get_string(jHAL); - char * pHALName = g_strdup( pHAL ); - g_ptr_array_add( g_AHLCtx.policyCtx.pHALList, pHALName ); - AFB_INFO("High-level binding add dependency on HAL: %s", pHALName); - - // Set dependency on HAL specified - err = afb_daemon_require_api_v2(pHAL,1) ; - if( err != 0 ) - { - AFB_ERROR("Audio high level API could not set dependency on API: %s",pHAL); - return AHL_FAIL; - } - } - } - - for (int i = 0; i < iNumberOfRoles; i++) - { - int priority = 0; - json_object * jAudioRole = json_object_array_get_idx(jAudioRoles,i); - json_object * jOutputDevices = NULL; - json_object * jInputDevices = NULL; - json_object * jActions = NULL; - char * pRoleName = NULL; - char * pInteruptBehavior = NULL; - - int iNumOutDevices = 0; - int iNumInDevices = 0; - int iNumActions = 0; - - err = wrap_json_unpack(jAudioRole, "{s:s,s:i,s:s,s?o,s?o,s?o}", - "name", &pRoleName, - "priority",&priority, - "interupt_behavior",&pInteruptBehavior, - "output",&jOutputDevices, - "input",&jInputDevices, - "actions",&jActions - ); - if (err) { - AFB_ERROR("Invalid audio role configuration : %s", json_object_to_json_string(jAudioRole)); - return AHL_FAIL; - } - - AFB_DEBUG("Parsing configuration audio role: %s", pRoleName); - - if (jOutputDevices) - { - json_type jTypeOutputDevices = json_object_get_type(jOutputDevices); - if(jTypeOutputDevices == json_type_array) - iNumOutDevices = json_object_array_length(jOutputDevices); - } - if (jInputDevices) - { - json_type jTypeInputDevices = json_object_get_type(jInputDevices); - if(jTypeInputDevices == json_type_array) - iNumInDevices = json_object_array_length(jInputDevices); - } - if (jActions) - { - json_type jTypeActions = json_object_get_type(jActions); - if(jTypeActions == json_type_array) - iNumActions = json_object_array_length(jActions); - } - RoleInfoT * pRoleInfo = (RoleInfoT*) malloc(sizeof(RoleInfoT)); - memset(pRoleInfo,0,sizeof(RoleInfoT)); - pRoleInfo->pRoleName = g_strdup( pRoleName ); - pRoleInfo->iPriority = priority; - pRoleInfo->eInterruptBehavior = InterruptBehaviorToEnum(pInteruptBehavior); - - // Actions - pRoleInfo->pActionList = g_ptr_array_new_with_free_func(g_free); - // Parse and validate list of available actions - for (int i = 0; i < iNumActions; i++) - { - json_object * jAction = json_object_array_get_idx(jActions,i); - char * pActionName = (char *)json_object_get_string(jAction); - if (pActionName) - g_ptr_array_add(pRoleInfo->pActionList, g_strdup(pActionName)); - } - - // Sources - pRoleInfo->pSourceEndpoints = g_ptr_array_new_with_free_func(TermEndpointInfo); - if (iNumInDevices) { - err = EnumerateDevices(jInputDevices,pRoleName,ENDPOINTTYPE_SOURCE,pRoleInfo->pSourceEndpoints); - if (err) { - AFB_ERROR("Invalid input devices for role %s : %s", pRoleName, json_object_to_json_string(jInputDevices)); - return AHL_FAIL; - } - } - // Sinks - pRoleInfo->pSinkEndpoints = g_ptr_array_new_with_free_func(TermEndpointInfo); - if (iNumOutDevices) { - err = EnumerateDevices(jOutputDevices,pRoleName,ENDPOINTTYPE_SINK,pRoleInfo->pSinkEndpoints); - if (err) { - AFB_ERROR("Invalid output devices for role %s: %s", pRoleName, json_object_to_json_string(jOutputDevices)); - return AHL_FAIL; - } - } - - g_hash_table_insert(g_AHLCtx.policyCtx.pRoleInfo, pRoleInfo->pRoleName, pRoleInfo); - } - - // Build lists of all device URI referenced in config file (input/output) - AFB_DEBUG ("Audio high-level - Parse high-level audio configuration done"); - return AHL_SUCCESS; -} diff --git a/ahl-binding/ahl-deviceenum.c b/ahl-binding/ahl-deviceenum.c deleted file mode 100644 index 75b0715..0000000 --- a/ahl-binding/ahl-deviceenum.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (C) 2017 "Audiokinetic Inc" - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <alsa/asoundlib.h> -#include <alsa/pcm.h> -#include "ahl-binding.h" - -extern AHLCtxT g_AHLCtx; - -// TODO: Hash from endpoint ID information instead -static endpointID_t CreateNewSourceID() -{ - endpointID_t newID = g_AHLCtx.nextSourceEndpointID; - g_AHLCtx.nextSourceEndpointID++; - return newID; -} - -// TODO: Hash from endpoint ID information instead -static endpointID_t CreateNewSinkID() -{ - endpointID_t newID = g_AHLCtx.nextSinkEndpointID; - g_AHLCtx.nextSinkEndpointID++; - return newID; -} - -// Watchout: This function uses strtok and is destructive on the input string (use a copy) -static int SeparateDomainFromDeviceURI( char * in_pDeviceURI, char ** out_pDomain, char ** out_pDevice) -{ - *out_pDomain = strtok(in_pDeviceURI, "."); - if (*out_pDomain == NULL) - { - AFB_ERROR("Error tokenizing device URI -> %s",in_pDeviceURI); - return AHL_FAIL; - } - // TODO: Validate domain is known string (e.g. ALSA,Pulse,GStreamer) - *out_pDevice = strtok(NULL, "."); - if (*out_pDevice == NULL) - { - AFB_ERROR("Error tokenizing device URI -> %s",in_pDeviceURI); - return AHL_FAIL; - } - return AHL_SUCCESS; -} - -static int IsAlsaDomain(const char * in_pDomainStr) -{ - return (int) (strcasecmp(in_pDomainStr,AHL_DOMAIN_ALSA) == 0); -} - -static int IsPulseDomain(const char * in_pDomainStr) -{ - return (int) (strcasecmp(in_pDomainStr,AHL_DOMAIN_PULSE) == 0); -} - -static int IsGStreamerDomain(const char * in_pDomainStr) -{ - return (int) (strcasecmp(in_pDomainStr,AHL_DOMAIN_GSTREAMER) == 0); -} - -static int IsExternalDomain(const char * in_pDomainStr) -{ - return (int) (strcasecmp(in_pDomainStr,AHL_DOMAIN_EXTERNAL) == 0); -} - -static int FillALSAPCMInfo( snd_pcm_t * in_pPcmHandle, EndpointInfoT * out_pEndpointInfo ) -{ - g_assert_nonnull(in_pPcmHandle); - g_assert_nonnull(out_pEndpointInfo); - snd_pcm_type_t pcmType = 0; - snd_pcm_info_t * pPcmInfo = NULL; - int iAlsaRet = 0; - const char * pCardName = NULL; - snd_ctl_t * ctlHandle = NULL; - snd_ctl_card_info_t * ctlInfo = NULL; - - snd_pcm_info_alloca(&pPcmInfo); - snd_ctl_card_info_alloca(&ctlInfo); - - // retrieve PCM type - pcmType = snd_pcm_type(in_pPcmHandle); - switch (pcmType) { - case SND_PCM_TYPE_HW: - out_pEndpointInfo->deviceURIType = DEVICEURITYPE_ALSA_HW; - break; - case SND_PCM_TYPE_DMIX: - out_pEndpointInfo->deviceURIType = DEVICEURITYPE_ALSA_DMIX; - break; - case SND_PCM_TYPE_SOFTVOL: - out_pEndpointInfo->deviceURIType = DEVICEURITYPE_ALSA_SOFTVOL; - break; - case SND_PCM_TYPE_PLUG: - out_pEndpointInfo->deviceURIType = DEVICEURITYPE_ALSA_PLUG; - break; - default: - out_pEndpointInfo->deviceURIType = DEVICEURITYPE_ALSA_OTHER; - break; - } - - iAlsaRet = snd_pcm_info(in_pPcmHandle,pPcmInfo); - if (iAlsaRet < 0) - { - AFB_WARNING("Error retrieving PCM device info"); - return AHL_FAIL; - } - - // get card number - out_pEndpointInfo->alsaInfo.cardNum = snd_pcm_info_get_card(pPcmInfo); - if ( out_pEndpointInfo->alsaInfo.cardNum < 0 ) - { - AFB_WARNING("No Alsa card number available"); - return AHL_FAIL; - } - - // get device number - out_pEndpointInfo->alsaInfo.deviceNum = snd_pcm_info_get_device(pPcmInfo); - if ( out_pEndpointInfo->alsaInfo.deviceNum < 0 ) - { - AFB_WARNING("No Alsa device number available"); - return AHL_FAIL; - } - - // get sub-device number - out_pEndpointInfo->alsaInfo.subDeviceNum = snd_pcm_info_get_subdevice(pPcmInfo); - if ( out_pEndpointInfo->alsaInfo.subDeviceNum < 0 ) - { - AFB_WARNING("No Alsa subdevice number available"); - return AHL_FAIL; - } - - char cardName[32]; - sprintf(cardName, "hw:%d", out_pEndpointInfo->alsaInfo.cardNum); - iAlsaRet = snd_ctl_open(&ctlHandle, cardName, 0); - if ( iAlsaRet < 0 ) - { - AFB_WARNING("Could not open ALSA card control"); - return AHL_FAIL; - } - - iAlsaRet = snd_ctl_card_info(ctlHandle, ctlInfo); - if ( iAlsaRet < 0 ) - { - AFB_WARNING("Could not retrieve ALSA card info"); - snd_ctl_close(ctlHandle); - return AHL_FAIL; - } - - // Populate unique target card name - pCardName = snd_ctl_card_info_get_id(ctlInfo); - if (pCardName == NULL) - { - AFB_WARNING("No Alsa card name available"); - snd_ctl_close(ctlHandle); - return AHL_FAIL; - } - g_strlcpy(out_pEndpointInfo->gsDeviceName,pCardName,AHL_STR_MAX_LENGTH); - - snd_ctl_close(ctlHandle); - - return AHL_SUCCESS; -} - -EndpointInfoT * InitEndpointInfo() -{ - EndpointInfoT * pEndpointInfo = (EndpointInfoT*) malloc(sizeof(EndpointInfoT)); - memset(pEndpointInfo,0,sizeof(EndpointInfoT)); - pEndpointInfo->endpointID = AHL_UNDEFINED; - pEndpointInfo->type = ENDPOINTTYPE_MAXVALUE; - pEndpointInfo->deviceURIType = DEVICEURITYPE_MAXVALUE; - pEndpointInfo->alsaInfo.cardNum = AHL_UNDEFINED; - pEndpointInfo->alsaInfo.deviceNum = AHL_UNDEFINED; - pEndpointInfo->alsaInfo.subDeviceNum = AHL_UNDEFINED; - pEndpointInfo->format.sampleRate = AHL_UNDEFINED; - pEndpointInfo->format.numChannels = AHL_UNDEFINED; - pEndpointInfo->format.sampleType = AHL_FORMAT_UNKNOWN; - // Assigned by device enumeration - pEndpointInfo->gsDeviceName = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); - memset(pEndpointInfo->gsDeviceName,0,AHL_STR_MAX_LENGTH*sizeof(char)); - pEndpointInfo->gsDeviceDomain = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); - memset(pEndpointInfo->gsDeviceDomain,0,AHL_STR_MAX_LENGTH*sizeof(char)); - pEndpointInfo->pRoleName = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); - memset(pEndpointInfo->pRoleName,0,AHL_STR_MAX_LENGTH*sizeof(char)); - pEndpointInfo->gsDeviceURI = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); - memset(pEndpointInfo->gsDeviceURI,0,AHL_STR_MAX_LENGTH*sizeof(char)); - // Assigned by policy initialization - pEndpointInfo->gsDisplayName = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); - memset(pEndpointInfo->gsDisplayName,0,AHL_STR_MAX_LENGTH*sizeof(char)); - pEndpointInfo->gsHALAPIName = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); - memset(pEndpointInfo->gsHALAPIName,0,AHL_STR_MAX_LENGTH*sizeof(char)); - pEndpointInfo->pPropTable = g_hash_table_new(g_str_hash, g_str_equal); - return pEndpointInfo; -} - -void TermEndpointInfo( gpointer data ) -{ - EndpointInfoT * out_pEndpointInfo = (EndpointInfoT *)data; - #define SAFE_FREE(__ptr__) if(__ptr__) g_free(__ptr__); __ptr__ = NULL; - if(out_pEndpointInfo) - { - SAFE_FREE(out_pEndpointInfo->gsDeviceName); - SAFE_FREE(out_pEndpointInfo->gsDeviceDomain); - SAFE_FREE(out_pEndpointInfo->pRoleName); - SAFE_FREE(out_pEndpointInfo->gsDeviceURI); - SAFE_FREE(out_pEndpointInfo->gsHALAPIName); - SAFE_FREE(out_pEndpointInfo->gsDisplayName); - - if (out_pEndpointInfo->pPropTable) { - // Free json_object for all property values - GHashTableIter iter; - gpointer key, value; - g_hash_table_iter_init (&iter, out_pEndpointInfo->pPropTable); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - if (value) - json_object_put(value); - } - g_hash_table_remove_all(out_pEndpointInfo->pPropTable); - g_hash_table_destroy(out_pEndpointInfo->pPropTable); - out_pEndpointInfo->pPropTable = NULL; - } - } -} - -// For a given audio role -int EnumerateDevices(json_object * in_jDeviceArray, char * in_pAudioRole, EndpointTypeT in_deviceType, GPtrArray * out_pEndpointArray) { - - g_assert_nonnull(in_jDeviceArray); - g_assert_nonnull(in_pAudioRole); - int iNumberDevices = json_object_array_length(in_jDeviceArray); - - // Parse and validate list of available devices - for (int i = 0; i < iNumberDevices; i++) - { - char * pDeviceURIDomain = NULL; - char * pFullDeviceURI = NULL; - char * pDeviceURIPCM = NULL; - int err = AHL_SUCCESS; - - json_object * jDevice = json_object_array_get_idx(in_jDeviceArray,i); - if (jDevice == NULL) { - AFB_WARNING("Invalid device array for audiorole:%s -> %s",in_pAudioRole,json_object_to_json_string(in_jDeviceArray)); - continue; - } - // strip domain name from URI - pFullDeviceURI = (char *)json_object_get_string(jDevice); - char * pFullDeviceURICopy = g_strdup(pFullDeviceURI); // strtok is destructive - err = SeparateDomainFromDeviceURI(pFullDeviceURICopy,&pDeviceURIDomain,&pDeviceURIPCM); - if (err) - { - AFB_WARNING("Invalid device URI string -> %s for audiorole:%s",pFullDeviceURICopy,in_pAudioRole); - continue; - } - - EndpointInfoT * pEndpointInfo = InitEndpointInfo(); - g_assert_nonnull(pEndpointInfo); - - // non ALSA URI are simply passed to application (no validation) at this time - // In Non ALSA case devices in config are assumed to be available, if not application can fallback on explicit device selection - g_strlcpy(pEndpointInfo->gsDeviceName,pDeviceURIPCM,AHL_STR_MAX_LENGTH); - g_strlcpy(pEndpointInfo->gsDeviceDomain,pDeviceURIDomain,AHL_STR_MAX_LENGTH); - g_strlcpy(pEndpointInfo->gsDeviceURI,pDeviceURIPCM,AHL_STR_MAX_LENGTH); - g_strlcpy(pEndpointInfo->pRoleName ,in_pAudioRole,AHL_STR_MAX_LENGTH); - - g_free(pFullDeviceURICopy); - pFullDeviceURICopy = NULL; - pDeviceURIDomain = NULL; //Derived from above mem - pDeviceURIPCM = NULL; //Derived from above mem - - if (IsAlsaDomain(pEndpointInfo->gsDeviceDomain)) - { - // TODO: Missing support for loose name matching - // This will require using ALSA hints to get PCM names - // And would iterate over all available devices matching string (possibly all if no filtering is desired for a certain role) - - // Get PCM handle - snd_pcm_t * pPcmHandle = NULL; - snd_pcm_stream_t streamType = in_deviceType == ENDPOINTTYPE_SOURCE ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK; - err = snd_pcm_open(&pPcmHandle, pEndpointInfo->gsDeviceURI, streamType, 0); - if (err < 0) - { - AFB_NOTICE("Alsa PCM device was not found -> %s for audiorole:%s", pEndpointInfo->gsDeviceURI,in_pAudioRole); - continue; - } - - err = FillALSAPCMInfo(pPcmHandle,pEndpointInfo); - if (err) { - AFB_WARNING("Unable to retrieve PCM information for PCM -> %s for audiorole:%s",pEndpointInfo->gsDeviceURI,in_pAudioRole); - snd_pcm_close(pPcmHandle); - continue; - } - - snd_pcm_close(pPcmHandle); - } - else if (IsPulseDomain(pEndpointInfo->gsDeviceDomain)) { - // Pulse domain - // For now display name is device URI directly, could extrapolated using more heuristics or even usins Pulse API later on - pEndpointInfo->deviceURIType = DEVICEURITYPE_NOT_ALSA; - } - else if (IsGStreamerDomain(pEndpointInfo->gsDeviceDomain)){ - // GStreamer domain - // For now display name is device URI directly, could extrapolated using more heuristics or even usins GStreamer API later on - pEndpointInfo->deviceURIType = DEVICEURITYPE_NOT_ALSA; - } - else if (IsExternalDomain(pEndpointInfo->gsDeviceDomain)){ - // External domain - pEndpointInfo->deviceURIType = DEVICEURITYPE_NOT_ALSA; - } - else { - // Unknown domain - AFB_WARNING("Unknown domain in device URI string -> %s for audiorole:%s",pFullDeviceURI,in_pAudioRole); - continue; - } - - pEndpointInfo->endpointID = in_deviceType == ENDPOINTTYPE_SOURCE ? CreateNewSourceID() : CreateNewSinkID(); - pEndpointInfo->type = in_deviceType; - - // add to structure to list of available devices - g_ptr_array_add(out_pEndpointArray, pEndpointInfo); - - } // for all devices - - AFB_DEBUG ("Audio high-level - Enumerate devices done"); - return AHL_SUCCESS; -}
\ No newline at end of file diff --git a/ahl-binding/ahl-json.c b/ahl-binding/ahl-json.c deleted file mode 100644 index b711b43..0000000 --- a/ahl-binding/ahl-json.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright (C) 2017 "Audiokinetic Inc" - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define AFB_BINDING_VERSION 2 -#include <afb/afb-binding.h> -#include "wrap-json.h" -#include <json-c/json.h> -#include <glib.h> -#include "ahl-binding.h" - -static char * DeviceURITypeEnumToStr(DeviceURITypeT in_eDeviceURIType) { - switch(in_eDeviceURIType) { - case DEVICEURITYPE_ALSA_HW: // Alsa hardware device URI - return AHL_DEVICEURITYPE_ALSA_HW; - case DEVICEURITYPE_ALSA_DMIX: // Alsa Dmix device URI (only for playback devices) - return AHL_DEVICEURITYPE_ALSA_DMIX; - case DEVICEURITYPE_ALSA_DSNOOP: // Alsa DSnoop device URI (only for capture devices) - return AHL_DEVICEURITYPE_ALSA_DSNOOP; - case DEVICEURITYPE_ALSA_SOFTVOL: // Alsa softvol device URI - return AHL_DEVICEURITYPE_ALSA_SOFTVOL; - case DEVICEURITYPE_ALSA_PLUG: // Alsa plug device URI - return AHL_DEVICEURITYPE_ALSA_PLUG; - case DEVICEURITYPE_ALSA_OTHER: // Alsa domain URI device of unspecified type - return AHL_DEVICEURITYPE_ALSA_OTHER; - case DEVICEURITYPE_NOT_ALSA: // Unknown (not ALSA domain) - return AHL_DEVICEURITYPE_NOT_ALSA; - default: - return "Unknown"; - } -} - -static char * StreamStateEnumToStr(StreamStateT in_eStreamState) { - switch(in_eStreamState) { - case STREAM_STATE_IDLE: - return AHL_STREAM_STATE_IDLE; - case STREAM_STATE_RUNNING: - return AHL_STREAM_STATE_RUNNING; - case STREAM_STATE_PAUSED: - return AHL_STREAM_STATE_PAUSED; - default: - return "Unknown"; - } -} - -static char * StreamMuteEnumToStr(StreamMuteT in_eStreamMute) { - switch(in_eStreamMute) { - case STREAM_UNMUTED: - return AHL_STREAM_UNMUTED; - case STREAM_MUTED: - return AHL_STREAM_MUTED; - default: - return "Unknown"; - } -} - -static int EndpointPropTableToJSON(GHashTable * pPropTable, json_object **ppProptableJ) -{ - if(pPropTable == NULL) - { - AFB_ERROR("Invalid EndpointPropTableToJSON arguments"); - return AHL_FAIL; - } - - // Create json object for PropTable - *ppProptableJ = json_object_new_array(); - GHashTableIter iter; - gpointer key, value; - g_hash_table_iter_init (&iter, pPropTable); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - if ( key != NULL && value != NULL) { - json_object *pPropertyJ = NULL; - json_object_get(value); - int err = wrap_json_pack(&pPropertyJ, "{s:s,s:o}", - "property_name", (char*)key, - "property_value", value - ); - if(err) - { - AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err)); - return AHL_FAIL; - } - json_object_array_add(*ppProptableJ, pPropertyJ); - } - } - - return AHL_SUCCESS; -} - -int EndpointInfoToJSON(EndpointInfoT * pEndpointInfo, json_object **ppEndpointInfoJ) -{ - if(pEndpointInfo == NULL || pEndpointInfo->pPropTable == NULL) - { - AFB_ERROR("Invalid EndpointInfoToJSON arguments"); - return AHL_FAIL; - } - - json_object * pPropTableJ = NULL; - int err = EndpointPropTableToJSON(pEndpointInfo->pPropTable,&pPropTableJ); - if (err) { - return AHL_FAIL; - } - - // Create json object for EndpointInfo - err = wrap_json_pack(ppEndpointInfoJ, "{s:i,s:i,s:s,s:s,s:s,s:s,s:s,s:i,s:s,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:o}", - "endpoint_id", pEndpointInfo->endpointID, - "endpoint_type", pEndpointInfo->type, - "device_name", pEndpointInfo->gsDeviceName, - "display_name", pEndpointInfo->gsDisplayName, - "device_uri", pEndpointInfo->gsDeviceURI, - "device_domain", pEndpointInfo->gsDeviceDomain, - "audio_role",pEndpointInfo->pRoleName, - "device_uri_type", pEndpointInfo->deviceURIType, - "hal_api_name", pEndpointInfo->gsHALAPIName, - "alsa_cardNum", pEndpointInfo->alsaInfo.cardNum, - "alsa_deviceNum", pEndpointInfo->alsaInfo.deviceNum, - "alsa_subDeviceNum", pEndpointInfo->alsaInfo.subDeviceNum, - "format_samplerate", pEndpointInfo->format.sampleRate, - "format_numchannels", pEndpointInfo->format.numChannels, - "format_sampletype",pEndpointInfo->format.sampleType, - "volume", pEndpointInfo->iVolume, - "property_table", pPropTableJ - ); - if (err) { - AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err)); - return AHL_FAIL; - } - - return AHL_SUCCESS; -} - -int StreamInfoToJSON(StreamInfoT * pStreamInfo, json_object **ppStreamInfoJ) -{ - if(pStreamInfo == NULL) - { - AFB_ERROR("Invalid arguments to StreamInfoToJSON"); - return AHL_FAIL; - } - - json_object * pEndpointInfoJ = NULL; - int err = EndpointInfoToJSON(pStreamInfo->pEndpointInfo, &pEndpointInfoJ); - if (err) { - return AHL_FAIL; - } - - // Create json object for stream - err = wrap_json_pack(ppStreamInfoJ, "{s:i,s:i,s:i,s:I,s:i,s:s,s:i,s:i,s:o}", - "stream_id", pStreamInfo->streamID, - "stream_state", pStreamInfo->streamState, - "stream_mute", pStreamInfo->streamMute, - "stream_state_event", &pStreamInfo->streamStateEvent, - "endpoint_sel_mod", pStreamInfo->endpointSelMode, - "role_name", pStreamInfo->pRoleName, - "priority", pStreamInfo->iPriority, - "interrupt_behavior", pStreamInfo->eInterruptBehavior, - "endpoint_info", pEndpointInfoJ - ); - if (err) { - AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err)); - return AHL_FAIL; - } - - return AHL_SUCCESS; -} - -static int UpdatePropertyList(GHashTable * pPropTable, json_object * pPropTableJ) { - if (pPropTable == NULL || pPropTableJ == NULL) { - AFB_ERROR("Invalid arguments to UpdatePropertyList"); - return AHL_FAIL; - } - // Unpack prop table - int nbProperties = json_object_array_length(pPropTableJ); - for(int i = 0; i < nbProperties; i++) - { - json_object * propJ = json_object_array_get_idx(pPropTableJ,i); - if (propJ) { - char * pPropertyName = NULL; - json_object * pPropertyValueJ = NULL; - int err = wrap_json_unpack(propJ, "{s:s,s:o}", - "property_name", &pPropertyName, - "property_value", &pPropertyValueJ); - if (err) { - AFB_ERROR("Unable to unpack JSON property, = %s", wrap_json_get_error_string(err)); - return AHL_FAIL; - } - - // Object type detection for property value (string = state, numeric = property) - json_type jType = json_object_get_type(pPropertyValueJ); - switch (jType) { - case json_type_double: - g_hash_table_insert(pPropTable, pPropertyName, json_object_new_double(json_object_get_double(pPropertyValueJ))); - break; - case json_type_int: - g_hash_table_insert(pPropTable, pPropertyName, json_object_new_int(json_object_get_int(pPropertyValueJ))); - break; - case json_type_string: - g_hash_table_insert(pPropTable, pPropertyName, json_object_new_string(json_object_get_string(pPropertyValueJ))); - break; - default: - AFB_ERROR("Invalid property argument Property value not a valid json object query=%s", json_object_get_string(pPropertyValueJ)); - return AHL_FAIL; - } - } - } - - return AHL_SUCCESS; -} - -int UpdateEndpointInfo(EndpointInfoT * pEndpoint, json_object * pEndpointInfoJ) { - - if(pEndpoint == NULL || pEndpointInfoJ == NULL) - { - AFB_ERROR("Invalid arguments to UpdateEndpointInfo"); - return AHL_FAIL; - } - - // Push information to endpoint info struct - json_object * pPropTableJ = NULL; - char * pDisplayName = NULL; - char * pHALName = NULL; - int err = wrap_json_unpack(pEndpointInfoJ,"{s:i,s:s,s:s,s:o}", - "init_volume",&pEndpoint->iVolume, - "display_name",&pDisplayName, - "hal_name", &pHALName, - "property_table",&pPropTableJ); - if (err) { - AFB_ERROR("Unable to create Endpoint Json object error:%s ",wrap_json_get_error_string(err)); - return AHL_FAIL; - } - g_strlcpy(pEndpoint->gsDisplayName,pDisplayName,AHL_STR_MAX_LENGTH); - g_strlcpy(pEndpoint->gsHALAPIName,pHALName,AHL_STR_MAX_LENGTH); - - if (pEndpoint->pPropTable && pPropTableJ) { - err = UpdatePropertyList(pEndpoint->pPropTable,pPropTableJ); - if (err) { - AFB_ERROR("Unable to update property table Json object error:%s ",wrap_json_get_error_string(err)); - return AHL_FAIL; - } - } - - return AHL_SUCCESS; -} - -static void AudioFormatStructToJSON(json_object **audioFormatJ, AudioFormatT * pAudioFormat) -{ - wrap_json_pack(audioFormatJ, "{s:i,s:i,s:i}", - "sample_rate", pAudioFormat->sampleRate, - "num_channels", pAudioFormat->numChannels, - "sample_type", pAudioFormat->sampleType); -} - -// Package only information that can useful to application clients when selecting endpoint -void JSONPublicPackageEndpoint(EndpointInfoT * pEndpointInfo,json_object **endpointInfoJ) -{ - json_object *formatInfoJ = NULL; - wrap_json_pack(endpointInfoJ, "{s:i,s:s,s:s,s:s,s:s,s:s,s:s}", - "endpoint_id", pEndpointInfo->endpointID, - "endpoint_type", (pEndpointInfo->type == ENDPOINTTYPE_SOURCE) ? AHL_ENDPOINTTYPE_SOURCE : AHL_ENDPOINTTYPE_SINK, - "device_name", pEndpointInfo->gsDeviceName, - "display_name", pEndpointInfo->gsDisplayName, - "audio_role", pEndpointInfo->pRoleName, - "device_domain",pEndpointInfo->gsDeviceDomain, - "device_uri_type", DeviceURITypeEnumToStr(pEndpointInfo->deviceURIType)); - AudioFormatStructToJSON(&formatInfoJ,&pEndpointInfo->format); - json_object_object_add(*endpointInfoJ,"format",formatInfoJ); - - json_object *pPropTableJ = NULL; - EndpointPropTableToJSON(pEndpointInfo->pPropTable,&pPropTableJ); - json_object_object_add(*endpointInfoJ,"property_table",pPropTableJ); -} - -// Package only information that can useful to application clients when opening a stream -void JSONPublicPackageStream(StreamInfoT * pStreamInfo,json_object **streamInfoJ) -{ - json_object *endpointInfoJ = NULL; - JSONPublicPackageEndpoint(pStreamInfo->pEndpointInfo,&endpointInfoJ); - wrap_json_pack(streamInfoJ, "{s:i,s:s,s:s,s:s}", - "stream_id", pStreamInfo->streamID, - "state", StreamStateEnumToStr(pStreamInfo->streamState), - "mute", StreamMuteEnumToStr(pStreamInfo->streamMute), - "device_uri",pStreamInfo->pEndpointInfo->gsDeviceURI); // Need to open a stream to have access to the device URI - json_object_object_add(*streamInfoJ,"endpoint_info",endpointInfoJ); -}
\ No newline at end of file diff --git a/ahl-binding/ahl-json.h b/ahl-binding/ahl-json.h deleted file mode 100644 index ffd683a..0000000 --- a/ahl-binding/ahl-json.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2017 "Audiokinetic Inc" - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef AHL_POLICY_JSON_INCLUDE -#define AHL_POLICY_JSON_INCLUDE - -int EndpointInfoToJSON(EndpointInfoT * pEndpointInfo, json_object **ppEndpointInfoJ); -int StreamInfoToJSON(StreamInfoT * pStreamInfo, json_object **ppStreamInfoJ); -int UpdateEndpointInfo(EndpointInfoT * pEndpoint,json_object * pEndpointInfoJ); -void JSONPublicPackageEndpoint(EndpointInfoT * pEndpointInfo,json_object **endpointInfoJ); -void JSONPublicPackageStream(StreamInfoT * pStreamInfo,json_object **streamInfoJ); - -#endif // AHL_POLICY_JSON_INCLUDE diff --git a/ahl-binding/config_entry.cpp b/ahl-binding/config_entry.cpp new file mode 100644 index 0000000..65d7a40 --- /dev/null +++ b/ahl-binding/config_entry.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Loïc Collignon <loic.collignon@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config_entry.hpp" + +config_entry_t::config_entry_t(std::string fp, std::string fn) + : fullpath_{fp} + , filename_{fn} +{ +} + +config_entry_t::config_entry_t(struct json_object* j) +{ + jcast(fullpath_, j, "fullpath"); + jcast(filename_, j, "filename"); +} + +config_entry_t& config_entry_t::operator<<(struct json_object* j) +{ + jcast(fullpath_, j, "fullpath"); + jcast(filename_, j, "filename"); + return *this; +} + +std::string config_entry_t::fullpath() const +{ + return fullpath_; +} + +std::string config_entry_t::filename() const +{ + return filename_; +} + +std::string config_entry_t::filepath() const +{ + return fullpath_ + '/' + filename_; +} + +void config_entry_t::fullpath(std::string fp) +{ + fullpath_ = fp; +} + +void config_entry_t::finename(std::string fn) +{ + filename_ = fn; +} diff --git a/ahl-binding/config_entry.hpp b/ahl-binding/config_entry.hpp new file mode 100644 index 0000000..492eed2 --- /dev/null +++ b/ahl-binding/config_entry.hpp @@ -0,0 +1,49 @@ +#pragma once + +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Loïc Collignon <loic.collignon@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jsonc_utils.hpp" + +class config_entry_t +{ +private: + std::string fullpath_; + std::string filename_; + +public: + explicit config_entry_t() = default; + explicit config_entry_t(const config_entry_t&) = default; + explicit config_entry_t(config_entry_t&&) = default; + + ~config_entry_t() = default; + + config_entry_t& operator=(const config_entry_t&) = default; + config_entry_t& operator=(config_entry_t&&) = default; + + explicit config_entry_t(std::string fp, std::string fn); + explicit config_entry_t(struct json_object* j); + config_entry_t& operator<<(struct json_object* j); + + std::string fullpath() const; + std::string filename() const; + std::string filepath() const; + + void fullpath(std::string fp); + void finename(std::string fn); + +}; diff --git a/ahl-binding/interrupt.cpp b/ahl-binding/interrupt.cpp new file mode 100644 index 0000000..fc54139 --- /dev/null +++ b/ahl-binding/interrupt.cpp @@ -0,0 +1,34 @@ +#include "interrupt.hpp" + +interrupt_t::interrupt_t(json_object* o) +{ + jcast(type_, o, "type"); + args_ = json_object_object_get(o, "args"); +} + +interrupt_t& interrupt_t::operator<<(json_object* o) +{ + jcast(type_, o, "type"); + args_ = json_object_object_get(o, "args"); + return *this; +} + +std::string interrupt_t::type() const +{ + return type_; +} + +json_object* interrupt_t::args() const +{ + return args_; +} + +void interrupt_t::type(std::string v) +{ + type_ = v; +} + +void interrupt_t::args(json_object* v) +{ + args_ = v; +} diff --git a/ahl-binding/interrupt.hpp b/ahl-binding/interrupt.hpp new file mode 100644 index 0000000..caf5cda --- /dev/null +++ b/ahl-binding/interrupt.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "jsonc_utils.hpp" + +class interrupt_t +{ +private: + std::string type_; + json_object* args_; + +public: + explicit interrupt_t() = default; + explicit interrupt_t(const interrupt_t&) = default; + explicit interrupt_t(interrupt_t&&) = default; + ~interrupt_t() = default; + + interrupt_t& operator=(const interrupt_t&) = default; + interrupt_t& operator=(interrupt_t&&) = default; + + explicit interrupt_t(json_object* o); + interrupt_t& operator<<(json_object* o); + + std::string type() const; + json_object* args() const; + + void type(std::string v); + void args(json_object* v); +}; diff --git a/ahl-binding/jsonc_utils.hpp b/ahl-binding/jsonc_utils.hpp new file mode 100644 index 0000000..5e82fca --- /dev/null +++ b/ahl-binding/jsonc_utils.hpp @@ -0,0 +1,91 @@ +#pragma once + +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Loïc Collignon <loic.collignon@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <string> +#include <vector> +#include <json-c/json.h> + +template<class T> +inline T& jcast(T& v, json_object* o) +{ + v << o; + return v; +} + +template<class T> +inline T& jcast_array(T& v, json_object* o) +{ + if (o == nullptr) return v; + auto sz = json_object_array_length(o); + for(auto i = 0; i < sz ; ++i) + { + typename T::value_type item; + jcast(item, json_object_array_get_idx(o, i)); + v.push_back(item); + } + return v; +} + +template<> +inline bool& jcast<bool>(bool& v, json_object* o) +{ + v = (json_object_get_boolean(o) == TRUE); + return v; +} + +template<> +inline double& jcast<double>(double& v, json_object* o) +{ + v = json_object_get_double(o); + return v; +} + +template<> +inline int32_t& jcast<int32_t>(int32_t& v, json_object* o) +{ + v = json_object_get_int(o); + return v; +} + +template<> +inline int64_t& jcast<int64_t>(int64_t& v, json_object* o) +{ + v = json_object_get_int(o); + return v; +} + +template<> +inline std::string& jcast<std::string>(std::string& v, json_object* o) +{ + const char* tmp = json_object_get_string(o); + v = tmp ? tmp : ""; + return v; +} + +template<class T> +inline T& jcast(T& v, json_object* o, std::string field) +{ + return jcast<T>(v, json_object_object_get(o, field.c_str())); +} + +template<class T> +inline T& jcast_array(T& v, json_object* o, std::string field) +{ + return jcast_array<T>(v, json_object_object_get(o, field.c_str())); +} diff --git a/ahl-binding/role.cpp b/ahl-binding/role.cpp new file mode 100644 index 0000000..2be3dc5 --- /dev/null +++ b/ahl-binding/role.cpp @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Loïc Collignon <loic.collignon@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "role.hpp" +#include "jsonc_utils.hpp" +#include "ahl-binding.hpp" + +role_t::role_t(json_object* j) +{ + jcast(uid_, j, "uid"); + jcast(description_, j, "description"); + jcast(priority_, j, "priority"); + jcast(stream_, j, "stream"); + jcast_array(interrupts_, j, "interrupts"); + opened_ = false; +} + +role_t& role_t::operator<<(json_object* j) +{ + jcast(uid_, j, "uid"); + jcast(description_, j, "description"); + jcast(priority_, j, "priority"); + jcast(stream_, j, "stream"); + jcast_array(interrupts_, j, "interrupts"); + return *this; +} + +std::string role_t::uid() const +{ + return uid_; +} + +std::string role_t::description() const +{ + return description_; +} + +std::string role_t::hal() const +{ + return hal_; +} + +std::string role_t::stream() const +{ + return stream_; +} + +int role_t::priority() const +{ + return priority_; +} + +std::string role_t::device_uri() const +{ + return device_uri_; +} + +bool role_t::opened() const +{ + return opened_; +} + +void role_t::uid(std::string v) +{ + uid_ = v; +} + +void role_t::description(std::string v) +{ + description_ = v; +} + +void role_t::hal(std::string v) +{ + hal_ = v; +} + +void role_t::stream(std::string v) +{ + stream_ = v; +} + +void role_t::priority(int v) +{ + priority_ = v; +} + +void role_t::device_uri(std::string v) +{ + device_uri_ = v; +} + +const std::vector<interrupt_t>& role_t::interrupts() const +{ + return interrupts_; +} + +int role_t::apply_policy(afb_request* req) +{ + if(interrupts_.size()) + { + const interrupt_t& i = interrupts_[0]; + /*if (i.type() == "mute") + { + } + else if (i.type() == "continue") + { + } + else if (i.type() == "cancel") + { + } + else */if (i.type() == "ramp") + { + for(const auto& r: ahl_binding_t::instance().roles()) + { + if (r.opened() && priority_ > r.priority()) + { + // { "ramp" : { "uid" : "ramp-slow", "volume" : 30 } } + json_object* arg = json_object_new_object(); + json_object_object_add(arg, "ramp", i.args()); + json_object_get(i.args()); + json_object* result = nullptr; + + AFB_DYNAPI_NOTICE(ahl_binding_t::instance().handle(), + "Call '%s'/'%s' '%s", + r.hal().c_str(), r.stream().c_str(), json_object_to_json_string(arg)); + + if(afb_dynapi_call_sync(ahl_binding_t::instance().handle(), r.hal().c_str(), r.stream().c_str(), arg, &result)) + { + afb_request_fail(req, "Failed to call 'ramp' action on stream", nullptr); + return -1; + } + AFB_DYNAPI_NOTICE(ahl_binding_t::instance().handle(), + "POLICY: Applying a ramp to '%s' stream because '%s' is opened and have higher priority!", + r.stream().c_str(), stream_.c_str()); + } + } + } + else + { + afb_request_fail(req, "Unkown interrupt uid!", nullptr); + return -1; + } + } + return 0; +} + +void role_t::invoke(afb_request* req) +{ + json_object* arg = afb_request_json(req); + if (arg == nullptr) + { + afb_request_fail(req, "No valid argument!", nullptr); + return; + } + + json_object* jaction = json_object_object_get(arg, "action"); + if (jaction == nullptr) + { + afb_request_fail(req, "No valid action!", nullptr); + return; + } + + std::string action = json_object_get_string(jaction); + if (action.size() == 0) + { + afb_request_fail(req, "No valid action!", nullptr); + return; + } + + if (action == "open") open(req, arg); + else if (action == "close") close(req, arg); + else if (action == "volume") volume(req, arg); + else if (action == "interrupt") interrupt(req, arg); + else afb_request_fail(req, "Unknown action!", nullptr); +} + +void role_t::open(afb_request* r, json_object* o) +{ + if (opened_) + { + afb_request_fail(r, "Already opened!", nullptr); + return; + } + + if (!apply_policy(r)) + { + afb_request_context_set(r, this, nullptr); + opened_ = true; + + json_object* result = json_object_new_object(); + json_object_object_add(result, "device_uri", json_object_new_string(device_uri_.c_str())); + + afb_request_success(r, result, nullptr); + } +} + +void role_t::close(afb_request* r, json_object* o) +{ + if (!opened_) + { + afb_request_success(r, nullptr, "Already closed!"); + return; + } + + if(!afb_request_context_get(r)) + { + afb_request_fail(r, "Stream is opened by another client!", nullptr); + return; + } + + opened_ = false; + afb_request_success(r, nullptr, "Stream closed!"); +} + +void role_t::volume(afb_request* r, json_object* o) +{ + if(!afb_request_has_permission(r, "urn:AGL:permission::public:4a-audio-mixer")) + { + if (!opened_) + { + afb_request_fail(r, "You have to open the stream first!", nullptr); + return; + } + + if(!afb_request_context_get(r)) + { + afb_request_fail(r, "Stream is opened by another client!", nullptr); + return; + } + } + else + { + AFB_DYNAPI_NOTICE(ahl_binding_t::instance().handle(), "Granted special audio-mixer permission to change volume"); + } + + json_object* value = json_object_object_get(o, "value"); + if(!value) + { + afb_request_fail(r, "No value given!", nullptr); + return; + } + + json_object_get(value); + + json_object* a = json_object_new_object(); + json_object_object_add(a, "volume", value); + + afb_dynapi_call( + r->dynapi, + hal_.c_str(), + stream_.c_str(), + a, + [](void* closure, int status, json_object* result, afb_dynapi* handle) + { + AFB_DYNAPI_DEBUG(handle, "Got the following answer: %s", json_object_to_json_string(result)); + afb_request* r = (afb_request*)closure; + + json_object_get(result); + if (status) afb_request_fail(r, json_object_to_json_string(result), nullptr); + else afb_request_success(r, result, nullptr); + afb_request_unref(r); + }, + afb_request_addref(r)); +} + +void role_t::interrupt(afb_request* r, json_object* o) +{ +} diff --git a/ahl-binding/role.hpp b/ahl-binding/role.hpp new file mode 100644 index 0000000..1469b68 --- /dev/null +++ b/ahl-binding/role.hpp @@ -0,0 +1,79 @@ +#pragma once + +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Loïc Collignon <loic.collignon@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <vector> +#include "interrupt.hpp" + +struct afb_request; + +class role_t +{ +private: + // Members filled by config + std::string uid_; + std::string description_; + std::string hal_; + std::string stream_; + int priority_; + std::vector<interrupt_t> interrupts_; + + std::string device_uri_; + bool opened_; + + int apply_policy(afb_request* req); + +public: + explicit role_t() = default; + explicit role_t(const role_t&) = default; + explicit role_t(role_t&&) = default; + + ~role_t() = default; + + role_t& operator=(const role_t&) = default; + role_t& operator=(role_t&&) = default; + + static role_t from_json(json_object* o); + + explicit role_t(json_object* j); + + role_t& operator<<(json_object* j); + + std::string uid() const; + std::string description() const; + std::string hal() const; + std::string stream() const; + int priority() const; + const std::vector<interrupt_t>& interrupts() const; + std::string device_uri() const; + bool opened() const; + + void uid(std::string v); + void description(std::string v); + void hal(std::string v); + void stream(std::string v); + void device_uri(std::string v); + void priority(int v); + + void invoke(afb_request* r); + + void open(afb_request* r, json_object* o); + void close(afb_request* r, json_object* o); + void volume(afb_request* r, json_object* o); + void interrupt(afb_request* r, json_object* o); +}; diff --git a/ahl-policy/CMakeLists.txt b/ahl-policy/CMakeLists.txt index 346669d..4446e5e 100644 --- a/ahl-policy/CMakeLists.txt +++ b/ahl-policy/CMakeLists.txt @@ -18,28 +18,28 @@ # Add target to project dependency list -PROJECT_TARGET_ADD(ahl-policy) +#PROJECT_TARGET_ADD(ahl-policy) # Define project Targets - ADD_LIBRARY(${TARGET_NAME} STATIC ahl-policy.c) + # ADD_LIBRARY(${TARGET_NAME} STATIC ahl-policy.c) - if($ENV{AK_DEMO}) - add_definitions(-DAK_POLICY_DEMO) - endif() + #if($ENV{AK_DEMO}) + #add_definitions(-DAK_POLICY_DEMO) + #endif() # Define target includes - TARGET_INCLUDE_DIRECTORIES(${TARGET_NAME} - PUBLIC ${GLIB_PKG_INCLUDE_DIRS} - ) + #TARGET_INCLUDE_DIRECTORIES(${TARGET_NAME} + # PUBLIC ${GLIB_PKG_INCLUDE_DIRS} + #) # Library dependencies (include updates automatically) - TARGET_LINK_LIBRARIES(${TARGET_NAME} - ahl-utilities - afb-utilities - ) + #TARGET_LINK_LIBRARIES(${TARGET_NAME} + # ahl-utilities + # afb-utilities + #) # Define target includes for this target client - TARGET_INCLUDE_DIRECTORIES(${TARGET_NAME} - PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} - ) + #TARGET_INCLUDE_DIRECTORIES(${TARGET_NAME} + # PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + #) diff --git a/ahl-utilities/ahl-policy-utils.c b/ahl-utilities/ahl-policy-utils.c deleted file mode 100755 index 41e203e..0000000 --- a/ahl-utilities/ahl-policy-utils.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2017 "Audiokinetic Inc" - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define AFB_BINDING_VERSION 2 -#include <afb/afb-binding.h> - -#include "ahl-policy-utils.h" -#include "wrap-json.h" -#include <json-c/json.h> - -void Add_Endpoint_Property_Double( json_object * io_pPropertyArray, char * in_pPropertyName, double in_dPropertyValue) -{ - json_object * pPropertyJ = NULL; - wrap_json_pack(&pPropertyJ, "{s:s,s:o}", - "property_name", in_pPropertyName, - "property_value", json_object_new_double(in_dPropertyValue) - ); - json_object_array_add(io_pPropertyArray, pPropertyJ); -} - -void Add_Endpoint_Property_Int( json_object * io_pPropertyArray, char * in_pPropertyName, int in_iPropertyValue) -{ - json_object * pPropertyJ = NULL; - wrap_json_pack(&pPropertyJ, "{s:s,s:o}", - "property_name", in_pPropertyName, - "property_value", json_object_new_int(in_iPropertyValue) - ); - json_object_array_add(io_pPropertyArray, pPropertyJ); -} - -void Add_Endpoint_Property_String( json_object * io_pPropertyArray, char * in_pPropertyName, const char * in_pPropertyValue) -{ - json_object * pPropertyJ = NULL; - wrap_json_pack(&pPropertyJ, "{s:s,s:o}", - "property_name", in_pPropertyName, - "property_value", json_object_new_string(in_pPropertyValue) - ); - json_object_array_add(io_pPropertyArray, pPropertyJ); -} - -int EndpointToJSON(EndPointInterfaceInfoT * pEndpoint, json_object **ppEndpointJ) -{ - if(ppEndpointJ == NULL || pEndpoint == NULL) - { - AFB_ERROR("Invalid EndpointToJSON arguments"); - return AHL_POLICY_UTIL_FAIL; - } - - // Create json object for Endpoint - int err = wrap_json_pack(ppEndpointJ, "{s:i,s:i,s:s,s:s,s:s,s:s,s:s,s:i,s:s,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s?o}", - "endpoint_id", pEndpoint->endpointID, - "endpoint_type", pEndpoint->type, - "device_name", pEndpoint->gsDeviceName, - "display_name", pEndpoint->gsDisplayName, - "device_uri", pEndpoint->gsDeviceURI, - "device_domain", pEndpoint->gsDeviceDomain, - "audio_role",pEndpoint->pRoleName, - "device_uri_type", pEndpoint->deviceURIType, - "hal_api_name", pEndpoint->gsHALAPIName, - "alsa_cardNum", pEndpoint->alsaInfo.cardNum, - "alsa_deviceNum", pEndpoint->alsaInfo.deviceNum, - "alsa_subDeviceNum", pEndpoint->alsaInfo.subDeviceNum, - "format_samplerate", pEndpoint->format.sampleRate, - "format_numchannels", pEndpoint->format.numChannels, - "format_sampletype",pEndpoint->format.sampleType, - "volume", pEndpoint->iVolume, - "property_table", pEndpoint->pPropTableJ - ); - if (err) { - AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err)); - return AHL_POLICY_UTIL_FAIL; - } - AFB_DEBUG("JSON endpoint information=%s", json_object_get_string(*ppEndpointJ)); - return AHL_POLICY_UTIL_SUCCESS; -} - -int StreamToJSON(StreamInterfaceInfoT * pStream, json_object **ppStreamJ) -{ - if(pStream == NULL) - { - AFB_ERROR("Invalid arguments to StreamToJSON, stream structure is NULL"); - return AHL_POLICY_UTIL_FAIL; - } - - json_object *EndpointJ = NULL; - int err = EndpointToJSON(&pStream->endpoint, &EndpointJ); - if (err) { - AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err)); - return AHL_POLICY_UTIL_FAIL; - } - - // Create json object for stream - err = wrap_json_pack(ppStreamJ, "{s:i,s:i,s:i,s:s,s:i,s:i,s:o}", - "stream_id", pStream->streamID, - "stream_state", pStream->streamState, - "stream_mute", pStream->streamMute, - "role_name", pStream->pRoleName, - "priority", pStream->iPriority, - "interrupt_behavior", pStream->eInterruptBehavior, - "endpoint_info", EndpointJ - ); - if (err) { - AFB_ERROR("Unable to pack JSON Stream, =%s", wrap_json_get_error_string(err)); - return AHL_POLICY_UTIL_FAIL; - } - - AFB_DEBUG("JSON stream information=%s", json_object_get_string(*ppStreamJ)); - - return AHL_POLICY_UTIL_SUCCESS; -} - -//pEndpointInterfaceInfo must be pre-allocated by the caller -int JSONToEndpoint(json_object *pEndpointJ, EndPointInterfaceInfoT *pEndpoint) -{ - if(pEndpointJ == NULL || pEndpoint == NULL) - { - AFB_ERROR("Invalid arguments for JSONToEndpoint"); - return AHL_POLICY_UTIL_FAIL; - } - - //Unpack Endpoint - int err = wrap_json_unpack(pEndpointJ, "{s:i,s:i,s:s,s:s,s:s,s:s,s:s,s:i,s:s,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s?o}", - "endpoint_id", &pEndpoint->endpointID, - "endpoint_type", &pEndpoint->type, - "device_name", &pEndpoint->gsDeviceName, - "display_name", &pEndpoint->gsDisplayName, - "device_uri", &pEndpoint->gsDeviceURI, - "device_domain", &pEndpoint->gsDeviceDomain, - "audio_role", &pEndpoint->pRoleName, - "device_uri_type", &pEndpoint->deviceURIType, - "hal_api_name", &pEndpoint->gsHALAPIName, - "alsa_cardNum", &pEndpoint->alsaInfo.cardNum, - "alsa_deviceNum", &pEndpoint->alsaInfo.deviceNum, - "alsa_subDeviceNum", &pEndpoint->alsaInfo.subDeviceNum, - "format_samplerate", &pEndpoint->format.sampleRate, - "format_numchannels", &pEndpoint->format.numChannels, - "format_sampletype",&pEndpoint->format.sampleType, - "volume", &pEndpoint->iVolume, - "property_table", &pEndpoint->pPropTableJ - ); - if (err) { - AFB_ERROR("Unable to unpack JSON endpoint, =%s", wrap_json_get_error_string(err)); - return AHL_POLICY_UTIL_FAIL; - } - return AHL_POLICY_UTIL_SUCCESS; -} - -int JSONToStream(json_object *pStreamJ, StreamInterfaceInfoT * pStream) -{ - if(pStreamJ == NULL || pStream == NULL) - { - AFB_ERROR("Invalid arguments for InterfaceCtxJSONToStream"); - return AHL_POLICY_UTIL_FAIL; - } - - //Unpack StreamInfo - json_object *pEndpointJ = NULL; - int err = wrap_json_unpack(pStreamJ, "{s:i,s:i,s:i,s:s,s:i,s:i,s:o}", - "stream_id", &pStream->streamID, - "stream_state", &pStream->streamState, - "stream_mute", &pStream->streamMute, - "role_name", &pStream->pRoleName, - "priority", &pStream->iPriority, - "interrupt_behavior", &pStream->eInterruptBehavior, - "endpoint_info", &pEndpointJ - ); - - if (err) { - AFB_ERROR("Unable to parse JSON stream information=%s", json_object_get_string(pStreamJ)); - return AHL_POLICY_UTIL_FAIL; - } - - err = JSONToEndpoint(pEndpointJ,&pStream->endpoint); - if (err) { - return AHL_POLICY_UTIL_FAIL; - } - return AHL_POLICY_UTIL_SUCCESS; -} -
\ No newline at end of file diff --git a/ahl-utilities/ahl-policy-utils.h b/ahl-utilities/ahl-policy-utils.h deleted file mode 100755 index 3c20020..0000000 --- a/ahl-utilities/ahl-policy-utils.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2017 "Audiokinetic Inc" - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef AHL_POLICY_UTILS_INCLUDE -#define AHL_POLICY_UTILS_INCLUDE - -#include <json-c/json.h> - -#define AHL_POLICY_ACCEPT 1 -#define AHL_POLICY_REJECT 0 -#define AHL_POLICY_UTIL_SUCCESS 0 -#define AHL_POLICY_UTIL_FAIL 1 - -#define AHL_UNDEFINED -1 - -typedef int endpointID_t; -typedef int streamID_t; - -typedef enum StreamEvent { - STREAM_EVENT_START = 0, // Stream is inactive - STREAM_EVENT_STOP, // Stream is running - STREAM_EVENT_PAUSE, // Audio stream paused - STREAM_EVENT_RESUME, // Audio stream resumed - STREAM_EVENT_MUTED, // Audio stream muted - STREAM_EVENT_UNMUTED, // Audio stream unmuted - STREAM_EVENT_MAXVALUE // Enum count, keep at the end -} StreamEventT; - -typedef enum StreamMute { - STREAM_UNMUTED = 0, // Stream is not muted - STREAM_MUTED, // Stream is muted - STREAM_MUTE_MAXVALUE, // Enum count, keep at the end -} StreamMuteT; - -typedef enum EndpointType { - ENDPOINTTYPE_SOURCE = 0, // source devices - ENDPOINTTYPE_SINK, // sink devices - ENDPOINTTYPE_MAXVALUE // Enum count, keep at the end -} EndpointTypeT; - -typedef enum StreamState { - STREAM_STATE_IDLE = 0, // Stream is inactive - STREAM_STATE_RUNNING, // Stream is active and running - STREAM_STATE_PAUSED, // Stream is active but paused - STREAM_STATE_MAXVALUE // Enum count, keep at the end -} StreamStateT; - -// Define default behavior of audio role when interrupting lower priority sources -typedef enum InterruptBehavior { - INTERRUPTBEHAVIOR_CONTINUE = 0, // Continue to play lower priority source when interrupted (e.g. media may be ducked) - INTERRUPTBEHAVIOR_CANCEL, // Abort playback of lower priority source when interrupted (e.g. non-important HMI feedback that does not make sense later) - INTERRUPTBEHAVIOR_PAUSE, // Pause lower priority source when interrupted, to be resumed afterwards (e.g. non-temporal guidance) - INTERRUPTBEHAVIOR_MAXVALUE, // Enum count, keep at the end -} InterruptBehaviorT; - -typedef enum DeviceURIType { - DEVICEURITYPE_ALSA_HW = 0, // Alsa hardware device URI - DEVICEURITYPE_ALSA_DMIX, // Alsa Dmix device URI (only for playback devices) - DEVICEURITYPE_ALSA_DSNOOP, // Alsa DSnoop device URI (only for capture devices) - DEVICEURITYPE_ALSA_SOFTVOL, // Alsa softvol device URI - DEVICEURITYPE_ALSA_PLUG, // Alsa plug device URI - DEVICEURITYPE_ALSA_OTHER, // Alsa domain URI device of unspecified type - DEVICEURITYPE_NOT_ALSA, // Unknown (not ALSA domain) - DEVICEURITYPE_MAXVALUE // Enum count, keep at the end -} DeviceURITypeT; - -// CPU endianness assumed in all formats -typedef enum SampleType { - AHL_FORMAT_UNKNOWN = -1, // Unknown - AHL_FORMAT_U8 = 0, // Unsigned 8 bit - AHL_FORMAT_S16, // Signed 16 bit Little Endian - AHL_FORMAT_S24, // Signed 24 bit Little Endian using low three bytes in 32-bit word - AHL_FORMAT_S32, // Signed 32 bit Little Endian - AHL_FORMAT_FLOAT, // Float 32 bit Little Endian, Range -1.0 to 1.0 - AHL_FORMAT_FLOAT64, // Float 64 bit Little Endian, Range -1.0 to 1.0 - AHL_FORMAT_IEC958, // IEC-958 Little Endian (SPDIF) - AHL_FORMAT_MU_LAW, // Mu-Law - AHL_FORMAT_A_LAW, // A-Law - AHL_FORMAT_IMA_ADPCM, // Ima-ADPCM - AHL_FORMAT_MPEG, // MPEG - AHL_FORMAT_GSM, // GSM - AHL_FORMAT_G723, // G723 - AHL_FORMAT_DSD, // Direct stream digital - AHL_FORMAT_MAXVALUE, // Enum count, keep at the end -} SampleTypeT; - -typedef struct AudioFormat { - int sampleRate; // Sample rate - int numChannels; // Number of channels - SampleTypeT sampleType; // Sample type - // TODO: Interleaving? - // TODO: Sample sub format? -} AudioFormatT; - -typedef struct AlsaDeviceInfo { - int cardNum; // HW card number - int deviceNum; // HW device number - int subDeviceNum; // HW sub device number -} AlsaDeviceInfoT; - -typedef enum EndpointSelectionMode { - ENDPOINTSELMODE_AUTO = 0, // Automatic endpoint selection based on config priority - ENDPOINTSELMODE_MANUAL, // Explicit endpoint selection - ENDPOINTSELMODEMAXVALUE, // Enum count, keep at the end -} EndpointSelectionModeT; - -typedef struct EndPointInterfaceInfo { - endpointID_t endpointID; // Unique endpoint ID (per type) - EndpointTypeT type; // Source or sink device - char * gsDeviceName; // Unique device card name - char * gsDisplayName; // Application display name - char * gsDeviceURI; // Associated URI - char * gsDeviceDomain; // Device URI domain (e.g. alsa or pulse) - DeviceURITypeT deviceURIType; // Device URI type (includes audio domain information) - char * gsHALAPIName; // HAL associated with the device (for volume control) - char * pRoleName; // Role string identifier (from role config but could be programatically overriden later) - AlsaDeviceInfoT alsaInfo; // ALSA specific device information - AudioFormatT format; // Preferred audio format supported (later could be array of supported formats) - int iVolume; // Storage for current endpoint volume (policy effected). - json_object *pPropTableJ; //Property Table -} EndPointInterfaceInfoT; - -typedef struct StreamInterfaceInfo { - streamID_t streamID; // Stream unique ID - StreamStateT streamState; // Stream activity state - StreamMuteT streamMute; // Stream mute state - char * pRoleName; // Role string identifier (from role config but could be programatically overriden later) - int iPriority; // Role normalized priority (0-100) (from role config but could be programatically overriden later) - InterruptBehaviorT eInterruptBehavior; // Role behavior when interrupting lower priority streams (from role config but could be programatically overriden later) - EndPointInterfaceInfoT endpoint; -} StreamInterfaceInfoT; - -void Add_Endpoint_Property_Double( json_object * io_pPropertyArray, char * in_pPropertyName, double in_dPropertyValue); -void Add_Endpoint_Property_Int( json_object * io_pPropertyArray, char * in_pPropertyName, int in_iPropertyValue); -void Add_Endpoint_Property_String( json_object * io_pPropertyArray, char * in_pPropertyName, const char * in_pPropertyValue); -int F(EndPointInterfaceInfoT * pEndpoint, json_object **ppEndpointJ); -int JSONToEndpoint(json_object *pEndpointJ, EndPointInterfaceInfoT * pStream); -int StreamToJSON(StreamInterfaceInfoT * pPolicyStream, json_object **ppStreamJ); -int JSONToStream(json_object *pStreamJ, StreamInterfaceInfoT * pPolicyStream); - -#endif // AHL_POLICY_UTILS_INCLUDE diff --git a/conf.d/CMakeLists.txt b/conf.d/CMakeLists.txt index 28a0609..0d82354 100644 --- a/conf.d/CMakeLists.txt +++ b/conf.d/CMakeLists.txt @@ -1,7 +1,7 @@ ########################################################################### -# Copyright 2015, 2016, 2017 IoT.bzh +# Copyright 2018 IoT.bzh # -# author: Fulup Ar Foll <fulup@iot.bzh> +# author: Loïc Collignon <loic.collignon@iot.bzh> # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,7 +16,4 @@ # limitations under the License. ########################################################################### - -# Include any directory not starting with _ -# ----------------------------------------------------- PROJECT_SUBDIRS_ADD(${PROJECT_SRC_DIR_PATTERN}) diff --git a/conf.d/app-templates b/conf.d/app-templates -Subproject 8c2b05967a3237e624a2cc78e13fcd1c5e72991 +Subproject 1f2944eea3a418ec02920673a390ed4b5d417a2 diff --git a/conf.d/autobuild/agl/autobuild b/conf.d/autobuild/agl/autobuild index 3a1ba5f..83097ab 100755 --- a/conf.d/autobuild/agl/autobuild +++ b/conf.d/autobuild/agl/autobuild @@ -55,7 +55,7 @@ package: build @mkdir -p ${BUILD_DIR}/$@/etc @mkdir -p ${BUILD_DIR}/$@/lib @mkdir -p ${BUILD_DIR}/$@/htdocs - @mkdir -p ${BUILD_DIR}/$@/data + @mkdir -p ${BUILD_DIR}/$@/var @cmake --build ${BUILD_DIR} --target widget @mkdir -p ${DEST} && cp ${BUILD_DIR}/*wgt ${DEST} diff --git a/conf.d/autobuild/linux/autobuild b/conf.d/autobuild/linux/autobuild index 3a1ba5f..83097ab 100755 --- a/conf.d/autobuild/linux/autobuild +++ b/conf.d/autobuild/linux/autobuild @@ -55,7 +55,7 @@ package: build @mkdir -p ${BUILD_DIR}/$@/etc @mkdir -p ${BUILD_DIR}/$@/lib @mkdir -p ${BUILD_DIR}/$@/htdocs - @mkdir -p ${BUILD_DIR}/$@/data + @mkdir -p ${BUILD_DIR}/$@/var @cmake --build ${BUILD_DIR} --target widget @mkdir -p ${DEST} && cp ${BUILD_DIR}/*wgt ${DEST} diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake index b89c5ed..6c0afb0 100644 --- a/conf.d/cmake/config.cmake +++ b/conf.d/cmake/config.cmake @@ -1,7 +1,7 @@ ########################################################################### -# Copyright 2017 Audiokinetic +# Copyright 2018 IoT.bzh # -# author: Tai Vuong <tvuong@audiokinetic.com> +# author: Loïc Collignon <loic.collignon@iot.bzh> # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -23,8 +23,8 @@ set(PROJECT_VERSION "1.0") set(PROJECT_PRETTY_NAME "Audio High Level Binding") set(PROJECT_DESCRIPTION "AGL High Level Interface for Audio") set(PROJECT_ICON "icon.png") -set(PROJECT_AUTHOR "Tai, Vuong") -set(PROJECT_AUTHOR_MAIL "tvuong@audiokinetic.com") +set(PROJECT_AUTHOR "Collignon, Loïc") +set(PROJECT_AUTHOR_MAIL "loic.collignon@iot.bzh") set(PROJECT_LICENCE "APL2.0") set(PROJECT_LANGUAGES,"C") @@ -37,12 +37,18 @@ set(PROJECT_SRC_DIR_PATTERN "[^_]*") # Compilation Mode (DEBUG, RELEASE) # ---------------------------------- -set(CMAKE_BUILD_TYPE "DEBUG") +# Set a default build type if none was specified +set(default_build_type "Debug") +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to '${default_build_type}' as none was specified.") + set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE) + # Set the possible values of build type for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") +endif() # Alsa does not really like libEfence set(USE_EFENCE 0) - # Compiler selection if needed. Overload the detected compiler. # ----------------------------------------------- set (gcc_minimal_version 4.9) @@ -63,6 +69,7 @@ set (PKG_REQUIRED_LIST json-c libafbwsc glib-2.0 + lua>=5.3 ) # Compilation options definition @@ -88,7 +95,10 @@ set(COMPILE_OPTIONS -DMAX_LINEAR_DB_SCALE=24 # until 24db volume normalisation use a simple linear scale -DTLV_BYTE_SIZE=256 # Alsa use 4096 as default but 256 should fit most sndcards -DCONTROL_MAXPATH_LEN=255 --DCONTROL_CONFIG_PATH="${CMAKE_SOURCE_DIR}/conf.d/project:${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}" +-DCONTROL_CONFIG_PATH="${CMAKE_CURRENT_BINARY_DIR}/package/etc:${CMAKE_SOURCE_DIR}/conf.d/project:${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}:/var/local/lib/afm/applications/${PROJECT_NAME}/${PROJECT_VERSION}/etc" +-DCONTROL_PLUGIN_PATH="${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/plugins/lib:${CMAKE_BINARY_DIR}/package/lib/plugins:/var/local/lib/afm/applications/${PROJECT_NAME}/${PROJECT_VERSION}/lib/plugins" +-DCONTROL_LUA_PATH="${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/plugins/lua:${CMAKE_BINARY_DIR}/package/data:/var/local/lib/afm/applications/${PROJECT_NAME}/${PROJECT_VERSION}/var" +-DUSE_API_DYN=1 CACHE STRING "Compilation flags") #set(C_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C language.") #set(CXX_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C++ language.") @@ -96,6 +106,7 @@ set(COMPILE_OPTIONS #set(DEBUG_COMPILE_OPTIONS -g -ggdb -Wp,-U_FORTIFY_SOURCE CACHE STRING "Compilation flags for DEBUG build type.") #set(CCOV_COMPILE_OPTIONS -g -O2 --coverage CACHE STRING "Compilation flags for CCOV build type.") #set(RELEASE_COMPILE_OPTIONS -g -O2 CACHE STRING "Compilation flags for RELEASE build type.") +#include_directories(${CMAKE_SOURCE_DIR}/controller/ctl-lib ${CMAKE_SOURCE_DIR}/afb-utilities) # Print a helper message when every thing is finished # ---------------------------------------------------- diff --git a/ahl-utilities/CMakeLists.txt b/conf.d/project/CMakeLists.txt index 1ef3bfa..0d82354 100644 --- a/ahl-utilities/CMakeLists.txt +++ b/conf.d/project/CMakeLists.txt @@ -1,7 +1,7 @@ ########################################################################### -# Copyright 2015, 2016, 2017 IoT.bzh +# Copyright 2018 IoT.bzh # -# author: Fulup Ar Foll <fulup@iot.bzh> +# author: Loïc Collignon <loic.collignon@iot.bzh> # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,24 +16,4 @@ # limitations under the License. ########################################################################### - -# Add target to project dependency list -PROJECT_TARGET_ADD(ahl-utilities) - - # Define project Targets - ADD_LIBRARY(${TARGET_NAME} STATIC ahl-policy-utils.c) - - # Define target includes - TARGET_INCLUDE_DIRECTORIES(${TARGET_NAME} - PUBLIC ${GLIB_PKG_INCLUDE_DIRS} - ) - - # Library dependencies (include updates automatically) - TARGET_LINK_LIBRARIES(${TARGET_NAME} - afb-utilities - ) - - # Define target includes for this target client - TARGET_INCLUDE_DIRECTORIES(${TARGET_NAME} - PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} - ) +PROJECT_SUBDIRS_ADD(${PROJECT_SRC_DIR_PATTERN}) diff --git a/conf.d/project/README.md b/conf.d/project/README.md deleted file mode 100644 index 274bde3..0000000 --- a/conf.d/project/README.md +++ /dev/null @@ -1,118 +0,0 @@ ------------------------------------------------------------------------- - Configuration ------------------------------------------------------------------------- - -# ALSA Configuration -An example .asoundrc is provided with the file asoundrc-audio4a. In this configuration, we choose to use software mixing of several virtual audio devices with distinct software volume controls (one per audio role). The example defines 2 audio zones with several ALSA virtual audio devices (endpoints) that applications should target. The prefix of the softvol control must match the configuration audio role name to automatically use audio role specific volume ramping and controls. - -For example: -``` -pcm.Entertainment_Main { - type softvol - slave.pcm "SoftMixer" - control{ - name "Entertainment_Volume" - card 0 - } -} -``` - -Defines a PCM Entertainment_Main endpoint using Entertainment_Volume softvol control for the Entertainment audio role volume. -Please modify your /etc/asound.conf or ~/.asoundrc configuration to match your hardware audio configuration. - -# AHL Configuration File -*ahl-audio4a-config.json* is an example of an AHL configuration file. - -Please modify the configuration file to match with your *.asoundrc* configuration and the desired audio roles. -Copy the file at a location as described below. - -# AHL Configuration File Location - -At loading time the AHL binding will search for a JSON configuration file located following theses rules: - -- default search path is $PROJECT_ROOT/conf.d/project:${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME} -- if environment variable "AAAA_CONFIG_PATH" is defined, it is used as search path -- configuration file name should match "ahl-BINDERNAME-config.json" where BINDERNAME is provided through "--name=BINDERNAME" in afb-daemon command line. - -Example: - -``` -export AAAA_CONFIG_PATH=~/opt/config -afb-daemon --name audio4a --workdir=.--ldpaths=./lib:../afb-aaaa/lib/afb-hal-intel-hda.so:../4a-alsa-core/lib/afb-alsa-4a.so --port=1234 --roothttp=./htdocs --token="" --verbose - -``` -# Configuration parameters definitions --------------------------------------- - -#### hal_list - - Define a list of HAL to be used with AHL. This is a list of HAL binding API names and it used by AHL to associate audio endpoints with a corresponding HAL. - -#### audio_roles - -Defines an application role specific (e.g. entertainment, navigation, etc.) list of prioritized endpoints, priorities and behaviors (e.g. interrupt) to be applied by the audio policy. - -Each audio role has the following parameters: - -- **name** - - Defines the name of the audio role. Some standard audio role names are provided in ahl-policy/ahl-interface.h and used in the sample policy implementation. - - In the sample configuration file (and accompanying policy implementation), the following audio role name are used: - - - **Warning** : Safety-relevant or critical alerts/alarms - - **Guidance** : Important user information where user action is expected (e.g. navigation instruction) - - **Notification** : HMI or else notifications (e.g. touchscreen events, speech recognition on/off,...) - - **Communication** : Voice communications (e.g. handsfree, speech recognition) - - **Entertainment** : Multimedia content (e.g. tuner, media player, etc.) - - **System** : System level content or development - - **Startup** : Early (startup) sound - - **Shutdown** : Late (shutdown) sound - - -- **priority** -Defines the priority audio stream associated with the audio role (will be used by policy implementation to determine audio focus). - -- **interupt_behavior** - - Defines what happens when the current stream interrupts a lower or equal priority stream. - - The following interrupt behaviors are implemented in the sample policy engine: - - - "continue" : Volume ducking, the volume of the lower priority stream is lowered. The target volume value is defined by the policy engine. An AHL_ENDPOINT_VOLUME_EVENT volume event is generated. - - - "pause" : Stream paused, a AHL_STREAM_STATE_EVENT with state_event=STREAM_EVENT_PAUSE is generated for the lower priority stream(s). - - - "cancel" : Stream stop, a AHL_STREAM_STATE_EVENT with state_event=STREAM_EVENT_STOP is generated for the lower priority stream(s). - - **Example** - - An entertainment application is playing music on the ALSA PCM 'Entertainment_Main' (this PCM is routed to the software mixer targeting hw:0). - A navigation application with a higher priority request a stream to be played on the ALSA PCM 'Guidance_Main' (this PCM is also routed to the software mixer targeting hw:0). - The guidance audio role has the interrupt_behavior set to "continue". - - The policy engine implements a volume ducking situation and the software volume control associated with 'Entertainment_Main' is lowered during the navigation application playback. - When the navigation application stops or closes its audio stream, the volume of 'Entertainment_Main' is restored back to it original value. - -- **output/input** - - Defines the list of sink/source endpoints available for the audio role (in order of priority for automatic endpoint selection purposes). - - The endpoint PCM URI values use the following naming convention: - - *framework.pcm_name* - - **Example**: - - ``` - alsa.hw:0 - alsa.plug:Entertainment_Main - pulse.default - - ``` - -- **actions** - - Defines the list of sound related actions supported for the audio role. - - Currently not implemented, this is a provision in the configuration file for future use case such as sound generation. diff --git a/conf.d/project/ahl-audio4a-config.json b/conf.d/project/ahl-audio4a-config.json deleted file mode 100644 index 4946e2c..0000000 --- a/conf.d/project/ahl-audio4a-config.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "version": "0.2.0", - "policy_module": "AudioPolicy_v1", - "description": "High-level binding configuration file", - "note": "Devices and routings are always listed in order of priority (for device selection rules)", - "hal_list": ["usb-audio"], - "audio_roles": [ - { - "name": "Warning", - "description": "Safety-relevant or critical alerts/alarms", - "priority": 100, - "output": [ - "alsa.plug:Warning_Main", - "alsa.plug:Warning_DriverHR" - ], - "actions": [ - "emergency_brake", - "collision_warning", - "blind_spot_warning" - ], - "interupt_behavior": "pause" - }, - { - "name": "Guidance", - "description": "Important user information where user action is expected (e.g. navigation instruction)", - "priority": 25, - "output": [ - "alsa.plug:Guidance_Main", - "alsa.plug:Guidance_DriverHR" - ], - "actions": [ - "lane_guidance_left", - "lane_guidance_right", - "destination_reached" - ], - "interupt_behavior": "continue" - }, - { - "name": "Notification", - "description": "HMI or else notifications (e.g. touchscreen events, speech recognition on/off,...)", - "priority": 0, - "output": [ - "alsa.plug:Notification_Main", - "alsa.plug:Notification_DriverHR" - ], - "actions": [ - "home", - "context_switch", - "accept", - "cancel", - "selection_change" - ], - "interupt_behavior": "cancel" - }, - { - "name": "Communication", - "description": "Voice communications (e.g. handsfree, speech recognition)", - "priority": 50, - "output": [ - "alsa.plug:Communications_Main", - "alsa.plug:Communications_DriverHR" - ], - "input": [ - "alsa.hw:0" - ], - "actions": [ - "bt_device_connected", - "bt_device_disconnected", - "sms_received" - ], - "interupt_behavior": "continue" - }, - { - "name": "Entertainment", - "description": "Multimedia content (e.g. tuner, media player, etc.)", - "priority": 0, - "output": [ - "alsa.plug:Entertainment_Main", - "alsa.plug:Entertainment_DriverHR" - ], - "interupt_behavior": "pause" - }, - { - "name": "System", - "description": "System level content or development", - "priority": 100, - "output": [ - "alsa.hw:0" - ], - "input": [ - "alsa.hw:0" - ], - "interupt_behavior": "continue" - }, - { - "name": "Startup", - "description": "Early (startup) sound", - "priority": 100, - "output": [ - "alsa.hw:0" - ], - "actions": [ - "welcome_sound" - ], - "interupt_behavior": "pause" - }, - { - "name": "Shutdown", - "description": "Late (shutdown) sound", - "priority": 100, - "output": [ - "alsa.hw:0" - ], - "actions": [ - "goodbye_sound" - ], - "interupt_behavior": "cancel" - } - ] -} diff --git a/conf.d/project/ahl-fulup4a-config.json b/conf.d/project/ahl-fulup4a-config.json deleted file mode 100644 index da7d93c..0000000 --- a/conf.d/project/ahl-fulup4a-config.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "version": "0.2.0", - "policy_module": "AudioPolicy_v1", - "description": "High-level binding configuration file", - "note": "Devices and routings are always listed in order of priority (for device selection rules)", - "hal_list": ["usb-jabra"], - "audio_roles": [ - { - "name": "Guidance", - "description": "Important user information where user action is expected (e.g. navigation instruction)", - "priority": 25, - "output": [ - "alsa.plug:Guidance_Main" - ], - "interupt_behavior": "continue" - }, - { - "name": "Notification", - "description": "HMI or else notifications (e.g. touchscreen events, speech recognition on/off,...)", - "priority": 100, - "output": [ - "alsa.plug:Notification_Main" - ], - "interupt_behavior": "pause" - }, - { - "name": "Entertainment", - "description": "Multimedia content (e.g. tuner, media player, etc.)", - "priority": 0, - "output": [ - "alsa.plug:Entertainment_Main" - ], - "interupt_behavior": "pause" - } - ] -} diff --git a/conf.d/project/asoundrc-audio4a b/conf.d/project/asoundrc-audio4a deleted file mode 100644 index 8dde445..0000000 --- a/conf.d/project/asoundrc-audio4a +++ /dev/null @@ -1,207 +0,0 @@ -#AGL Audio High Level ALSA configuration -#This define 2 sounds card with 8 audio roles each -#The alsa soft volume control name must match with the HAL Control Name -pcm.SoftMixer { - type dmix - ipc_key 1024 - ipc_key_add_uid false - ipc_perm 0666 # mixing for all users - - # Define target effective sound card (cannot be a plugin) - slave { - pcm "hw:0" # Main sound card - channels 2 - buffer_size 4096 - period_size 1024 - } - - # DMIX can only map two channels - bindings { - 0 0 - 1 1 - } -} - -pcm.SoftMixer_DriverHR { - type dmix - ipc_key 1024 - ipc_key_add_uid false - ipc_perm 0666 # mixing for all users - - # Define target effective sound card (cannot be a plugin) - slave { - pcm "hw:3" # Alternate sound card / dummy - channels 2 - buffer_size 4096 - period_size 1024 - } - - # DMIX can only map two channels - bindings { - 0 0 - 1 1 - } -} - -pcm.SoftMixer_RSE { - type dmix - ipc_key 1024 - ipc_key_add_uid false - ipc_perm 0666 # mixing for all users - - # Define target effective sound card (cannot be a plugin) - slave { - pcm "hw:4" # Alternate sound card / dummy - } - - # DMIX can only map two channels - bindings { - 0 0 - 1 1 - } -} - -pcm.Entertainment_Main { - type softvol - slave.pcm "SoftMixer" - control{ - name "Entertainment_Volume" - card 0 - } -} - -pcm.Guidance_Main { - type softvol - slave.pcm "SoftMixer" - control{ - name "Guidance_Volume" - card 0 - } -} - -pcm.Communications_Main { - type softvol - slave.pcm "SoftMixer" - control{ - name "Communications_Volume" - card 0 - } -} - -pcm.Notification_Main { - type softvol - slave.pcm "SoftMixer" - control{ - name "Notification_Volume" - card 0 - } -} - -pcm.Warning_Main { - type softvol - slave.pcm "SoftMixer" - control{ - name "Warning_Volume" - card 0 - } -} - -pcm.System_Main { - type softvol - slave.pcm "SoftMixer" - control{ - name "System_Volume" - card 0 - } -} - -pcm.Startup_Main { - type softvol - slave.pcm "SoftMixer" - control{ - name "Startup_Volume" - card 0 - } -} - -pcm.Shutdown_Main { - type softvol - slave.pcm "SoftMixer" - control{ - name "Shutdown_Volume" - card 0 - } -} - -pcm.Entertainment_DriverHR { - type softvol - slave.pcm "SoftMixer_DriverHR" - control{ - name "Entertainment_Volume" - card 3 - } -} - -pcm.Guidance_DriverHR { - type softvol - slave.pcm "SoftMixer_DriverHR" - control{ - name "Guidance_Volume" - card 3 - } -} - -pcm.Communications_DriverHR { - type softvol - slave.pcm "SoftMixer_DriverHR" - control{ - name "Communications_Volume" - card 3 - } -} - -pcm.Notification_DriverHR { - type softvol - slave.pcm "SoftMixer_DriverHR" - control{ - name "Notification_Volume" - card 3 - } -} - -pcm.Warning_DriverHR { - type softvol - slave.pcm "SoftMixer_DriverHR" - control{ - name "Warning_Volume" - card 3 - } -} - - -pcm.System_DriverHR { - type softvol - slave.pcm "SoftMixer_DriverHR" - control{ - name "System_Volume" - card 3 - } -} - -pcm.Startup_DriverHR { - type softvol - slave.pcm "SoftMixer_DriverHR" - control{ - name "Startup_Volume" - card 3 - } -} - -pcm.Shutdown_DriverHR { - type softvol - slave.pcm "SoftMixer_DriverHR" - control{ - name "Shutdown_Volume" - card 3 - } -} diff --git a/conf.d/project/asoundrc-fulup4a b/conf.d/project/asoundrc-fulup4a deleted file mode 100644 index 0942c0b..0000000 --- a/conf.d/project/asoundrc-fulup4a +++ /dev/null @@ -1,171 +0,0 @@ -#AGL Audio High Level ALSA configuration -#This define 2 sounds card with 8 audio roles each -#The alsa soft volume control name must match with the HAL Control Name - -pcm.SoftMixer { - type dmix - slave {pcm "hw:v1340"} #Jabra Solmate 1 - ipc_key 1001 # ipc_key should be unique to each dmix -} - -# ----------------------------------------------------- -# Register ControllerHookPlugin (ToiBeFix fullpath) -# ----------------------------------------------------- -pcm_hook_type.CtlHookPlugin { - install "AlsaInstallHook" - lib "/home/fulup/Workspace/Audio-4a/alsa-4a/build/alsa-hook/policy_alsa_hook.so" -} - -pcm.Entertainment_Main { - type softvol - slave.pcm "SoftMixer" - control{ - name "Entertainment_Volume" - } -} - -pcm.Guidance_Main { - type softvol - slave.pcm "SoftMixer" - control{ - name "Guidance_Volume" - } -} - -pcm.Communications_Main { - type softvol - slave.pcm "SoftMixer" - control{ - name "Communications_Volume" - } -} - -pcm.Notification_Main { - type softvol - slave.pcm "SoftMixer" - control{ - name "Notification_Volume" - } -} - -pcm.Warning_Main { - type softvol - slave.pcm "SoftMixer" - control{ - name "Warning_Volume" - } -} - -pcm.System_Main { - type softvol - slave.pcm "SoftMixer" - control{ - name "System_Volume" - } -} - -pcm.Startup_Main { - type softvol - slave.pcm "SoftMixer" - control{ - name "Startup_Volume" - } -} - -pcm.Shutdown_Main { - type softvol - slave.pcm "SoftMixer" - control{ - name "Shutdown_Volume" - } -} - -# ---------------------------------------------------- -# Define one hooked PCM channel per Audio Roles -# ---------------------------------------------------- -pcm.Multimedia { - type hooks - slave {pcm "Entertainment_Main"} - hooks.0 { - comment "Defined used hook sharelib and provide arguments/config to install func" - type "CtlHookPlugin" - hook_args { - - # print few log messages (default false) - verbose true - - # uri to audio-4a policy engine - uri="unix:/var/tmp/ahl-4a" - - # timeout in ms (default 500) - timeout 5000 - - # force API synchronous mode - synchronous true - - # api subcall to request a role - request { - stream_open "{'audio_role': 'Entertainment', 'endpoint_type':'sink'}" - set_stream_state "{'state':'running'}" - } - - # api subcall to request a role - release { - set_stream_state "{'state':'idle'}" - stream_close "{}" - } - - # map AGL event on Unix signal. Search in event for json key=value - events { - sig-02 {search state_event, value 1} - sig-31 {search state_event, value 2} - sig-32 {search state_event, value 3} - } - } - } -} - -# ---------------------------------------------------- -# Define one hooked PCM channel per Audio Roles -# ---------------------------------------------------- -pcm.Navigation { - type hooks - slave {pcm "Guidance_Main"} - hooks.0 { - comment "Defined used hook sharelib and provide arguments/config to install func" - type "CtlHookPlugin" - hook_args { - - # print few log messages (default false) - verbose true - - # uri to audio-4a policy engine - uri="unix:/var/tmp/ahl-4a" - - # timeout in ms (default 500) - timeout 5000 - - # force API synchronous mode - synchronous true - - # api subcall to request a role - request { - stream_open "{'audio_role': 'Guidance', 'endpoint_type':'sink'}" - set_stream_state "{'state':'running'}" - } - - # api subcall to request a role - release { - set_stream_state "{'state':'idle'}" - stream_close "{}" - } - - # map AGL event on Unix signal. Search in event for json key=value - events { - sig-02 {search state_event, value 1} - sig-31 {search state_event, value 2} - sig-32 {search state_event, value 3} - } - } - } -} diff --git a/htdocs/CMakeLists.txt b/conf.d/project/etc/CMakeLists.txt index c08a493..56271b0 100644 --- a/htdocs/CMakeLists.txt +++ b/conf.d/project/etc/CMakeLists.txt @@ -1,7 +1,7 @@ ########################################################################### -# Copyright 2015, 2016, 2017 IoT.bzh +# Copyright 2018 IoT.bzh # -# author: Fulup Ar Foll <fulup@iot.bzh> +# author: Loïc Collignon <loic.collignon@iot.bzh> # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,19 +16,13 @@ # limitations under the License. ########################################################################### +PROJECT_TARGET_ADD(4a-hl-policy-config) + file(GLOB CONF_FILES "*.json") -################################################## -# HTML Testing Files -################################################## -PROJECT_TARGET_ADD(htdocs) + add_input_files("${CONF_FILES}") - file(GLOB SOURCE_FILES LIST_DIRECTORIES true "*.html" "*.js" "*.jpg" "*.css") - - add_input_files("${SOURCE_FILES}") - - SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES - LABELS "HTDOCS" + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + LABELS "BINDING-CONFIG" OUTPUT_NAME ${TARGET_NAME} - ) - + ) diff --git a/conf.d/project/etc/policy-4a-sample1.json b/conf.d/project/etc/policy-4a-sample1.json new file mode 100644 index 0000000..a1aebcd --- /dev/null +++ b/conf.d/project/etc/policy-4a-sample1.json @@ -0,0 +1,42 @@ +{ + "$schema": "http://iot.bzh/download/public/schema/json/ctl-schema.json", + "metadata": { + "uid": "4a-policy", + "version": "0.1", + "api": "4a-policy", + "require": [], + "info": "Basic Audio Policy Control for Audio-4a - Sample 1", + "author": "Loïc Collignon <loic.collignon@iot.bzh>", + "date": "2018-05-25" + }, + "onload": [], + "controls": [], + "events": [], + "roles":[ + { + "uid": "multimedia", + "description": "Multimedia content (e.g. tuner, media player, etc.)", + "priority": 0, + "stream": "multimedia" + }, + { + "uid": "emergency", + "description": "Safety-relevant or critical alerts/alarms", + "priority": 100, + "stream": "emergency", + "interrupts":[ + {"type": "ramp", "args": { "uid": "ramp-slow", "volume": 30} } + ] + }, + { + "uid": "navigation", + "name": "navigation", + "description": "Navigation instructions (GPS, turn directions, etc...)", + "priority": 25, + "stream": "navigation", + "interrupts":[ + {"type": "ramp", "args": { "uid": "ramp-slow", "volume": 30} } + ] + } + ] +} diff --git a/conf.d/wgt/config.xml.in b/conf.d/wgt/config.xml.in index 458dcaf..2905be1 100644 --- a/conf.d/wgt/config.xml.in +++ b/conf.d/wgt/config.xml.in @@ -15,7 +15,11 @@ </feature> <feature name="urn:AGL:widget:provided-api"> - <param name="ahl-4a" value="ws" /> + <param name="ahl-4a" value="ws" /> + <!-- + <param name="alsacore" value="ws" /> + <param name="rsnddai0ak4613h" value="ws" /> + --> </feature> <feature name="urn:AGL:widget:required-api"> diff --git a/controller b/controller new file mode 160000 +Subproject 738ae54a57f1df7191e075e9eb467d1fc76e4ad diff --git a/htdocs/AFB-websock.js b/htdocs/AFB-websock.js deleted file mode 100644 index ff9fa60..0000000 --- a/htdocs/AFB-websock.js +++ /dev/null @@ -1,177 +0,0 @@ -var urlws; -var urlhttp; - -AFB = function(base, initialtoken){ - -urlws = "ws://"+window.location.host+"/"+base; -urlhttp = "http://"+window.location.host+"/"+base; - -/*********************************************/ -/**** ****/ -/**** AFB_context ****/ -/**** ****/ -/*********************************************/ -var AFB_context; -{ - var UUID = undefined; - var TOKEN = initialtoken; - - var context = function(token, uuid) { - this.token = token; - this.uuid = uuid; - } - - context.prototype = { - get token() {return TOKEN;}, - set token(tok) {if(tok) TOKEN=tok;}, - get uuid() {return UUID;}, - set uuid(id) {if(id) UUID=id;} - }; - - AFB_context = new context(); -} -/*********************************************/ -/**** ****/ -/**** AFB_websocket ****/ -/**** ****/ -/*********************************************/ -var AFB_websocket; -{ - var CALL = 2; - var RETOK = 3; - var RETERR = 4; - var EVENT = 5; - - var PROTO1 = "x-afb-ws-json1"; - - AFB_websocket = function(onopen, onabort) { - var u = urlws; - if (AFB_context.token) { - u = u + '?x-afb-token=' + AFB_context.token; - if (AFB_context.uuid) - u = u + '&x-afb-uuid=' + AFB_context.uuid; - } - this.ws = new WebSocket(u, [ PROTO1 ]); - this.pendings = {}; - this.awaitens = {}; - this.counter = 0; - this.ws.onopen = onopen.bind(this); - this.ws.onerror = onerror.bind(this); - this.ws.onclose = onclose.bind(this); - this.ws.onmessage = onmessage.bind(this); - this.onopen = onopen; - this.onabort = onabort; - this.onclose = onabort; - } - - function onerror(event) { - var f = this.onabort; - if (f) { - delete this.onopen; - delete this.onabort; - f && f(this); - } - this.onerror && this.onerror(this); - } - - function onopen(event) { - var f = this.onopen; - delete this.onopen; - delete this.onabort; - f && f(this); - } - - function onclose(event) { - for (var id in this.pendings) { - var ferr = this.pendings[id].onerror; - ferr && ferr(null, this); - } - this.pendings = {}; - this.onclose && this.onclose(); - } - - function fire(awaitens, name, data) { - var a = awaitens[name]; - if (a) - a.forEach(function(handler){handler(data);}); - var i = name.indexOf("/"); - if (i >= 0) { - a = awaitens[name.substring(0,i)]; - if (a) - a.forEach(function(handler){handler(data);}); - } - a = awaitens["*"]; - if (a) - a.forEach(function(handler){handler(data);}); - } - - function reply(pendings, id, ans, offset) { - if (id in pendings) { - var p = pendings[id]; - delete pendings[id]; - var f = p[offset]; - f(ans); - } - } - - function onmessage(event) { - var obj = JSON.parse(event.data); - var code = obj[0]; - var id = obj[1]; - var ans = obj[2]; - AFB_context.token = obj[3]; - switch (code) { - case RETOK: - reply(this.pendings, id, ans, 0); - break; - case RETERR: - reply(this.pendings, id, ans, 1); - break; - case EVENT: - default: - fire(this.awaitens, id, ans); - break; - } - } - - function close() { - this.ws.close(); - this.onabort(); - } - - function call(method, request) { - return new Promise((function(resolve, reject){ - var id, arr; - do { - id = String(this.counter = 4095 & (this.counter + 1)); - } while (id in this.pendings); - this.pendings[id] = [ resolve, reject ]; - arr = [CALL, id, method, request ]; - if (AFB_context.token) arr.push(AFB_context.token); - this.ws.send(JSON.stringify(arr)); - }).bind(this)); - } - - function onevent(name, handler) { - var id = name; - var list = this.awaitens[id] || (this.awaitens[id] = []); - list.push(handler); - } - - AFB_websocket.prototype = { - close: close, - call: call, - onevent: onevent - }; -} -/*********************************************/ -/**** ****/ -/**** ****/ -/**** ****/ -/*********************************************/ -return { - context: AFB_context, - ws: AFB_websocket -}; -}; - diff --git a/htdocs/AudioBinding.css b/htdocs/AudioBinding.css deleted file mode 100644 index 1052aa7..0000000 --- a/htdocs/AudioBinding.css +++ /dev/null @@ -1,7 +0,0 @@ -pre {outline: 1px solid #ccc; padding: 5px; margin: 5px; } -.string { color: green; } -.number { color: darkorange; } -.boolean { color: blue; } -.null { color: magenta; } -.key { color: red; } - diff --git a/htdocs/AudioBinding.js b/htdocs/AudioBinding.js deleted file mode 100644 index 607d31c..0000000 --- a/htdocs/AudioBinding.js +++ /dev/null @@ -1,198 +0,0 @@ - var afb = new AFB("api", "mysecret"); - var ws; - var sndcard="HALNotSelected"; - var evtidx=0; - var numid=0; - - function syntaxHighlight(json) { - if (typeof json !== 'string') { - json = JSON.stringify(json, undefined, 2); - } - json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); - return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { - var cls = 'number'; - if (/^"/.test(match)) { - if (/:$/.test(match)) { - cls = 'key'; - } else { - cls = 'string'; - } - } else if (/true|false/.test(match)) { - cls = 'boolean'; - } else if (/null/.test(match)) { - cls = 'null'; - } - return '<span class="' + cls + '">' + match + '</span>'; - }); - } - - function getParameterByName(name, url) { - if (!url) { - url = window.location.href; - } - name = name.replace(/[\[\]]/g, "\\$&"); - var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), - results = regex.exec(url); - if (!results) return null; - if (!results[2]) return ''; - return decodeURIComponent(results[2].replace(/\+/g, " ")); - } - - // default soundcard is "PCH" - var devid=getParameterByName("devid"); - if (!devid) devid="hw:1"; - - var haldev=getParameterByName("haldev"); - if (!haldev) haldev="scarlett-usb"; - - var sndname=getParameterByName("sndname"); - if (!sndname) sndname="PCH"; - - var mode=getParameterByName("mode"); - if (!mode) mode="0"; - - - - - function replyok(obj) { - console.log("replyok:" + JSON.stringify(obj)); - document.getElementById("output").innerHTML = "OK: "+ syntaxHighlight(obj); - } - - function replyerr(obj) { - console.log("replyerr:" + JSON.stringify(obj)); - document.getElementById("output").innerHTML = "ERROR: "+ syntaxHighlight(obj); - } - - function gotevent(obj) { - console.log("gotevent:" + JSON.stringify(obj)); - document.getElementById("outevt").innerHTML = (evtidx++) +": "+JSON.stringify(obj); - } - - function send(message) { - var api = document.getElementById("api").value; - var verb = document.getElementById("verb").value; - document.getElementById("question").innerHTML = "subscribe: "+api+"/"+verb + " (" + JSON.stringify(message) +")"; - ws.call(api+"/"+verb, {data:message}).then(replyok, replyerr); - } - - - // On button click from HTML page - function callbinder(api, verb, query) { - console.log ("subscribe api="+api+" verb="+verb+" query=" +query); - var question = urlws +"/" +api +"/" +verb + "?query=" + JSON.stringify(query); - document.getElementById("question").innerHTML = syntaxHighlight(question); - ws.call(api+"/"+verb, query).then(replyok, replyerr); - } - - - // Retreive Select value and Text from the binder - // Note: selection of value/text for a given context is huggly!!! - function querySelectList (elemid, api, verb, query) { - - console.log("querySelectList elemid=%s api=%s verb=%s query=%s", elemid, api, verb, query); - - var selectobj = document.getElementById(elemid); - if (!selectobj) { - console.log ("****** elemid=%s does not exit in HTML page ****", elemid); - return; - } - - // onlick update selected HAL api - selectobj.onclick=function(){ - sndcard= this.value; - console.log ("Default Selection=" + sndcard); - }; - - function gotit (result) { - - // display response as for normal onclick action - replyok(result); - var response=result.response; - - // fulfill select with avaliable active HAL - for (idx=0; idx<response.length; idx++) { - var opt = document.createElement('option'); - - // Alsa LowLevel selection mode - if (response[idx].name) opt.text = response[idx].name; - if (response[idx].devid) opt.value = response[idx].devid; - - // HAL selection mode - if (response[idx].shortname) opt.text = response[idx].shortname; - if (response[idx].api) opt.value = response[idx].api; - - selectobj.appendChild(opt); - } - - sndcard= selectobj.value; - } - - var question = urlws +"/"+api +"/" +verb + "?query=" + JSON.stringify(query); - document.getElementById("question").innerHTML = syntaxHighlight(question); - - // request lowlevel ALSA to get API list - ws.call(api+"/"+verb, query).then(gotit, replyerr); - } - - function refresh_list (self, api, verb, query) { - console.log("refresh_list id=%s api=%s verb=%s query=%s", self.id, api, verb, query); - - if (self.value > 0) return; - - // onlick update selected HAL api - self.onclick=function(){ - numid = parseInt(self.value); - console.log ("Default numid=%d", numid); - }; - - function gotit (result) { - - // display response as for normal onclick action - replyok(result); - var response=result.response; - - - - // fulfill select with avaliable active HAL - for (idx=0; idx<response.length; idx++) { - var opt = document.createElement('option'); - - // Alsa LowLevel selection mode - opt.text = response[idx].name + ' id=' + response[idx].id; - opt.value = response[idx].id; - - self.appendChild(opt); - } - self.selectedIndex=2; - numid = parseInt (self.value); - } - - var question = urlws +"/"+api +"/" +verb + "?query=" + JSON.stringify(query); - document.getElementById("question").innerHTML = syntaxHighlight(question); - - // request lowlevel ALSA to get API list - ws.call(api+"/"+verb, query).then(gotit, replyerr); - } - - - function init(elemid, api, verb, query) { - - function onopen() { - // check for active HALs - querySelectList (elemid, api, verb, query); - - document.getElementById("main").style.visibility = "visible"; - document.getElementById("connected").innerHTML = "Binder WS Active"; - document.getElementById("connected").style.background = "lightgreen"; - ws.onevent("*", gotevent); - } - - function onabort() { - document.getElementById("main").style.visibility = "hidden"; - document.getElementById("connected").innerHTML = "Connected Closed"; - document.getElementById("connected").style.background = "red"; - - } - ws = new afb.ws(onopen, onabort); - } diff --git a/htdocs/README.md b/htdocs/README.md deleted file mode 100644 index bb14b7e..0000000 --- a/htdocs/README.md +++ /dev/null @@ -1,7 +0,0 @@ ------------------------------------------------------------------------- - Basic HTML & WS test ------------------------------------------------------------------------- - - # Load bindings directly from development tree for debug - afb-daemon --verbose --verbose --token="" --ldpaths=build --port=1234 --roothttp=htdocs - diff --git a/htdocs/alsa-core.html b/htdocs/alsa-core.html deleted file mode 100644 index deca631..0000000 --- a/htdocs/alsa-core.html +++ /dev/null @@ -1,66 +0,0 @@ -<html> -<head> - <link rel="stylesheet" href="AudioBinding.css"> - <title>Alsa Low Level Simple Test</title> - - <script type="text/javascript" src="AFB-websock.js"></script> - <script type="text/javascript" src="AudioBinding.js"></script> -</head> - -<body onload="init('alsa_registry','alsacore', 'infoget')"> - - <button id="connected" onclick="init('alsa_registry','alsacore', 'infoget');">Binder WS Fail</button> - <button id="mnitoring" onclick="window.open('/monitoring/monitor.html','_monitor_audio')">Debug/Monitoring</a></button> - - <br><br> - <b>Selected SndCard </b> - <select id='alsa_registry'></select> - - <b>Select NUMID </b> - <select id='alsa_ctl_list' onclick="refresh_list(this, 'alsacore', 'ctlget', {devid:sndcard, mode:1})"> - <option value='-1'>Refresh NUMID list</option> - </select> - - <b>API Verbosity </b> - <select id='api_verbosity' onclick='mode=this.value'> - <option value='0'>Quiet</option> - <option value='1'>Compact</option> - <option value='2'>Verbose</option> - <option value='3'>Full</option> - </select> - - - <br> - <ol> - <li><button onclick="callbinder('alsacore','ctlget', {devid:sndcard, mode:mode})">Get all Alsa Ctls</button></li> - <li><button onclick="callbinder('alsacore','ctlget', {devid:sndcard, mode:mode, ctl:numid})">Get Alsa Ctls [numid]</button></li> - <li><button onclick="callbinder('alsacore','ctlget', {devid:sndcard, mode:mode, ctl:[numid,numid+1]})">Get Alsa Ctls [numid,numid+1]</button></li> - <li><button onclick="callbinder('alsacore','infoget', {})">Get info</button></li> - <li><button onclick="callbinder('alsacore','hallist', {})">HAL list</button></li> - <br> - - - <li><button onclick="callbinder('alsacore','ctlset', {devid:sndcard, mode:mode, ctl:[[9,20]]})">Set Alsa Ctl ctl:[[9,20]]</button></li> - <li><button onclick="callbinder('alsacore','ctlset', {devid:sndcard, mode:mode, ctl:[{id:46,val:50}]})">Set Alsa Ctl ctl:[{id:46,val:50}]}</button></li> - <li><button onclick="callbinder('alsacore','ctlset', {devid:sndcard, mode:mode, ctl:[[6,[20,20]]]})">Set Alsa Ctl ctl:[[6,[20,20]]]</button></li> - <li><button onclick="callbinder('alsacore','ctlset', {devid:sndcard, mode:mode, ctl:[{id:6,val:[50,50]}]})">Set Alsa Ctl ctl:[{id:2,val:[50,50]}]</button></li> - <li><button onclick="callbinder('alsacore','ctlset', {devid:sndcard, mode:mode, ctl:[{id:6,val:[50,50]}, {id:9,val:50,50}]})">Set Alsa Ctl ctl:[{id:6,val:[50,50]}, {id:9,val:50,50}]</button></li> - <br> - <li><button onclick="callbinder('alsacore','ucmquery', {devid:sndcard, mode:mode})">List UCM verbs</button></li> - <li><button onclick="callbinder('alsacore','ucmset' , {devid:sndcard, mode:mode, verb:'HiFi'})">Set UCM HiFi</button></li> - <li><button onclick="callbinder('alsacore','ucmset' , {devid:sndcard, mode:mode, verb:'HiFi', dev:'Headphone'})">Set UCM HiFi+Headphone</button></li> - <li><button onclick="callbinder('alsacore','ucmset' , {devid:sndcard, mode:mode, verb:'HiFi', dev:'Headphone', mod:'RecordMedia'})">Set UCM HiFi+Headphone+RecordMedia</button></li> - <br> - <li><button onclick="callbinder('alsacore','ucmget' , {devid:sndcard, mode:mode, values:['OutputDspName','PlaybackPCM','CapturePCM']})">Get UCM OutputDspName+PlaybackPCM+CapturePCM (SET UCM)</button></li> - <br> - <li><button onclick="callbinder('alsacore','subscribe', {devid:sndcard})">Subscribe AlsaCtl Events</button></li> - <br> - </ol> - - <div id="main" style="visibility:hidden"> - <ol> - <li>Question <pre id="question"></pre> - <li>Response <pre id="output"></pre> - <li>Events: <pre id="outevt"></pre> - </ol> - </div> diff --git a/htdocs/alsa-hal.html b/htdocs/alsa-hal.html deleted file mode 100644 index 013c547..0000000 --- a/htdocs/alsa-hal.html +++ /dev/null @@ -1,54 +0,0 @@ -<html> -<head> - <title>Basic Audio Hardware Abstraction Layer Test</title> - <link rel="stylesheet" href="AudioBinding.css"> - <script type="text/javascript" src="AFB-websock.js"></script> - <script type="text/javascript" src="AudioBinding.js"></script> -</head> - - -<body onload="init('hal_registry','alsacore', 'hallist')"> - - <h1>Simple AlsaHAL tests</h1> - <button id="connected" onclick="init('hal_registry','alsacore', 'hallist')">Binder WS Fail</button> - <br><br> - <b>Selected HAL </b> - <select id='hal_registry'></select> - - <b>API Verbosity </b> - <select id='api_verbosity' onclick='mode=this.value'> - <option value='0'>Quiet</option> - <option value='1'>Compact</option> - <option value='2'>Verbose</option> - <option value='3'>Full</option> - </select> - <br> - - <br> - <ol> - - <li><button onclick="callbinder(sndcard,'ctllist')">List Selected HAL Controls </button></li> - <li><button onclick="callbinder(sndcard,'ctlget', {label:'Master_Playback_Volume'})">Get {label:'Master_Playback_Volume'}</button></li> - <li><button onclick="callbinder(sndcard,'ctlget', [{tag:4},{tag:5}])">Get[{tag:4},{tag:5}]</button></li> - <li><button onclick="callbinder(sndcard,'ctlget', [4,5])">Get [4,5]</button></li> - <br> - <li><button onclick="callbinder(sndcard,'ctlset', {label:'Master_Playback_Volume', val:[50]})">Set {label:'Master_Playback_Volume', value=[50]}</button></li> - <li><button onclick="callbinder(sndcard,'ctlset', {tag: 4, val:5})">Set {tag: 4, val:5}</button></li> - <li><button onclick="callbinder(sndcard,'ctlset', [{tag:4, val:25},{tag:5, val:25}])">Set[{tag:4, val:25},{tag:5, val:25}]</button></li> - <li><button onclick="callbinder(sndcard,'ctlset', [{tag:4, val:[55,45]},{tag:5, val:[45,55]}])">Set[{tag:4, val:[55,45]},{tag:5, val:[45,55]}]]</button></li> - <br> - - <li> - <label for="volramp">Volume Ramp</label> - <input id="volramp" type="number" min=0 max=100 step=10 maxlength=3 placeholder="Enter 0-100" onChange="callbinder(sndcard,'ctl-set', {label:'Volume_Ramp', val:this.value})"> - </li> - <br> - </ol> - - <div id="main" style="visibility:hidden"> - <ol> - <li>Question <pre id="question"></pre> - <li>Response <pre id="output"></pre> - <li>Events: <pre id="outevt"></pre> - </ol> - </div> diff --git a/htdocs/assets/content-background-car-wide.png b/htdocs/assets/content-background-car-wide.png Binary files differdeleted file mode 100644 index 073d1b9..0000000 --- a/htdocs/assets/content-background-car-wide.png +++ /dev/null diff --git a/htdocs/assets/content-background-car.png b/htdocs/assets/content-background-car.png Binary files differdeleted file mode 100644 index 4bcde72..0000000 --- a/htdocs/assets/content-background-car.png +++ /dev/null diff --git a/htdocs/assets/content-background.png b/htdocs/assets/content-background.png Binary files differdeleted file mode 100644 index 9be581c..0000000 --- a/htdocs/assets/content-background.png +++ /dev/null diff --git a/htdocs/assets/emergency.png b/htdocs/assets/emergency.png Binary files differdeleted file mode 100644 index c959b04..0000000 --- a/htdocs/assets/emergency.png +++ /dev/null diff --git a/htdocs/assets/favicon.ico b/htdocs/assets/favicon.ico Binary files differdeleted file mode 100644 index eeb7ab7..0000000 --- a/htdocs/assets/favicon.ico +++ /dev/null diff --git a/htdocs/assets/iot-bzh-logo-small.png b/htdocs/assets/iot-bzh-logo-small.png Binary files differdeleted file mode 100644 index 2c3b2ae..0000000 --- a/htdocs/assets/iot-bzh-logo-small.png +++ /dev/null diff --git a/htdocs/assets/messages.png b/htdocs/assets/messages.png Binary files differdeleted file mode 100644 index 4919452..0000000 --- a/htdocs/assets/messages.png +++ /dev/null diff --git a/htdocs/assets/music-pause.png b/htdocs/assets/music-pause.png Binary files differdeleted file mode 100644 index 790a6c4..0000000 --- a/htdocs/assets/music-pause.png +++ /dev/null diff --git a/htdocs/assets/music-play.png b/htdocs/assets/music-play.png Binary files differdeleted file mode 100644 index 37c18e3..0000000 --- a/htdocs/assets/music-play.png +++ /dev/null diff --git a/htdocs/assets/phone-call-emit1.png b/htdocs/assets/phone-call-emit1.png Binary files differdeleted file mode 100644 index a8d863b..0000000 --- a/htdocs/assets/phone-call-emit1.png +++ /dev/null diff --git a/htdocs/assets/phone-call-emit2.png b/htdocs/assets/phone-call-emit2.png Binary files differdeleted file mode 100644 index 14c8cbe..0000000 --- a/htdocs/assets/phone-call-emit2.png +++ /dev/null diff --git a/htdocs/assets/phone-call-emit3.png b/htdocs/assets/phone-call-emit3.png Binary files differdeleted file mode 100644 index 5283a40..0000000 --- a/htdocs/assets/phone-call-emit3.png +++ /dev/null diff --git a/htdocs/assets/phone-call.png b/htdocs/assets/phone-call.png Binary files differdeleted file mode 100644 index 40007d2..0000000 --- a/htdocs/assets/phone-call.png +++ /dev/null diff --git a/htdocs/audiobackend.html b/htdocs/audiobackend.html deleted file mode 100644 index a7c8f41..0000000 --- a/htdocs/audiobackend.html +++ /dev/null @@ -1,35 +0,0 @@ -<html> -<head> - <link rel="stylesheet" href="AudioBinding.css"> - <title>Audio High Level Test</title> - - <script type="text/javascript" src="AFB-websock.js"></script> - <script type="text/javascript" src="AudioBinding.js"></script> -</head> - -<body onload="init('audiod')"> - - <button id="connected" onclick="init('audiod');">Binder WS Fail</button> <br><br> - <br> - <ol> - <li><button onclick="callbinder('audiod','play', {device_name:'plug:Entertainment_Main', filepath:'style.wav', loop:0})">play('hw:0', 'style.wav')</button></li> - <li><button onclick="callbinder('audiod','play', {device_name:'hw:0', filepath:'application.wav', loop:1})">playloop('hw:0', 'application.wav')</button></li> - <li><button onclick="callbinder('audiod','play', {device_name:'hw:0', filepath:'application.wav', loop:0})">play('hw:0', 'application.wav')</button></li> - <li><button onclick="callbinder('audiod','stop', {audio_role:4})">stop()</button></li> - <li><button onclick="callbinder('audiod','pause', {audio_role:4})">pause()</button></li> - <li><button onclick="callbinder('audiod','subscribe', {audio_role:4})">subscribe()</button></li> - <li><button onclick="callbinder('audiod','unsubscribe', {audio_role:4})">unsubscribe()</button></li> - <li><button onclick="callbinder('audiod','post_event', {event_name:'speed', event_parameter:{speed:10}})">post_event('event_name:speed, {speed:10}')</button></li> - - </li> - - <br> - </ol> - - <div id="main" style="visibility:hidden"> - <ol> - <li>Question <pre id="question"></pre> - <li>Response <pre id="output"></pre> - <li>Events: <pre id="outevt"></pre> - </ol> - </div> diff --git a/htdocs/audiohl.html b/htdocs/audiohl.html deleted file mode 100644 index 6745ba1..0000000 --- a/htdocs/audiohl.html +++ /dev/null @@ -1,93 +0,0 @@ -<html> - -<head> - <link rel="stylesheet" href="AudioBinding.css"> - <title>Audio High Level Test</title> - - <script type="text/javascript" src="AFB-websock.js"></script> - <script type="text/javascript" src="AudioBinding.js"></script> -</head> - -<body onload="init('ahl-4a'); ep_type='sink' ; ar='Entertainment' ; ep_id=0 ; s_id=0 ; prop_val=0 ; vol_val='0' ; p_name='balance' ; st_state='idle' ; st_mute=0"> - - <button id="connected" onclick="init('ahl-4a');">Binder WS Fail</button> <br><br> - <button id="monitoring" onclick="window.open('/monitoring/monitor.html','_monitor_audio')">Debug/Monitoring</a></button> - - <b>Audio Role</b> - <select select_id='audiorole_list' onclick='ar=this.value'> - <option value='Warning' >Warning</option> - <option value='Guidance'>Guidance</option> - <option value='Notification'>Notification</option> - <option value='Communication'>Communication</option> - <option selected value='Entertainment'>Entertainment</option> - <option value='System'>System</option> - <option value='Startup'>Startup</option> - <option value='Shutdown'>Shutdown</option> - </select><br> - <b>Endpoint Type</b> - <select select_id='endpoint_type_list' onclick='ep_type=this.value'> - <option value='source'>Source</option> - <option selected value='sink'>Sink</option> - </select><br> - <b><label for="epidsel">Endpoint ID</label></b> - <input id="epidsel" type="number" value="0" min=0 step=1 maxlength=4 onchange='ep_id=eval(parseInt(this.value))'><br> - <b><label for="stidsel">Stream ID</label></b> - <input id="stidsel" type="number" value="0" min=0 step=1 maxlength=4 onchange='s_id=eval(parseInt(this.value))'><br> - <b>Stream State</b> - <select select_id='stream_state' onclick='st_state=this.value'> - <option selected value='idle'>Idle</option> - <option value='running'>Running</option> - <option value='paused'>Paused</option> - </select><br> - <b>Stream Mute State</b> - <select select_id='stream_mute' onclick='st_mute=parseInt(this.value)'> - <option selected value='0'>Unmuted</option> - <option value='1'>Muted</option> - </select><br> - <b>Property</b> - <select select_id='property_name_list' onclick='p_name=this.value'> - <option selected value='balance'>Balance</option> - <option value='fade'>Fade</option> - <option value='eq_bass'>EQ Bass</option> - <option value='eq_mid'>EQ Mid</option> - <option value='eq_treble'>EQ Treble</option> - </select><br> - <b><label for="valpropsel">Volume Value</label></b> - <input id="volvalsel" type="number" value="0" min=0 max=100 step=1 maxlength=4 onchange='vol_val=this.value'><br> - <b><label for="valpropsel">Property Value</label></b> - <input id="propvalsel" type="number" value="0" min=0 max=100 step=1 maxlength=4 onchange='prop_val=eval(parseInt(this.value))'><br> - - <br> - <ol> - <li><button onclick="callbinder('ahl-4a','get_endpoints', {audio_role:ar,endpoint_type:ep_type})">get_endpoints(audio_role,endpoint_type)</button></li> - <li><button onclick="callbinder('ahl-4a','stream_open', {audio_role:ar,endpoint_type:ep_type})">stream_open(audio_role,endpoint_type)</button></li> - <li><button onclick="callbinder('ahl-4a','stream_open', {audio_role:ar,endpoint_type:ep_type,endpoint_id:ep_id})">stream_open(audio_role,enpoint_type,endpoint_id)</button></li> - <li><button onclick="callbinder('ahl-4a','stream_close', {stream_id:s_id})">stream_close(stream_id)</button></li> - <li><button onclick="callbinder('ahl-4a','stream_close', {})">stream_close()</button></li> - <li><button onclick="callbinder('ahl-4a','get_stream_info', {stream_id:s_id})">get_stream_info(stream_id)</button></li> - <li><button onclick="callbinder('ahl-4a','set_stream_state', {stream_id:s_id})">set_stream_state(stream_id)</button></li> - <li><button onclick="callbinder('ahl-4a','set_stream_state', {stream_id:s_id,state:st_state})">set_stream_state(stream_id,stream_state)</button></li> - <li><button onclick="callbinder('ahl-4a','set_stream_state', {stream_id:s_id,mute:st_mute})">set_stream_state(stream_id,mute)</button></li> - <li><button onclick="callbinder('ahl-4a','set_stream_state', {stream_id:s_id,state:st_state,mute:st_mute})">set_stream_state(stream_id,stream_state,mute)</button></li> - <li><button onclick="callbinder('ahl-4a','set_stream_state', {state:st_state,mute:st_mute})">set_stream_state(stream_state,mute)</button></li> - <li><button onclick="callbinder('ahl-4a','volume', {endpoint_type:ep_type,endpoint_id:ep_id,volume:vol_val})">volume(endpoint_type,endpoint_id,value)</button></li> - <li><button onclick="callbinder('ahl-4a','volume', {endpoint_type:ep_type,endpoint_id:ep_id})">volume(endpoint_type,endpoint_id)</button></li> - <li><button onclick="callbinder('ahl-4a','property', {endpoint_type:ep_type,endpoint_id:ep_id,property_name:p_name,value:prop_val})">property(endpoint_type,endpoint_id,property,value)</button></li> - <li><button onclick="callbinder('ahl-4a','property', {endpoint_type:ep_type,endpoint_id:ep_id,property_name:p_name})">property(endpoint_type,endpoint_id,property)</button></li> - <li><button onclick="callbinder('ahl-4a','get_endpoint_info', {endpoint_type:ep_type,endpoint_id:ep_id})">get_endpoint_info(endpoint_type,endpoint_id)</button></li> - <li><button onclick="callbinder('ahl-4a','get_list_actions', {audio_role:ar})">get_list_actions(audio_role)</button></li> - <li><button onclick="callbinder('ahl-4a','post_action', {action_name:'emergency_brake',audio_role:ar,media_name:'Warning.wav'})">post_action(emergency_brake,audio_role,'Warning.wav')</button></li> - <li><button onclick="callbinder('ahl-4a','event_subscription', {events:['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_action'],subscribe:1})">subscribe(['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_action'])</button> - <li><button onclick="callbinder('ahl-4a','event_subscription', {events:['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_action'],subscribe:0})">unsubscribe(['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_action'])</button></li> - </ol> - - <div id="main" style="visibility:hidden"> - <ol> - <li>Question - <pre id="question"></pre> - <li>Response - <pre id="output"></pre> - <li>Events: - <pre id="outevt"></pre> - </ol> - </div>
\ No newline at end of file diff --git a/htdocs/index.html b/htdocs/index.html deleted file mode 100644 index 532cfce..0000000 --- a/htdocs/index.html +++ /dev/null @@ -1,10 +0,0 @@ -<html> - <head> - <title>AGL Audio Agent</title> - <body> - <h1>AAA binding tests</h1> - <ol> - <li><a href="alsa-core.html">AlsaCore Low Level Binding</a> - <li><a href="alsa-hal.html" >AlsaHAL Hardware Abstraction Layer</a> - <li><a href="audiohl.html">High Level Audio API</a> - <li><a href="audiobackend.html">Audio Backend Test</a> diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml deleted file mode 100644 index 5bd3283..0000000 --- a/nbproject/configurations.xml +++ /dev/null @@ -1,85 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<configurationDescriptor version="100"> - <logicalFolder name="root" displayName="root" projectFiles="true" kind="ROOT"> - <df root="." name="0"> - <df name="afb-utilities"> - <in>filescan-utils.c</in> - <in>wrap-json.c</in> - </df> - <df name="ahl-binding"> - <in>ahl-binding.c</in> - <in>ahl-config.c</in> - <in>ahl-deviceenum.c</in> - <in>ahl-json.c</in> - </df> - <df name="ahl-policy"> - <in>ahl-policy.c</in> - </df> - <df name="ahl-utilities"> - <in>ahl-policy-utils.c</in> - </df> - </df> - <logicalFolder name="ExternalFiles" - displayName="Important Files" - projectFiles="false" - kind="IMPORTANT_FILES_FOLDER"> - <itemPath>build/Makefile</itemPath> - <itemPath>nbproject/private/launcher.properties</itemPath> - </logicalFolder> - </logicalFolder> - <sourceFolderFilter>^(nbproject)$</sourceFolderFilter> - <sourceRootList> - <Elem>.</Elem> - </sourceRootList> - <projectmakefile>build/Makefile</projectmakefile> - <confs> - <conf name="Default" type="0"> - <toolsSet> - <compilerSet>default</compilerSet> - <dependencyChecking>false</dependencyChecking> - <rebuildPropChanged>false</rebuildPropChanged> - </toolsSet> - <codeAssistance> - <buildAnalyzer>false</buildAnalyzer> - </codeAssistance> - <makefileType> - <makeTool> - <buildCommandWorkingDir>build</buildCommandWorkingDir> - <buildCommand>${MAKE} -f Makefile</buildCommand> - <cleanCommand>${MAKE} -f Makefile clean</cleanCommand> - <executablePath></executablePath> - <cTool> - <incDir> - <pElem>.</pElem> - </incDir> - </cTool> - <ccTool> - <incDir> - <pElem>.</pElem> - </incDir> - </ccTool> - </makeTool> - <preBuild> - <preBuildCommandWorkingDir>build</preBuildCommandWorkingDir> - <preBuildCommand>cmake ..</preBuildCommand> - </preBuild> - </makefileType> - <item path="afb-utilities/filescan-utils.c" ex="false" tool="0" flavor2="0"> - </item> - <item path="afb-utilities/wrap-json.c" ex="false" tool="0" flavor2="0"> - </item> - <item path="ahl-binding/ahl-binding.c" ex="false" tool="0" flavor2="0"> - </item> - <item path="ahl-binding/ahl-config.c" ex="false" tool="0" flavor2="0"> - </item> - <item path="ahl-binding/ahl-deviceenum.c" ex="false" tool="0" flavor2="0"> - </item> - <item path="ahl-binding/ahl-json.c" ex="false" tool="0" flavor2="0"> - </item> - <item path="ahl-policy/ahl-policy.c" ex="false" tool="0" flavor2="0"> - </item> - <item path="ahl-utilities/ahl-policy-utils.c" ex="false" tool="0" flavor2="0"> - </item> - </conf> - </confs> -</configurationDescriptor> diff --git a/nbproject/project.xml b/nbproject/project.xml deleted file mode 100644 index 91d2ad4..0000000 --- a/nbproject/project.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://www.netbeans.org/ns/project/1"> - <type>org.netbeans.modules.cnd.makeproject</type> - <configuration> - <data xmlns="http://www.netbeans.org/ns/make-project/1"> - <name>agl-service-audio-4a</name> - <c-extensions>c</c-extensions> - <cpp-extensions/> - <header-extensions>h,hpp</header-extensions> - <sourceEncoding>UTF-8</sourceEncoding> - <make-dep-projects/> - <sourceRootList> - <sourceRootElem>.</sourceRootElem> - </sourceRootList> - <confList> - <confElem> - <name>Default</name> - <type>0</type> - </confElem> - </confList> - <formatting> - <project-formatting-style>false</project-formatting-style> - </formatting> - </data> - </configuration> -</project> |