c@23: c@23: #include "VampJson.h" c@23: #include "VampnProto.h" c@23: c@23: #include c@23: #include c@23: #include c@23: c@23: using namespace std; c@23: using namespace json11; c@23: using namespace vampipe; c@23: c@24: // Accepting JSON objects with two fields, "type" and "content". The c@23: // "type" string corresponds to the JSON schema filename c@24: // (e.g. "outputdescriptor") and the "content" is the JSON object c@23: // encoded with that schema. c@23: c@23: class PreservingPluginHandleMapper : public PluginHandleMapper c@23: { c@23: public: c@23: PreservingPluginHandleMapper() : m_handle(0), m_plugin(0) { } c@23: c@23: virtual int32_t pluginToHandle(Vamp::Plugin *p) { c@23: if (p == m_plugin) return m_handle; c@23: else throw NotFound(); c@23: } c@23: c@23: virtual Vamp::Plugin *handleToPlugin(int32_t h) { c@23: m_handle = h; c@23: m_plugin = reinterpret_cast(h); c@23: return m_plugin; c@23: } c@23: c@23: private: c@23: int32_t m_handle; c@23: Vamp::Plugin *m_plugin; c@23: }; c@23: c@23: void usage() c@23: { c@23: string myname = "vampipe-convert"; c@23: cerr << "\n" << myname << c@24: ": Validate and convert Vamp request and response messages\n\n" c@24: " Usage: " << myname << " [-i ] [-o ] request\n" c@24: " " << myname << " [-i ] [-o ] response\n\n" c@24: " where\n" c@24: " : the format to read from stdin\n" c@24: " (\"json\" or \"capnp\", default is \"json\")\n" c@24: " : the format to convert to and write to stdout\n" c@24: " (\"json\" or \"capnp\", default is \"json\")\n" c@24: " request|response: whether to expect Vamp request or response messages\n\n" c@24: "If and differ, convert from to .\n" c@24: "If and are the same, just check validity of incoming\n" c@24: "messages and pass them to output.\n\n"; c@24: c@23: exit(2); c@23: } c@23: c@23: class RequestOrResponse c@23: { c@23: public: c@24: enum Direction { c@24: Request, Response c@24: }; c@24: c@23: RequestOrResponse() : // nothing by default c@24: direction(Request), c@25: type(RRType::NotValid), c@23: success(false), c@23: finishPlugin(0) { } c@23: c@24: Direction direction; c@25: RRType type; c@23: bool success; c@23: string errorText; c@23: c@23: PreservingPluginHandleMapper mapper; c@24: c@24: vector listResponse; c@23: Vamp::HostExt::LoadRequest loadRequest; c@23: Vamp::HostExt::LoadResponse loadResponse; c@23: Vamp::HostExt::ConfigurationRequest configurationRequest; c@23: Vamp::HostExt::ConfigurationResponse configurationResponse; c@23: Vamp::HostExt::ProcessRequest processRequest; c@23: Vamp::HostExt::ProcessResponse processResponse; c@23: Vamp::Plugin *finishPlugin; c@23: Vamp::HostExt::ProcessResponse finishResponse; c@23: }; c@23: c@24: Json c@24: convertRequestJson(string input) c@24: { c@24: string err; c@24: Json j = Json::parse(input, err); c@24: if (err != "") { c@24: throw VampJson::Failure("invalid json: " + err); c@24: } c@24: if (!j.is_object()) { c@24: throw VampJson::Failure("object expected at top level"); c@24: } c@24: if (!j["type"].is_string()) { c@24: throw VampJson::Failure("string expected for type field"); c@24: } c@24: if (!j["content"].is_object()) { c@24: throw VampJson::Failure("object expected for content field"); c@24: } c@24: return j; c@24: } c@24: c@24: Json c@24: convertResponseJson(string input) c@24: { c@24: string err; c@24: Json j = Json::parse(input, err); c@24: if (err != "") { c@24: throw VampJson::Failure("invalid json: " + err); c@24: } c@24: if (!j.is_object()) { c@24: throw VampJson::Failure("object expected at top level"); c@24: } c@24: if (!j["success"].is_bool()) { c@24: throw VampJson::Failure("bool expected for success field"); c@24: } c@24: if (!j["content"].is_object()) { c@24: throw VampJson::Failure("object expected for content field"); c@24: } c@24: return j; c@24: } c@24: c@30: //!!! Lots of potential for refactoring the conversion classes based c@30: //!!! on the common matter in the following eight functions... c@30: c@23: RequestOrResponse c@24: readRequestJson() c@23: { c@23: RequestOrResponse rr; c@24: rr.direction = RequestOrResponse::Request; c@24: c@24: string input; c@24: if (!getline(cin, input)) { c@25: rr.type = RRType::NotValid; c@24: return rr; c@24: } c@24: c@24: Json j = convertRequestJson(input); c@27: c@25: rr.type = VampJson::getRequestResponseType(j); c@24: c@27: switch (rr.type) { c@27: c@27: case RRType::List: c@27: VampJson::toVampRequest_List(j); // type check only c@27: break; c@27: case RRType::Load: c@24: rr.loadRequest = VampJson::toVampRequest_Load(j); c@27: break; c@27: case RRType::Configure: c@27: rr.configurationRequest = c@27: VampJson::toVampRequest_Configure(j, rr.mapper); c@27: break; c@27: case RRType::Process: c@24: rr.processRequest = VampJson::toVampRequest_Process(j, rr.mapper); c@27: break; c@27: case RRType::Finish: c@24: rr.finishPlugin = VampJson::toVampRequest_Finish(j, rr.mapper); c@27: break; c@27: case RRType::NotValid: c@27: break; c@24: } c@24: c@24: return rr; c@24: } c@24: c@24: void c@24: writeRequestJson(RequestOrResponse &rr) c@24: { c@24: Json j; c@24: c@27: switch (rr.type) { c@27: c@27: case RRType::List: c@24: j = VampJson::fromVampRequest_List(); c@27: break; c@27: case RRType::Load: c@24: j = VampJson::fromVampRequest_Load(rr.loadRequest); c@27: break; c@27: case RRType::Configure: c@27: j = VampJson::fromVampRequest_Configure(rr.configurationRequest, c@27: rr.mapper); c@27: break; c@27: case RRType::Process: c@24: j = VampJson::fromVampRequest_Process(rr.processRequest, rr.mapper); c@27: break; c@27: case RRType::Finish: c@24: j = VampJson::fromVampRequest_Finish(rr.finishPlugin, rr.mapper); c@27: break; c@27: case RRType::NotValid: c@27: break; c@24: } c@24: c@24: cout << j.dump() << endl; c@24: } c@24: c@24: RequestOrResponse c@24: readResponseJson() c@24: { c@24: RequestOrResponse rr; c@24: rr.direction = RequestOrResponse::Response; c@24: c@23: string input; c@23: if (!getline(cin, input)) { c@25: rr.type = RRType::NotValid; c@23: return rr; c@23: } c@23: c@24: Json j = convertResponseJson(input); c@27: c@25: rr.type = VampJson::getRequestResponseType(j); c@23: c@27: switch (rr.type) { c@27: c@27: case RRType::List: c@24: rr.listResponse = VampJson::toVampResponse_List(j); c@27: break; c@27: case RRType::Load: c@24: rr.loadResponse = VampJson::toVampResponse_Load(j, rr.mapper); c@27: break; c@27: case RRType::Configure: c@24: rr.configurationResponse = VampJson::toVampResponse_Configure(j); c@27: break; c@27: case RRType::Process: c@24: rr.processResponse = VampJson::toVampResponse_Process(j); c@27: break; c@27: case RRType::Finish: c@24: rr.finishResponse = VampJson::toVampResponse_Finish(j); c@27: break; c@27: case RRType::NotValid: c@27: break; c@23: } c@23: c@23: return rr; c@23: } c@23: c@24: void c@24: writeResponseJson(RequestOrResponse &rr) c@24: { c@24: Json j; c@24: c@27: switch (rr.type) { c@27: c@27: case RRType::List: c@24: j = VampJson::fromVampResponse_List("", rr.listResponse); c@27: break; c@27: case RRType::Load: c@24: j = VampJson::fromVampResponse_Load(rr.loadResponse, rr.mapper); c@27: break; c@27: case RRType::Configure: c@27: j = VampJson::fromVampResponse_Configure(rr.configurationResponse); c@27: break; c@27: case RRType::Process: c@27: j = VampJson::fromVampResponse_Process(rr.processResponse); c@27: break; c@27: case RRType::Finish: c@27: j = VampJson::fromVampResponse_Finish(rr.finishResponse); c@27: break; c@27: case RRType::NotValid: c@27: break; c@24: } c@24: c@27: cout << j.dump() << endl; c@27: } c@25: c@27: RequestOrResponse c@27: readRequestCapnp() c@27: { c@27: RequestOrResponse rr; c@27: rr.direction = RequestOrResponse::Request; c@27: c@27: ::capnp::PackedFdMessageReader message(0); // stdin c@27: VampRequest::Reader reader = message.getRoot(); c@27: c@27: rr.type = VampnProto::getRequestResponseType(reader); c@27: c@27: switch (rr.type) { c@27: c@27: case RRType::List: c@27: VampnProto::readVampRequest_List(reader); // type check only c@27: break; c@27: case RRType::Load: c@27: VampnProto::readVampRequest_Load(rr.loadRequest, reader); c@27: break; c@27: case RRType::Configure: c@27: VampnProto::readVampRequest_Configure(rr.configurationRequest, reader, c@27: rr.mapper); c@27: break; c@27: case RRType::Process: c@27: VampnProto::readVampRequest_Process(rr.processRequest, reader, c@27: rr.mapper); c@27: break; c@27: case RRType::Finish: c@27: VampnProto::readVampRequest_Finish(rr.finishPlugin, reader, c@27: rr.mapper); c@27: break; c@27: case RRType::NotValid: c@27: break; c@27: } c@27: c@27: return rr; c@27: } c@27: c@29: void c@29: writeRequestCapnp(RequestOrResponse &rr) c@29: { c@29: ::capnp::MallocMessageBuilder message; c@29: VampRequest::Builder builder = message.initRoot(); c@29: c@29: switch (rr.type) { c@29: c@29: case RRType::List: c@29: VampnProto::buildVampRequest_List(builder); c@29: break; c@29: case RRType::Load: c@29: VampnProto::buildVampRequest_Load(builder, rr.loadRequest); c@29: break; c@29: case RRType::Configure: c@29: VampnProto::buildVampRequest_Configure(builder, c@29: rr.configurationRequest, c@29: rr.mapper); c@29: break; c@29: case RRType::Process: c@29: VampnProto::buildVampRequest_Process(builder, c@29: rr.processRequest, c@29: rr.mapper); c@29: break; c@29: case RRType::Finish: c@29: VampnProto::buildVampRequest_Finish(builder, c@29: rr.finishPlugin, c@29: rr.mapper); c@29: break; c@29: case RRType::NotValid: c@29: break; c@29: } c@29: c@29: writePackedMessageToFd(1, message); c@29: } c@29: c@27: RequestOrResponse c@27: readResponseCapnp() c@27: { c@27: RequestOrResponse rr; c@27: rr.direction = RequestOrResponse::Response; c@27: c@27: ::capnp::PackedFdMessageReader message(0); // stdin c@27: VampResponse::Reader reader = message.getRoot(); c@27: c@27: rr.type = VampnProto::getRequestResponseType(reader); c@27: c@27: switch (rr.type) { c@27: c@27: case RRType::List: c@27: VampnProto::readVampResponse_List(rr.listResponse, reader); c@27: break; c@27: case RRType::Load: c@27: VampnProto::readVampResponse_Load(rr.loadResponse, reader, rr.mapper); c@27: break; c@27: case RRType::Configure: c@27: VampnProto::readVampResponse_Configure(rr.configurationResponse, c@27: reader); c@27: break; c@27: case RRType::Process: c@27: VampnProto::readVampResponse_Process(rr.processResponse, reader); c@27: break; c@27: case RRType::Finish: c@27: VampnProto::readVampResponse_Finish(rr.finishResponse, reader); c@27: break; c@27: case RRType::NotValid: c@27: break; c@27: } c@27: c@27: return rr; c@24: } c@24: c@29: void c@29: writeResponseCapnp(RequestOrResponse &rr) c@29: { c@29: ::capnp::MallocMessageBuilder message; c@29: VampResponse::Builder builder = message.initRoot(); c@29: c@29: switch (rr.type) { c@29: c@29: case RRType::List: c@29: VampnProto::buildVampResponse_List(builder, "", rr.listResponse); c@29: break; c@29: case RRType::Load: c@29: VampnProto::buildVampResponse_Load(builder, rr.loadResponse, rr.mapper); c@29: break; c@29: case RRType::Configure: c@29: VampnProto::buildVampResponse_Configure(builder, rr.configurationResponse); c@29: break; c@29: case RRType::Process: c@29: VampnProto::buildVampResponse_Process(builder, rr.processResponse); c@29: break; c@29: case RRType::Finish: c@29: VampnProto::buildVampResponse_Finish(builder, rr.finishResponse); c@29: break; c@29: case RRType::NotValid: c@29: break; c@29: } c@29: c@29: writePackedMessageToFd(1, message); c@29: } c@29: c@23: RequestOrResponse c@24: readInput(string format, RequestOrResponse::Direction direction) c@23: { c@23: if (format == "json") { c@24: if (direction == RequestOrResponse::Request) { c@24: return readRequestJson(); c@24: } else { c@24: return readResponseJson(); c@24: } c@27: } else if (format == "capnp") { c@27: if (direction == RequestOrResponse::Request) { c@27: return readRequestCapnp(); c@27: } else { c@27: return readResponseCapnp(); c@27: } c@23: } else { c@27: throw runtime_error("unknown input format \"" + format + "\""); c@23: } c@23: } c@23: c@23: void c@24: writeOutput(string format, RequestOrResponse &rr) c@23: { c@24: if (format == "json") { c@24: if (rr.direction == RequestOrResponse::Request) { c@24: writeRequestJson(rr); c@24: } else { c@24: writeResponseJson(rr); c@24: } c@29: } else if (format == "capnp") { c@29: if (rr.direction == RequestOrResponse::Request) { c@29: writeRequestCapnp(rr); c@29: } else { c@29: writeResponseCapnp(rr); c@29: } c@24: } else { c@27: throw runtime_error("unknown output format \"" + format + "\""); c@24: } c@23: } c@23: c@23: int main(int argc, char **argv) c@23: { c@24: if (argc < 2) { c@23: usage(); c@23: } c@23: c@24: string informat = "json", outformat = "json"; c@27: RequestOrResponse::Direction direction = RequestOrResponse::Request; c@24: bool haveDirection = false; c@23: c@24: for (int i = 1; i < argc; ++i) { c@23: c@23: string arg = argv[i]; c@24: bool final = (i + 1 == argc); c@23: c@23: if (arg == "-i") { c@27: if (final) usage(); c@23: else informat = argv[++i]; c@23: c@23: } else if (arg == "-o") { c@27: if (final) usage(); c@23: else outformat = argv[++i]; c@23: c@24: } else if (arg == "request") { c@24: direction = RequestOrResponse::Request; c@24: haveDirection = true; c@24: c@24: } else if (arg == "response") { c@24: direction = RequestOrResponse::Response; c@24: haveDirection = true; c@24: c@23: } else { c@23: usage(); c@23: } c@23: } c@23: c@24: if (informat == "" || outformat == "" || !haveDirection) { c@23: usage(); c@23: } c@23: c@23: while (true) { c@23: c@23: try { c@23: c@24: RequestOrResponse rr = readInput(informat, direction); c@29: c@29: // NotValid without an exception indicates EOF: c@25: if (rr.type == RRType::NotValid) break; c@29: c@23: writeOutput(outformat, rr); c@23: c@23: } catch (std::exception &e) { c@23: cerr << "Error: " << e.what() << endl; c@23: exit(1); c@23: } c@23: } c@23: c@23: exit(0); c@23: } c@23: c@23: