summaryrefslogtreecommitdiffstats
path: root/Alsa-Plugin/Alsa-Policy-Hook/PolicyHookCb.c
blob: 80a9ceb38b1987d07b5dc0014b9cf42996597ba7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109

@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
/*
 * Copyright (C) 2016 "IoT.bzh"
 * Author "Manuel Bachmann"
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef MEDIA_RYGEL_H
#define MEDIA_RYGEL_H

/* --------------- MEDIA RYGEL DEFINITIONS ------------------ */

#include <sys/time.h>
#include <libgupnp/gupnp-control-point.h>

#include "local-def.h"

#define URN_MEDIA_SERVER   "urn:schemas-upnp-org:device:MediaServer:1"
#define URN_MEDIA_RENDERER "urn:schemas-upnp-org:device:MediaRenderer:1"
#define URN_CONTENT_DIR    "urn:schemas-upnp-org:service:ContentDirectory"
#define URN_AV_TRANSPORT   "urn:schemas-upnp-org:service:AVTransport"

typedef enum { PLAY, PAUSE, STOP } State;
typedef struct dev_ctx dev_ctx_T;

struct dev_ctx {
    GMainContext *loop;
    GUPnPContext *context;
    GUPnPDeviceInfo *device_info;
    GUPnPServiceInfo *content_dir;
    GUPnPServiceInfo *av_transport;
    char *content_res;
    int content_num;
    State state;
    State target_state;
};

STATIC char* _rygel_list_raw (dev_ctx_T *, unsigned int *);
STATIC char* _rygel_find_id_for_index (dev_ctx_T *, char *, unsigned int);
STATIC char* _rygel_find_metadata_for_id (dev_ctx_T *, char *);
STATIC char* _rygel_find_uri_for_metadata (dev_ctx_T *, char *);
STATIC unsigned char _rygel_start_doing (dev_ctx_T *, char *, char *, State);
STATIC unsigned char _rygel_find_av_transport (dev_ctx_T *);
STATIC void _rygel_device_cb (GUPnPControlPoint *, GUPnPDeviceProxy *, gpointer);
STATIC void _rygel_av_transport_cb (GUPnPControlPoint *
@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
/*
 * Copyright (C) 2016 "IoT.bzh"
 * Author Fulup Ar Foll <fulup@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.
 * 
 * AfbCallBack (snd_ctl_hal_t *handle, int numid, void **response);
 *  AlsaHookInit is mandatory and called with numid=0
 * 
 * Syntax in .asoundrc file
 *   CrlLabel    { cb MyFunctionName name "My_Second_Control" }
 * 
 * Testing:
 *   aplay -DAlsaHook /usr/share/sounds/alsa/test.wav 
 * 
 * References:
 *  https://www.spinics.net/lists/alsa-devel/msg54235.html
 *  https://github.com/shivdasgujare/utilities/blob/master/nexuss/alsa-scenario-hook/src/alsa-wrapper.c
 */

#define _GNU_SOURCE  
#include <stdio.h>
#include <alsa/asoundlib.h>
#include <alsa/conf.h>
#include <alsa/pcm.h>

#include <systemd/sd-event.h>
#include <json-c/json.h>

#include "afb/afb-wsj1.h"
#include "afb/afb-ws-client.h"
#include <pthread.h>
#include <semaphore.h>


#define PLUGIN_ENTRY_POINT AlsaInstallHook
    // Fulup Note: What ever you may find on Internet you should use
    // SND_CONFIG_DLSYM_VERSION_HOOK and not SND_CONFIG_DLSYM_VERSION_HOOK
    SND_DLSYM_BUILD_VERSION(PLUGIN_ENTRY_POINT, SND_PCM_DLSYM_VERSION)

// this should be more than enough
#define MAX_API_CALL 10
#define MAX_EVT_CALL 10
    
// timeout in ms    
#define REQUEST_DEFAULT_TIMEOUT 500
#ifndef MAINLOOP_WATCHDOG
#define MAINLOOP_WATCHDOG 100000
#endif

// closing message is added to query when PCM is closed
#define CLOSING_MSG ",\"source\":-1}"
    
// Currently not implemented
#define UNUSED_ARG(x) UNUSED_ ## x __attribute__((__unused__))    
void OnRequestCB(void* UNUSED_ARG(handle) , const char* UNUSED_ARG(api), const char* UNUSED_ARG(verb), struct afb_wsj1_msg*UNUSED_ARG(msg)) {}

typedef struct {
    const char *api;
    const char *verb;
    long timeout;
    char *query;
    size_t length;
    
    sd_event_source *evtSource;
    char *callIdTag;
    void *afbClient;
} afbRequestT;

typedef struct {
    const char *name;
    int  signal;
} afbEventT;

typedef struct {
    snd_pcm_t *pcm;
    const char *uri;
    struct afb_wsj1 *wsj1;
    sd_event *sdLoop;
    int verbose;
    sem_t semaphore;
    int count;
    int error;
    afbRequestT **request;
    afbEventT **event;
} afbClientT;



static void *LoopInThread(void *handle) {
    afbClientT *afbClient = (afbClientT*) handle;
    int count=0;
    int watchdog= MAINLOOP_WATCHDOG *1000;

    /* loop until end */
    for (;;) {
        
        if (afbClient->verbose) printf("ON-MAINLOOP ping=%d\n", count++);
        sd_event_run(afbClient->sdLoop, watchdog);
    }

    return NULL;
}

// lost connect with the AudioDaemon
static void OnHangupCB(void *handle, struct afb_wsj1 *wsj1) {

    afbClientT *afbClient = (afbClientT*) handle;
    SNDERR("(Hoops) Lost Connection to %s", afbClient->uri);
    
    // try to close PCM when impossible terminate client
    int err = snd_pcm_close (afbClient->pcm);
    if (err) exit(1);
}

typedef enum {
    HOOK_INSTALL,
    HOOK_CLOSE,
} hookActionT;


void OnEventCB(void *handle, const char *event, struct afb_wsj1_msg *msg) {
    afbClientT *afbClient = (afbClientT*) handle;
    afbEventT **afbEvent = afbClient->event;
    json_object *eventJ, *tmpJ, *dataJ;
    const char *label;
    int value, done, index;
    
    eventJ = afb_wsj1_msg_object_j(msg);
    done= json_object_object_get_ex(eventJ,"data", &dataJ);
    if (!done) {
        SNDERR ("PCM_HOOK: uri=%s empty event label", afbClient->uri);  
        goto OnErrorExit;
    }   

    json_object_object_get_ex(dataJ,"signal", &tmpJ);
    label=json_object_get_string(tmpJ);
    
    json_object_object_get_ex(dataJ,"value", &tmpJ);
    value=json_object_get_int(tmpJ);
    
    for (index=0; afbEvent[index]!= NULL; index++) {
        if (!strcmp(afbEvent[index]->name, label)) break;
    }
    
    if (!afbEvent[index] || !afbEvent[index]->signal) {
        SNDERR ("PCM_HOOK: Unsupported uri=%s label=%s", afbClient->uri, label); 
        return;
    }

    // send signal to self process
    kill (getpid(), afbEvent[index]->signal);
    
    if (afbClient->verbose) printf("ON-EVENT label=%s signal=%d\n", label, value);
    return;
    
OnErrorExit:
    SNDERR("ON-EVENT %s(%s)\n", event, afb_wsj1_msg_object_s(msg));
    return;
}

// callback interface for wsj1
static struct afb_wsj1_itf itf = {
    .on_hangup = OnHangupCB,
    .on_call = OnRequestCB,
    .on_event = OnEventCB
};

void OnResponseCB(void *handle, struct afb_wsj1_msg *msg) {
    afbRequestT *afbRequest= (afbRequestT*)handle;
    afbClientT *afbClient=(afbClientT*)afbRequest->afbClient;

    if (afbClient->verbose) printf("ON-RESPONSE call=%s response=%s\n", afbRequest->callIdTag, afb_wsj1_msg_object_s(msg));
    
    // Cancel timeout for this request
    sd_event_source_unref(afbRequest->evtSource);
    
    if (! afb_wsj1_msg_is_reply_ok(msg)) goto OnErrorExit;
    
    // When not more waiting call release semaphore
    afbClient->count--;
    if (afbClient->count == 0) {
        if (afbClient->verbose) printf("ON-RESPONSE No More Waiting Request\n");
        afbClient->error=0;
        sem_post (&afbClient->semaphore);
    }
    return;

OnErrorExit:    
    fprintf(stderr, "ON-RESPONSE ERROR call=%s response=%s\n", afbRequest->callIdTag, afb_wsj1_msg_object_s(msg));
   afbClient->error=1;
    sem_post (&afbClient->semaphore);
}

int OnTimeoutCB (sd_event_source* source, uint64_t timer, void* handle) {
    afbClientT *afbClient= (afbClientT*)handle;

    SNDERR("\nON-TIMEOUT Call Request Fail URI=%s\n", afbClient->uri);
    
    // Close PCM and release waiting client
    afbClient->error=1;
    sem_post (&afbClient->semaphore);          
    
    return 0;
}

// Call AGL binder asynchronously by with a timeout
static int CallWithTimeout(afbClientT *afbClient, afbRequestT *afbRequest, int count, hookActionT action) {
    uint64_t usec;
    int err;

    // create a unique tag for request
    (void) asprintf(&afbRequest->callIdTag, "%d:%s/%s", count, afbRequest->api, afbRequest->verb);
    
    // create a timer with ~250us accuracy
    sd_event_now(afbClient->sdLoop, CLOCK_MONOTONIC, &usec);
    sd_event_add_time(afbClient->sdLoop, &afbRequest->evtSource, CLOCK_MONOTONIC, usec+afbRequest->timeout*1000, 250, OnTimeoutCB, afbClient);

    if (afbClient->verbose) printf("CALL-REQUEST api=%s/%s tag=%s\n", afbRequest->api, afbRequest->verb, afbRequest->callIdTag);
    
    // on PCM close replace last '}' by CLOSING_MSG 
    if (action == HOOK_CLOSE) {
        for (size_t index=afbRequest->length; index >0; index--) {
            if (afbRequest->query[index] == '}') {
                strcpy (&afbRequest->query[index], CLOSING_MSG);
                break;
            }
        }
    }

    err = afb_wsj1_call_s(afbClient->wsj1, afbRequest->api, afbRequest->verb, afbRequest->query, OnResponseCB, afbRequest);    
    if (err) goto OnErrorExit;
    
    // save client handle in request
    afbRequest->afbClient = afbClient;
    afbClient->count ++;
    
    return 0;
    
OnErrorExit:
    return -1;
}

static int LaunchCallRequest(afbClientT *afbClient, hookActionT action) {

    pthread_t tid;
    int err, idx;
    afbRequestT **afbRequest= afbClient->request;
        
    if (action == HOOK_INSTALL) {
        // init waiting counting semaphore
        if (sem_init(&afbClient->semaphore, 1, 0) == -1) {
           fprintf(stderr, "LaunchCallRequest: Fail Semaphore Init: %s\n", afbClient->uri); 
        }

        // Create a main loop
        err = sd_event_default(&afbClient->sdLoop);
        if (err < 0) {
            fprintf(stderr, "LaunchCallRequest: Connection to default event loop failed: %s\n", strerror(-err));
            goto OnErrorExit;
        }

        // start a thread with a mainloop to monitor Audio-Agent
        err = pthread_create(&tid, NULL, &LoopInThread, afbClient);
        if (err) goto OnErrorExit;

        // connect the websocket wsj1 to the uri given by the first argument 
        afbClient->wsj1 = afb_ws_client_connect_wsj1(afbClient->sdLoop, afbClient->uri, &itf, afbClient);
        if (afbClient->wsj1 == NULL) {
            fprintf(stderr, "LaunchCallRequest: Connection to %s failed\n", afbClient->uri);
            goto OnErrorExit;
        }
    }
    
    // send call request to audio-agent asynchronously (respond with thread mainloop context)
    for (idx = 0; afbRequest[idx] != NULL; idx++) {
        err = CallWithTimeout(afbClient, afbRequest[idx], idx, action);
        if (err) {
            fprintf(stderr, "LaunchCallRequest: Fail call %s//%s/%s&%s", afbClient->uri, afbRequest[idx]->api, afbRequest[idx]->verb, afbRequest[idx]->query);
            goto OnErrorExit;
        }        
    }
    
    // launch counter to keep track of waiting request call
    afbClient->count=idx;

    return 0;

OnErrorExit:
    return -1;
}

static int AlsaCloseHook(snd_pcm_hook_t *hook) {

    afbClientT *afbClient = (afbClientT*) snd_pcm_hook_get_private (hook);
    
    // launch call request and create a waiting mainloop thread
    int err = LaunchCallRequest(afbClient, HOOK_CLOSE);
    if (err < 0) {
        fprintf (stderr, "PCM Fail to Enter Mainloop\n");
        goto OnErrorExit;
    }
    
    // wait for all call request to return
    sem_wait(&afbClient->semaphore);
    if (afbClient->error) {
        fprintf (stderr, "AlsaCloseHook: Audio Agent Fail to respond\n");
        goto OnErrorExit;        
    }

    if (afbClient->verbose) fprintf(stdout, "\nAlsaHook Close Success PCM=%s URI=%s\n", snd_pcm_name(afbClient->pcm), afbClient->uri);
    return 0;

OnErrorExit:
    fprintf(stderr, "\nAlsaPcmHook Plugin Close Fail PCM=%s\n", snd_pcm_name(afbClient->pcm));
    return 0;
}

// Function call when Plugin PCM is OPEN
int PLUGIN_ENTRY_POINT (snd_pcm_t *pcm, snd_config_t *conf) {
    snd_pcm_hook_t *h_close = NULL;
    snd_config_iterator_t it, next;
    afbClientT *afbClient = malloc(sizeof (afbClientT));
    afbRequestT **afbRequest = malloc(MAX_API_CALL * sizeof(afbRequestT*));
    afbEventT **afbEvent= malloc(MAX_EVT_CALL * sizeof(afbEventT*));
    int err;

    // start populating client handle
    afbClient->pcm = pcm;
    afbClient->verbose = 0;
    afbClient->request = afbRequest;
    
    // Get PCM arguments from asoundrc
    snd_config_for_each(it, next, conf) {
        snd_config_t *node = snd_config_iterator_entry(it);
        const char *id;

        // ignore comment en empty lines
        if (snd_config_get_id(node, &id) < 0) continue;
        if (strcmp(id, "comment") == 0 || strcmp(id, "hint") == 0) continue;

        if (strcmp(id, "uri") == 0) {
            const char *uri;
            if (snd_config_get_string(node, &uri) < 0) {
                SNDERR("Invalid String for %s", id);
                goto OnErrorExit;
            }
            afbClient->uri=strdup(uri);
            continue;
        }

        if (strcmp(id, "verbose") == 0) {
            afbClient->verbose= snd_config_get_bool(node);
            if (afbClient->verbose < 0) {
                SNDERR("Invalid Boolean for %s", id);
                goto OnErrorExit;
            }
            continue;
        }

        if (strcmp(id, "request") == 0) {
            const char *callConf, *callLabel;
            snd_config_type_t ctype;
            snd_config_iterator_t currentCall, follow;
            snd_config_t *itemConf;
            int callCount=0;

            ctype = snd_config_get_type(node);
            if (ctype != SND_CONFIG_TYPE_COMPOUND) {
                snd_config_get_string(node, &callConf);
                SNDERR("Invalid compound type for %s", callConf);
                goto OnErrorExit;
            }


            // loop on each call 
            snd_config_for_each(currentCall, follow, node) {
                snd_config_t *ctlconfig = snd_config_iterator_entry(currentCall);
                
                // ignore empty line
                if (snd_config_get_id(ctlconfig, &callLabel) < 0) continue;

                // each clt should be a valid config compound
                ctype = snd_config_get_type(ctlconfig);
                if (ctype != SND_CONFIG_TYPE_COMPOUND) {
                    snd_config_get_string(node, &callConf);
                    SNDERR("Invalid call element for %s value=%s", callLabel, callConf);
                    goto OnErrorExit;
                }

                // allocate an empty call request
                afbRequest[callCount] = calloc(1, sizeof (afbRequestT));


                err = snd_config_search(ctlconfig, "api", &itemConf);
                if (!err) {
                    const char *api;
                    if (snd_config_get_string(itemConf, &api) < 0) {
                        SNDERR("Invalid api string for %s", callLabel);
                        goto OnErrorExit;
                    }
                    afbRequest[callCount]->api=strdup(api);
                }
                
                err = snd_config_search(ctlconfig, "verb", &itemConf);
                if (!err) {
                    const char *verb;
                    if (snd_config_get_string(itemConf, &verb) < 0) {
                        SNDERR("Invalid verb string %s", id);
                        goto OnErrorExit;
                    }
                    afbRequest[callCount]->verb=strdup(verb);
                }
                
                err = snd_config_search(ctlconfig, "timeout", &itemConf);
                if (!err) {
                    if (snd_config_get_integer(itemConf, &afbRequest[callCount]->timeout) < 0) {
                        SNDERR("Invalid timeout Integer %s", id);
                        goto OnErrorExit;
                    }
                }
                
                err = snd_config_search(ctlconfig, "query", &itemConf);
                if (!err) {
                    const char *query;
                    if (snd_config_get_string(itemConf, &query) < 0) {
                        SNDERR("Invalid args string %s", id);
                        goto OnErrorExit;
                    }
                    // reserve enough space to ad closing message
                    afbRequest[callCount]->length= strlen(query);
                    afbRequest[callCount]->query = malloc (afbRequest[callCount]->length+strlen(CLOSING_MSG)+1);
                    strcpy (afbRequest[callCount]->query, query);
                    
                    // cleanup string for json_tokener
                    for (int idx = 0; query[idx] != '\0'; idx++) {
                        if (query[idx] == '\'') afbRequest[callCount]->query[idx] = '"';
                        else afbRequest[callCount]->query[idx] = query[idx];
                    }
                    json_object *queryJ = json_tokener_parse(afbRequest[callCount]->query);
                    if (!queryJ) {
                        SNDERR("Invalid Json %s args=%s should be args=\"{'tok1':'val1', 'tok2':'val2'}\" ", id, afbRequest[callCount]->query);
                        goto OnErrorExit;
                    }
                }
                
                // Simple check on call request validity
                if (!afbRequest[callCount]->query)   afbRequest[callCount]->query= "";
                if (!afbRequest[callCount]->timeout) afbRequest[callCount]->timeout=REQUEST_DEFAULT_TIMEOUT ;
                if (!afbRequest[callCount]->verb || !afbRequest[callCount]->api) {
                    SNDERR("Missing api/verb %s in asoundrc", callLabel);
                    goto OnErrorExit;
                }

                // move to next call if any
                callCount ++;
                if (callCount == MAX_API_CALL) {
                    SNDERR("Too Many call MAX_API_CALL=%d", MAX_API_CALL);
                    goto OnErrorExit;                    
                }
                afbRequest[callCount]=NULL; // afbRequest array is NULL terminated

            } 
            continue;
        }
        if (strcmp(id, "event") == 0) {
            const char *callConf, *callLabel;
            snd_config_type_t ctype;
            snd_config_iterator_t currentCall, follow;
            int callCount=0;

            ctype = snd_config_get_type(node);
            if (ctype != SND_CONFIG_TYPE_COMPOUND) {
                snd_config_get_string(node, &callConf);
                SNDERR("Invalid compound type for %s", callConf);
                goto OnErrorExit;
            }


            // loop on each call 
            snd_config_for_each(currentCall, follow, node) {
                snd_config_t *ctlconfig = snd_config_iterator_entry(currentCall);
                long sigval;
                
                // ignore empty line
                if (snd_config_get_id(ctlconfig, &callLabel) < 0) continue;

                // each clt should be a valid config compound
                ctype = snd_config_get_type(ctlconfig);
                if (ctype != SND_CONFIG_TYPE_INTEGER) {
                    snd_config_get_string(ctlconfig, &callConf);
                    SNDERR("Invalid signal number for %s value=%s", callLabel, callConf);
                    goto OnErrorExit;
                }
                
                // allocate an empty call request
                snd_config_get_integer(ctlconfig, &sigval);
                afbEvent[callCount] = calloc(1, sizeof (afbEventT));
                afbEvent[callCount]->name=strdup(callLabel);
                afbEvent[callCount]->signal= (int)sigval;

                // move to next call if any
                callCount ++;
                if (callCount == MAX_EVT_CALL) {
                    SNDERR("Too Many call MAX_EVT_CALL=%d", MAX_EVT_CALL);
                    goto OnErrorExit;                    
                }
                afbEvent[callCount]=NULL; // afbEvent array is NULL terminated

            } 
            continue;
        }
    }
    
    if (afbClient->verbose) fprintf(stdout, "\nAlsaHook Install Start PCM=%s URI=%s\n", snd_pcm_name(afbClient->pcm), afbClient->uri);

    err = snd_pcm_hook_add(&h_close, afbClient->pcm, SND_PCM_HOOK_TYPE_CLOSE, AlsaCloseHook, afbClient);
    if (err < 0) goto OnErrorExit;

    // launch call request and create a waiting mainloop thread
    err = LaunchCallRequest(afbClient, HOOK_INSTALL);
    if (err < 0) {
        fprintf (stderr, "PCM Fail to Enter Mainloop\n");
        goto OnErrorExit;
    }
    
    // wait for all call request to return
    sem_wait(&afbClient->semaphore);
    if (afbClient->error) {
        fprintf (stderr, "PCM Authorisation Deny from AAAA Controller (AGL Advanced Audio Agent)\n");
        goto OnErrorExit;        
    }

    if (afbClient->verbose) fprintf(stdout, "\nAlsaHook Install Success PCM=%s URI=%s\n", snd_pcm_name(afbClient->pcm), afbClient->uri);
    return 0;

OnErrorExit:
    fprintf(stderr, "\nAlsaPcmHook Plugin Install Fail PCM=%s\n", snd_pcm_name(pcm));
    if (h_close)
        snd_pcm_hook_remove(h_close);

    return -EINVAL;
}