1 /*
2  *  API - service related calls
3  *
4  *  Copyright (C) 2013 Adam Sutton
5  *
6  *  This program is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef __TVH_API_SERVICE_H__
21 #define __TVH_API_SERVICE_H__
22 
23 #include "tvheadend.h"
24 #include "service.h"
25 #include "service_mapper.h"
26 #include "access.h"
27 #include "api.h"
28 #include "notify.h"
29 
30 static int
api_mapper_stop(access_t * perm,void * opaque,const char * op,htsmsg_t * args,htsmsg_t ** resp)31 api_mapper_stop
32   ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
33 {
34   pthread_mutex_lock(&global_lock);
35   service_mapper_stop();
36   pthread_mutex_unlock(&global_lock);
37 
38   return 0;
39 }
40 
41 static htsmsg_t *
api_mapper_status_msg(void)42 api_mapper_status_msg ( void )
43 {
44   htsmsg_t *m;
45   char ubuf[UUID_HEX_SIZE];
46   service_mapper_status_t stat = service_mapper_status();
47   m = htsmsg_create_map();
48   htsmsg_add_u32(m, "total",  stat.total);
49   htsmsg_add_u32(m, "ok",     stat.ok);
50   htsmsg_add_u32(m, "fail",   stat.fail);
51   htsmsg_add_u32(m, "ignore", stat.ignore);
52   if (stat.active)
53     htsmsg_add_str(m, "active", idnode_uuid_as_str(&stat.active->s_id, ubuf));
54   return m;
55 }
56 
57 static int
api_mapper_status(access_t * perm,void * opaque,const char * op,htsmsg_t * args,htsmsg_t ** resp)58 api_mapper_status
59   ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
60 {
61   pthread_mutex_lock(&global_lock);
62   *resp = api_mapper_status_msg();
63   pthread_mutex_unlock(&global_lock);
64   return 0;
65 }
66 
67 void
api_service_mapper_notify(void)68 api_service_mapper_notify ( void )
69 {
70   notify_by_msg("servicemapper", api_mapper_status_msg(), 0);
71 }
72 
73 static htsmsg_t *
api_service_streams_get_one(elementary_stream_t * es,int use_filter)74 api_service_streams_get_one ( elementary_stream_t *es, int use_filter )
75 {
76   htsmsg_t *e = htsmsg_create_map();
77   htsmsg_add_u32(e, "index",    es->es_index);
78   htsmsg_add_u32(e, "pid",      es->es_pid);
79   htsmsg_add_str(e, "type",     streaming_component_type2txt(es->es_type));
80   htsmsg_add_str(e, "language", es->es_lang);
81   if (SCT_ISSUBTITLE(es->es_type)) {
82     htsmsg_add_u32(e, "composition_id", es->es_composition_id);
83     htsmsg_add_u32(e, "ancillary_id",   es->es_ancillary_id);
84   } else if (SCT_ISAUDIO(es->es_type)) {
85     htsmsg_add_u32(e, "audio_type",     es->es_audio_type);
86     if (es->es_audio_version)
87       htsmsg_add_u32(e, "audio_version", es->es_audio_version);
88   } else if (SCT_ISVIDEO(es->es_type)) {
89     htsmsg_add_u32(e, "width",          es->es_width);
90     htsmsg_add_u32(e, "height",         es->es_height);
91     htsmsg_add_u32(e, "duration",       es->es_frame_duration);
92     htsmsg_add_u32(e, "aspect_num",     es->es_aspect_num);
93     htsmsg_add_u32(e, "aspect_den",     es->es_aspect_den);
94   } else if (es->es_type == SCT_CA) {
95     caid_t *ca;
96     htsmsg_t *e2, *l2 = htsmsg_create_list();
97     LIST_FOREACH(ca, &es->es_caids, link) {
98       if (use_filter && !ca->use)
99         continue;
100       e2 = htsmsg_create_map();
101       htsmsg_add_u32(e2, "caid",     ca->caid);
102       htsmsg_add_u32(e2, "provider", ca->providerid);
103       htsmsg_add_msg(l2, NULL, e2);
104     }
105     htsmsg_add_msg(e, "caids", l2);
106   }
107   return e;
108 }
109 
110 static int
api_service_streams(access_t * perm,void * opaque,const char * op,htsmsg_t * args,htsmsg_t ** resp)111 api_service_streams
112   ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
113 {
114   const char *uuid;
115   htsmsg_t *e, *st, *stf;
116   service_t *s;
117   elementary_stream_t *es;
118 
119   /* No UUID */
120   if (!(uuid = htsmsg_get_str(args, "uuid")))
121     return EINVAL;
122 
123   pthread_mutex_lock(&global_lock);
124 
125   /* Couldn't find */
126   if (!(s = service_find(uuid))) {
127     pthread_mutex_unlock(&global_lock);
128     return EINVAL;
129   }
130 
131   /* Build response */
132   pthread_mutex_lock(&s->s_stream_mutex);
133   st = htsmsg_create_list();
134   stf = htsmsg_create_list();
135   if (s->s_pcr_pid) {
136     e = htsmsg_create_map();
137     htsmsg_add_u32(e, "pid", s->s_pcr_pid);
138     htsmsg_add_str(e, "type", "PCR");
139     htsmsg_add_msg(st, NULL, e);
140   }
141   if (s->s_pmt_pid) {
142     e = htsmsg_create_map();
143     htsmsg_add_u32(e, "pid", s->s_pmt_pid);
144     htsmsg_add_str(e, "type", "PMT");
145     htsmsg_add_msg(st, NULL, e);
146   }
147   TAILQ_FOREACH(es, &s->s_components, es_link)
148     htsmsg_add_msg(st, NULL, api_service_streams_get_one(es, 0));
149   if (TAILQ_FIRST(&s->s_filt_components) == NULL ||
150       s->s_status == SERVICE_IDLE)
151     service_build_filter(s);
152   TAILQ_FOREACH(es, &s->s_filt_components, es_filt_link)
153     htsmsg_add_msg(stf, NULL, api_service_streams_get_one(es, 1));
154   *resp = htsmsg_create_map();
155   htsmsg_add_str(*resp, "name", s->s_nicename);
156   htsmsg_add_msg(*resp, "streams", st);
157   htsmsg_add_msg(*resp, "fstreams", stf);
158   pthread_mutex_unlock(&s->s_stream_mutex);
159 
160   /* Done */
161   pthread_mutex_unlock(&global_lock);
162   return 0;
163 }
164 
165 static int
api_service_remove_unseen(access_t * perm,void * opaque,const char * op,htsmsg_t * args,htsmsg_t ** resp)166 api_service_remove_unseen
167   ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
168 {
169   int days = htsmsg_get_s32_or_default(args, "days", 7);
170   const char *type = htsmsg_get_str(args, "type");
171 
172   pthread_mutex_lock(&global_lock);
173   service_remove_unseen(type, days);
174   pthread_mutex_unlock(&global_lock);
175   return 0;
176 }
177 
api_service_init(void)178 void api_service_init ( void )
179 {
180   extern const idclass_t service_class;
181   static api_hook_t ah[] = {
182     { "service/mapper/load",    ACCESS_ADMIN, api_idnode_load_simple, &service_mapper_conf },
183     { "service/mapper/save",    ACCESS_ADMIN, api_idnode_save_simple, &service_mapper_conf },
184     { "service/mapper/stop",    ACCESS_ADMIN, api_mapper_stop,   NULL },
185     { "service/mapper/status",  ACCESS_ADMIN, api_mapper_status, NULL },
186     { "service/list",           ACCESS_ADMIN, api_idnode_load_by_class, (void*)&service_class },
187     { "service/streams",        ACCESS_ADMIN, api_service_streams, NULL },
188     { "service/removeunseen",   ACCESS_ADMIN, api_service_remove_unseen, NULL },
189     { NULL },
190   };
191 
192   api_register_all(ah);
193 }
194 
195 
196 #endif /* __TVH_API_SERVICE_H__ */
197