c@23
|
1
|
c@23
|
2 #include "VampJson.h"
|
c@23
|
3 #include "VampnProto.h"
|
c@23
|
4
|
c@23
|
5 #include <iostream>
|
c@23
|
6 #include <sstream>
|
c@23
|
7 #include <stdexcept>
|
c@23
|
8
|
c@23
|
9 using namespace std;
|
c@23
|
10 using namespace json11;
|
c@23
|
11 using namespace vampipe;
|
c@23
|
12
|
c@24
|
13 // Accepting JSON objects with two fields, "type" and "content". The
|
c@23
|
14 // "type" string corresponds to the JSON schema filename
|
c@24
|
15 // (e.g. "outputdescriptor") and the "content" is the JSON object
|
c@23
|
16 // encoded with that schema.
|
c@23
|
17
|
c@23
|
18 class PreservingPluginHandleMapper : public PluginHandleMapper
|
c@23
|
19 {
|
c@23
|
20 public:
|
c@23
|
21 PreservingPluginHandleMapper() : m_handle(0), m_plugin(0) { }
|
c@23
|
22
|
c@23
|
23 virtual int32_t pluginToHandle(Vamp::Plugin *p) {
|
c@23
|
24 if (p == m_plugin) return m_handle;
|
c@23
|
25 else throw NotFound();
|
c@23
|
26 }
|
c@23
|
27
|
c@23
|
28 virtual Vamp::Plugin *handleToPlugin(int32_t h) {
|
c@23
|
29 m_handle = h;
|
c@23
|
30 m_plugin = reinterpret_cast<Vamp::Plugin *>(h);
|
c@23
|
31 return m_plugin;
|
c@23
|
32 }
|
c@23
|
33
|
c@23
|
34 private:
|
c@23
|
35 int32_t m_handle;
|
c@23
|
36 Vamp::Plugin *m_plugin;
|
c@23
|
37 };
|
c@23
|
38
|
c@23
|
39 void usage()
|
c@23
|
40 {
|
c@23
|
41 string myname = "vampipe-convert";
|
c@23
|
42 cerr << "\n" << myname <<
|
c@24
|
43 ": Validate and convert Vamp request and response messages\n\n"
|
c@24
|
44 " Usage: " << myname << " [-i <informat>] [-o <outformat>] request\n"
|
c@24
|
45 " " << myname << " [-i <informat>] [-o <outformat>] response\n\n"
|
c@24
|
46 " where\n"
|
c@24
|
47 " <informat>: the format to read from stdin\n"
|
c@24
|
48 " (\"json\" or \"capnp\", default is \"json\")\n"
|
c@24
|
49 " <outformat>: the format to convert to and write to stdout\n"
|
c@24
|
50 " (\"json\" or \"capnp\", default is \"json\")\n"
|
c@24
|
51 " request|response: whether to expect Vamp request or response messages\n\n"
|
c@24
|
52 "If <informat> and <outformat> differ, convert from <informat> to <outformat>.\n"
|
c@24
|
53 "If <informat> and <outformat> are the same, just check validity of incoming\n"
|
c@24
|
54 "messages and pass them to output.\n\n";
|
c@24
|
55
|
c@23
|
56 exit(2);
|
c@23
|
57 }
|
c@23
|
58
|
c@23
|
59 class RequestOrResponse
|
c@23
|
60 {
|
c@23
|
61 public:
|
c@24
|
62 enum Direction {
|
c@24
|
63 Request, Response
|
c@24
|
64 };
|
c@24
|
65
|
c@23
|
66 RequestOrResponse() : // nothing by default
|
c@24
|
67 direction(Request),
|
c@25
|
68 type(RRType::NotValid),
|
c@23
|
69 success(false),
|
c@23
|
70 finishPlugin(0) { }
|
c@23
|
71
|
c@24
|
72 Direction direction;
|
c@25
|
73 RRType type;
|
c@23
|
74 bool success;
|
c@23
|
75 string errorText;
|
c@23
|
76
|
c@23
|
77 PreservingPluginHandleMapper mapper;
|
c@24
|
78
|
c@24
|
79 vector<Vamp::HostExt::PluginStaticData> listResponse;
|
c@23
|
80 Vamp::HostExt::LoadRequest loadRequest;
|
c@23
|
81 Vamp::HostExt::LoadResponse loadResponse;
|
c@23
|
82 Vamp::HostExt::ConfigurationRequest configurationRequest;
|
c@23
|
83 Vamp::HostExt::ConfigurationResponse configurationResponse;
|
c@23
|
84 Vamp::HostExt::ProcessRequest processRequest;
|
c@23
|
85 Vamp::HostExt::ProcessResponse processResponse;
|
c@23
|
86 Vamp::Plugin *finishPlugin;
|
c@23
|
87 Vamp::HostExt::ProcessResponse finishResponse;
|
c@23
|
88 };
|
c@23
|
89
|
c@24
|
90 Json
|
c@24
|
91 convertRequestJson(string input)
|
c@24
|
92 {
|
c@24
|
93 string err;
|
c@24
|
94 Json j = Json::parse(input, err);
|
c@24
|
95 if (err != "") {
|
c@24
|
96 throw VampJson::Failure("invalid json: " + err);
|
c@24
|
97 }
|
c@24
|
98 if (!j.is_object()) {
|
c@24
|
99 throw VampJson::Failure("object expected at top level");
|
c@24
|
100 }
|
c@24
|
101 if (!j["type"].is_string()) {
|
c@24
|
102 throw VampJson::Failure("string expected for type field");
|
c@24
|
103 }
|
c@24
|
104 if (!j["content"].is_object()) {
|
c@24
|
105 throw VampJson::Failure("object expected for content field");
|
c@24
|
106 }
|
c@24
|
107 return j;
|
c@24
|
108 }
|
c@24
|
109
|
c@24
|
110 Json
|
c@24
|
111 convertResponseJson(string input)
|
c@24
|
112 {
|
c@24
|
113 string err;
|
c@24
|
114 Json j = Json::parse(input, err);
|
c@24
|
115 if (err != "") {
|
c@24
|
116 throw VampJson::Failure("invalid json: " + err);
|
c@24
|
117 }
|
c@24
|
118 if (!j.is_object()) {
|
c@24
|
119 throw VampJson::Failure("object expected at top level");
|
c@24
|
120 }
|
c@24
|
121 if (!j["success"].is_bool()) {
|
c@24
|
122 throw VampJson::Failure("bool expected for success field");
|
c@24
|
123 }
|
c@24
|
124 if (!j["content"].is_object()) {
|
c@24
|
125 throw VampJson::Failure("object expected for content field");
|
c@24
|
126 }
|
c@24
|
127 return j;
|
c@24
|
128 }
|
c@24
|
129
|
c@30
|
130 //!!! Lots of potential for refactoring the conversion classes based
|
c@30
|
131 //!!! on the common matter in the following eight functions...
|
c@30
|
132
|
c@23
|
133 RequestOrResponse
|
c@24
|
134 readRequestJson()
|
c@23
|
135 {
|
c@23
|
136 RequestOrResponse rr;
|
c@24
|
137 rr.direction = RequestOrResponse::Request;
|
c@24
|
138
|
c@24
|
139 string input;
|
c@24
|
140 if (!getline(cin, input)) {
|
c@25
|
141 rr.type = RRType::NotValid;
|
c@24
|
142 return rr;
|
c@24
|
143 }
|
c@24
|
144
|
c@24
|
145 Json j = convertRequestJson(input);
|
c@27
|
146
|
c@25
|
147 rr.type = VampJson::getRequestResponseType(j);
|
c@24
|
148
|
c@27
|
149 switch (rr.type) {
|
c@27
|
150
|
c@27
|
151 case RRType::List:
|
c@27
|
152 VampJson::toVampRequest_List(j); // type check only
|
c@27
|
153 break;
|
c@27
|
154 case RRType::Load:
|
c@24
|
155 rr.loadRequest = VampJson::toVampRequest_Load(j);
|
c@27
|
156 break;
|
c@27
|
157 case RRType::Configure:
|
c@27
|
158 rr.configurationRequest =
|
c@27
|
159 VampJson::toVampRequest_Configure(j, rr.mapper);
|
c@27
|
160 break;
|
c@27
|
161 case RRType::Process:
|
c@24
|
162 rr.processRequest = VampJson::toVampRequest_Process(j, rr.mapper);
|
c@27
|
163 break;
|
c@27
|
164 case RRType::Finish:
|
c@24
|
165 rr.finishPlugin = VampJson::toVampRequest_Finish(j, rr.mapper);
|
c@27
|
166 break;
|
c@27
|
167 case RRType::NotValid:
|
c@27
|
168 break;
|
c@24
|
169 }
|
c@24
|
170
|
c@24
|
171 return rr;
|
c@24
|
172 }
|
c@24
|
173
|
c@24
|
174 void
|
c@24
|
175 writeRequestJson(RequestOrResponse &rr)
|
c@24
|
176 {
|
c@24
|
177 Json j;
|
c@24
|
178
|
c@27
|
179 switch (rr.type) {
|
c@27
|
180
|
c@27
|
181 case RRType::List:
|
c@24
|
182 j = VampJson::fromVampRequest_List();
|
c@27
|
183 break;
|
c@27
|
184 case RRType::Load:
|
c@24
|
185 j = VampJson::fromVampRequest_Load(rr.loadRequest);
|
c@27
|
186 break;
|
c@27
|
187 case RRType::Configure:
|
c@27
|
188 j = VampJson::fromVampRequest_Configure(rr.configurationRequest,
|
c@27
|
189 rr.mapper);
|
c@27
|
190 break;
|
c@27
|
191 case RRType::Process:
|
c@24
|
192 j = VampJson::fromVampRequest_Process(rr.processRequest, rr.mapper);
|
c@27
|
193 break;
|
c@27
|
194 case RRType::Finish:
|
c@24
|
195 j = VampJson::fromVampRequest_Finish(rr.finishPlugin, rr.mapper);
|
c@27
|
196 break;
|
c@27
|
197 case RRType::NotValid:
|
c@27
|
198 break;
|
c@24
|
199 }
|
c@24
|
200
|
c@24
|
201 cout << j.dump() << endl;
|
c@24
|
202 }
|
c@24
|
203
|
c@24
|
204 RequestOrResponse
|
c@24
|
205 readResponseJson()
|
c@24
|
206 {
|
c@24
|
207 RequestOrResponse rr;
|
c@24
|
208 rr.direction = RequestOrResponse::Response;
|
c@24
|
209
|
c@23
|
210 string input;
|
c@23
|
211 if (!getline(cin, input)) {
|
c@25
|
212 rr.type = RRType::NotValid;
|
c@23
|
213 return rr;
|
c@23
|
214 }
|
c@23
|
215
|
c@24
|
216 Json j = convertResponseJson(input);
|
c@27
|
217
|
c@25
|
218 rr.type = VampJson::getRequestResponseType(j);
|
c@23
|
219
|
c@27
|
220 switch (rr.type) {
|
c@27
|
221
|
c@27
|
222 case RRType::List:
|
c@24
|
223 rr.listResponse = VampJson::toVampResponse_List(j);
|
c@27
|
224 break;
|
c@27
|
225 case RRType::Load:
|
c@24
|
226 rr.loadResponse = VampJson::toVampResponse_Load(j, rr.mapper);
|
c@27
|
227 break;
|
c@27
|
228 case RRType::Configure:
|
c@24
|
229 rr.configurationResponse = VampJson::toVampResponse_Configure(j);
|
c@27
|
230 break;
|
c@27
|
231 case RRType::Process:
|
c@24
|
232 rr.processResponse = VampJson::toVampResponse_Process(j);
|
c@27
|
233 break;
|
c@27
|
234 case RRType::Finish:
|
c@24
|
235 rr.finishResponse = VampJson::toVampResponse_Finish(j);
|
c@27
|
236 break;
|
c@27
|
237 case RRType::NotValid:
|
c@27
|
238 break;
|
c@23
|
239 }
|
c@23
|
240
|
c@23
|
241 return rr;
|
c@23
|
242 }
|
c@23
|
243
|
c@24
|
244 void
|
c@24
|
245 writeResponseJson(RequestOrResponse &rr)
|
c@24
|
246 {
|
c@24
|
247 Json j;
|
c@24
|
248
|
c@27
|
249 switch (rr.type) {
|
c@27
|
250
|
c@27
|
251 case RRType::List:
|
c@24
|
252 j = VampJson::fromVampResponse_List("", rr.listResponse);
|
c@27
|
253 break;
|
c@27
|
254 case RRType::Load:
|
c@24
|
255 j = VampJson::fromVampResponse_Load(rr.loadResponse, rr.mapper);
|
c@27
|
256 break;
|
c@27
|
257 case RRType::Configure:
|
c@27
|
258 j = VampJson::fromVampResponse_Configure(rr.configurationResponse);
|
c@27
|
259 break;
|
c@27
|
260 case RRType::Process:
|
c@27
|
261 j = VampJson::fromVampResponse_Process(rr.processResponse);
|
c@27
|
262 break;
|
c@27
|
263 case RRType::Finish:
|
c@27
|
264 j = VampJson::fromVampResponse_Finish(rr.finishResponse);
|
c@27
|
265 break;
|
c@27
|
266 case RRType::NotValid:
|
c@27
|
267 break;
|
c@24
|
268 }
|
c@24
|
269
|
c@27
|
270 cout << j.dump() << endl;
|
c@27
|
271 }
|
c@25
|
272
|
c@27
|
273 RequestOrResponse
|
c@27
|
274 readRequestCapnp()
|
c@27
|
275 {
|
c@27
|
276 RequestOrResponse rr;
|
c@27
|
277 rr.direction = RequestOrResponse::Request;
|
c@27
|
278
|
c@27
|
279 ::capnp::PackedFdMessageReader message(0); // stdin
|
c@27
|
280 VampRequest::Reader reader = message.getRoot<VampRequest>();
|
c@27
|
281
|
c@27
|
282 rr.type = VampnProto::getRequestResponseType(reader);
|
c@27
|
283
|
c@27
|
284 switch (rr.type) {
|
c@27
|
285
|
c@27
|
286 case RRType::List:
|
c@27
|
287 VampnProto::readVampRequest_List(reader); // type check only
|
c@27
|
288 break;
|
c@27
|
289 case RRType::Load:
|
c@27
|
290 VampnProto::readVampRequest_Load(rr.loadRequest, reader);
|
c@27
|
291 break;
|
c@27
|
292 case RRType::Configure:
|
c@27
|
293 VampnProto::readVampRequest_Configure(rr.configurationRequest, reader,
|
c@27
|
294 rr.mapper);
|
c@27
|
295 break;
|
c@27
|
296 case RRType::Process:
|
c@27
|
297 VampnProto::readVampRequest_Process(rr.processRequest, reader,
|
c@27
|
298 rr.mapper);
|
c@27
|
299 break;
|
c@27
|
300 case RRType::Finish:
|
c@27
|
301 VampnProto::readVampRequest_Finish(rr.finishPlugin, reader,
|
c@27
|
302 rr.mapper);
|
c@27
|
303 break;
|
c@27
|
304 case RRType::NotValid:
|
c@27
|
305 break;
|
c@27
|
306 }
|
c@27
|
307
|
c@27
|
308 return rr;
|
c@27
|
309 }
|
c@27
|
310
|
c@29
|
311 void
|
c@29
|
312 writeRequestCapnp(RequestOrResponse &rr)
|
c@29
|
313 {
|
c@29
|
314 ::capnp::MallocMessageBuilder message;
|
c@29
|
315 VampRequest::Builder builder = message.initRoot<VampRequest>();
|
c@29
|
316
|
c@29
|
317 switch (rr.type) {
|
c@29
|
318
|
c@29
|
319 case RRType::List:
|
c@29
|
320 VampnProto::buildVampRequest_List(builder);
|
c@29
|
321 break;
|
c@29
|
322 case RRType::Load:
|
c@29
|
323 VampnProto::buildVampRequest_Load(builder, rr.loadRequest);
|
c@29
|
324 break;
|
c@29
|
325 case RRType::Configure:
|
c@29
|
326 VampnProto::buildVampRequest_Configure(builder,
|
c@29
|
327 rr.configurationRequest,
|
c@29
|
328 rr.mapper);
|
c@29
|
329 break;
|
c@29
|
330 case RRType::Process:
|
c@29
|
331 VampnProto::buildVampRequest_Process(builder,
|
c@29
|
332 rr.processRequest,
|
c@29
|
333 rr.mapper);
|
c@29
|
334 break;
|
c@29
|
335 case RRType::Finish:
|
c@29
|
336 VampnProto::buildVampRequest_Finish(builder,
|
c@29
|
337 rr.finishPlugin,
|
c@29
|
338 rr.mapper);
|
c@29
|
339 break;
|
c@29
|
340 case RRType::NotValid:
|
c@29
|
341 break;
|
c@29
|
342 }
|
c@29
|
343
|
c@29
|
344 writePackedMessageToFd(1, message);
|
c@29
|
345 }
|
c@29
|
346
|
c@27
|
347 RequestOrResponse
|
c@27
|
348 readResponseCapnp()
|
c@27
|
349 {
|
c@27
|
350 RequestOrResponse rr;
|
c@27
|
351 rr.direction = RequestOrResponse::Response;
|
c@27
|
352
|
c@27
|
353 ::capnp::PackedFdMessageReader message(0); // stdin
|
c@27
|
354 VampResponse::Reader reader = message.getRoot<VampResponse>();
|
c@27
|
355
|
c@27
|
356 rr.type = VampnProto::getRequestResponseType(reader);
|
c@27
|
357
|
c@27
|
358 switch (rr.type) {
|
c@27
|
359
|
c@27
|
360 case RRType::List:
|
c@27
|
361 VampnProto::readVampResponse_List(rr.listResponse, reader);
|
c@27
|
362 break;
|
c@27
|
363 case RRType::Load:
|
c@27
|
364 VampnProto::readVampResponse_Load(rr.loadResponse, reader, rr.mapper);
|
c@27
|
365 break;
|
c@27
|
366 case RRType::Configure:
|
c@27
|
367 VampnProto::readVampResponse_Configure(rr.configurationResponse,
|
c@27
|
368 reader);
|
c@27
|
369 break;
|
c@27
|
370 case RRType::Process:
|
c@27
|
371 VampnProto::readVampResponse_Process(rr.processResponse, reader);
|
c@27
|
372 break;
|
c@27
|
373 case RRType::Finish:
|
c@27
|
374 VampnProto::readVampResponse_Finish(rr.finishResponse, reader);
|
c@27
|
375 break;
|
c@27
|
376 case RRType::NotValid:
|
c@27
|
377 break;
|
c@27
|
378 }
|
c@27
|
379
|
c@27
|
380 return rr;
|
c@24
|
381 }
|
c@24
|
382
|
c@29
|
383 void
|
c@29
|
384 writeResponseCapnp(RequestOrResponse &rr)
|
c@29
|
385 {
|
c@29
|
386 ::capnp::MallocMessageBuilder message;
|
c@29
|
387 VampResponse::Builder builder = message.initRoot<VampResponse>();
|
c@29
|
388
|
c@29
|
389 switch (rr.type) {
|
c@29
|
390
|
c@29
|
391 case RRType::List:
|
c@29
|
392 VampnProto::buildVampResponse_List(builder, "", rr.listResponse);
|
c@29
|
393 break;
|
c@29
|
394 case RRType::Load:
|
c@29
|
395 VampnProto::buildVampResponse_Load(builder, rr.loadResponse, rr.mapper);
|
c@29
|
396 break;
|
c@29
|
397 case RRType::Configure:
|
c@29
|
398 VampnProto::buildVampResponse_Configure(builder, rr.configurationResponse);
|
c@29
|
399 break;
|
c@29
|
400 case RRType::Process:
|
c@29
|
401 VampnProto::buildVampResponse_Process(builder, rr.processResponse);
|
c@29
|
402 break;
|
c@29
|
403 case RRType::Finish:
|
c@29
|
404 VampnProto::buildVampResponse_Finish(builder, rr.finishResponse);
|
c@29
|
405 break;
|
c@29
|
406 case RRType::NotValid:
|
c@29
|
407 break;
|
c@29
|
408 }
|
c@29
|
409
|
c@29
|
410 writePackedMessageToFd(1, message);
|
c@29
|
411 }
|
c@29
|
412
|
c@23
|
413 RequestOrResponse
|
c@24
|
414 readInput(string format, RequestOrResponse::Direction direction)
|
c@23
|
415 {
|
c@23
|
416 if (format == "json") {
|
c@24
|
417 if (direction == RequestOrResponse::Request) {
|
c@24
|
418 return readRequestJson();
|
c@24
|
419 } else {
|
c@24
|
420 return readResponseJson();
|
c@24
|
421 }
|
c@27
|
422 } else if (format == "capnp") {
|
c@27
|
423 if (direction == RequestOrResponse::Request) {
|
c@27
|
424 return readRequestCapnp();
|
c@27
|
425 } else {
|
c@27
|
426 return readResponseCapnp();
|
c@27
|
427 }
|
c@23
|
428 } else {
|
c@27
|
429 throw runtime_error("unknown input format \"" + format + "\"");
|
c@23
|
430 }
|
c@23
|
431 }
|
c@23
|
432
|
c@23
|
433 void
|
c@24
|
434 writeOutput(string format, RequestOrResponse &rr)
|
c@23
|
435 {
|
c@24
|
436 if (format == "json") {
|
c@24
|
437 if (rr.direction == RequestOrResponse::Request) {
|
c@24
|
438 writeRequestJson(rr);
|
c@24
|
439 } else {
|
c@24
|
440 writeResponseJson(rr);
|
c@24
|
441 }
|
c@29
|
442 } else if (format == "capnp") {
|
c@29
|
443 if (rr.direction == RequestOrResponse::Request) {
|
c@29
|
444 writeRequestCapnp(rr);
|
c@29
|
445 } else {
|
c@29
|
446 writeResponseCapnp(rr);
|
c@29
|
447 }
|
c@24
|
448 } else {
|
c@27
|
449 throw runtime_error("unknown output format \"" + format + "\"");
|
c@24
|
450 }
|
c@23
|
451 }
|
c@23
|
452
|
c@23
|
453 int main(int argc, char **argv)
|
c@23
|
454 {
|
c@24
|
455 if (argc < 2) {
|
c@23
|
456 usage();
|
c@23
|
457 }
|
c@23
|
458
|
c@24
|
459 string informat = "json", outformat = "json";
|
c@27
|
460 RequestOrResponse::Direction direction = RequestOrResponse::Request;
|
c@24
|
461 bool haveDirection = false;
|
c@23
|
462
|
c@24
|
463 for (int i = 1; i < argc; ++i) {
|
c@23
|
464
|
c@23
|
465 string arg = argv[i];
|
c@24
|
466 bool final = (i + 1 == argc);
|
c@23
|
467
|
c@23
|
468 if (arg == "-i") {
|
c@27
|
469 if (final) usage();
|
c@23
|
470 else informat = argv[++i];
|
c@23
|
471
|
c@23
|
472 } else if (arg == "-o") {
|
c@27
|
473 if (final) usage();
|
c@23
|
474 else outformat = argv[++i];
|
c@23
|
475
|
c@24
|
476 } else if (arg == "request") {
|
c@24
|
477 direction = RequestOrResponse::Request;
|
c@24
|
478 haveDirection = true;
|
c@24
|
479
|
c@24
|
480 } else if (arg == "response") {
|
c@24
|
481 direction = RequestOrResponse::Response;
|
c@24
|
482 haveDirection = true;
|
c@24
|
483
|
c@23
|
484 } else {
|
c@23
|
485 usage();
|
c@23
|
486 }
|
c@23
|
487 }
|
c@23
|
488
|
c@24
|
489 if (informat == "" || outformat == "" || !haveDirection) {
|
c@23
|
490 usage();
|
c@23
|
491 }
|
c@23
|
492
|
c@23
|
493 while (true) {
|
c@23
|
494
|
c@23
|
495 try {
|
c@23
|
496
|
c@24
|
497 RequestOrResponse rr = readInput(informat, direction);
|
c@29
|
498
|
c@29
|
499 // NotValid without an exception indicates EOF:
|
c@25
|
500 if (rr.type == RRType::NotValid) break;
|
c@29
|
501
|
c@23
|
502 writeOutput(outformat, rr);
|
c@23
|
503
|
c@23
|
504 } catch (std::exception &e) {
|
c@23
|
505 cerr << "Error: " << e.what() << endl;
|
c@23
|
506 exit(1);
|
c@23
|
507 }
|
c@23
|
508 }
|
c@23
|
509
|
c@23
|
510 exit(0);
|
c@23
|
511 }
|
c@23
|
512
|
c@23
|
513
|