<commit_before>/*
 * opencog/server/CogServer.cc
 *
 * Copyright (C) 2002-2007 Novamente LLC
 * Copyright (C) 2008 by Singularity Institute for Artificial Intelligence
 * All Rights Reserved
 *
 * Written by Andre Senna <senna@vettalabs.com>
 *            Gustavo Gama <gama@vettalabs.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * 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 Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "CogServer.h"

#include <tr1/memory>
#include <tr1/functional>

#include <time.h>
#ifdef WIN32
#include <winsock2.h>
#else
#include <dlfcn.h>
#include <unistd.h>
#include <sys/time.h>
#endif

#include <opencog/atomspace/AtomSpace.h>
#include <opencog/server/Agent.h>
#include <opencog/server/ConsoleSocket.h>
#include <opencog/server/NetworkServer.h>
#include <opencog/server/Request.h>
#include <opencog/util/Config.h>
#include <opencog/util/Logger.h>
#include <opencog/util/exceptions.h>
#include <opencog/util/misc.h>
#include "load-file.h"

using namespace opencog;

namespace opencog {
struct equal_to_id : public std::binary_function<const Agent*, const std::string&, bool>
{
    bool operator()(const Agent* a, const std::string& cid) const {
        return (a->classinfo().id != cid);
    }
};
}

CogServer* CogServer::createInstance() {
    return new CogServer();
}

CogServer::~CogServer()
{
    disableNetworkServer();

    // unload all modules
    for (ModuleMap::iterator it = modules.begin(); it != modules.end(); ++it) {
        // retest the key because it might have been removed already
        if (modules.find(it->first) != modules.end()) {
            logger().debug("[CogServer] removing module %s\"", it->first.c_str());
            ModuleData mdata = it->second;

            // cache filename and id to erase the entries from the modules map
            std::string filename = mdata.filename;
            std::string id = mdata.id;

            // invoke the module's unload function
            (*mdata.unloadFunction)(mdata.module);

            // erase the map entries (one with the filename as key, and one with the module)
            // id as key
            modules.erase(filename);
            modules.erase(id);
        }
    }
}

CogServer::CogServer() : cycleCount(1)
{
    if (atomSpace != NULL) delete atomSpace;
    atomSpace = new AtomSpace();

    pthread_mutex_init(&messageQueueLock, NULL);
}

NetworkServer& CogServer::networkServer()
{
    return _networkServer;
}

void CogServer::enableNetworkServer()
{
    _networkServer.start();
    _networkServer.addListener<ConsoleSocket>(config().get_int("SERVER_PORT"));

}

void CogServer::disableNetworkServer()
{
    _networkServer.stop();
}

void CogServer::serverLoop()
{
    struct timeval timer_start, timer_end;
    time_t elapsed_time;
    time_t cycle_duration = config().get_int("SERVER_CYCLE_DURATION") * 1000;

    // XXX I've hard-coded a file path here, it assumes that the
    // cog server is running from the "bin" directory, (where "make"
    // was typed). Iwithout a more general file-path mangling mechanism,
    // I am not sure what else to do. But this should be fixed XXX.
    load_scm_file("../src/scm/type_constructors.scm");

    logger().info("opencog server ready.");

    gettimeofday(&timer_start, NULL);
    for (running = true; running;) {

        if (getRequestQueueSize() != 0) {
            processRequests();
        }

        processAgents();

        cycleCount++;
        if (cycleCount < 0) cycleCount = 0;

        // sleep long enough so that the next cycle will only start
        // after config["SERVER_CYCLE_DURATION"] milliseconds
        gettimeofday(&timer_end, NULL);
        elapsed_time = ((timer_end.tv_sec - timer_start.tv_sec) * 1000000) +
                       (timer_end.tv_usec - timer_start.tv_usec);
        if ((cycle_duration - elapsed_time) > 0)
            usleep((unsigned int) (cycle_duration - elapsed_time));
        timer_start = timer_end;
    }
}

void CogServer::processRequests(void)
{
    Request* request;
    while ((request = popRequest()) != NULL) {
        request->execute();
        delete request;
    }
}

void CogServer::processAgents(void)
{
    std::vector<Agent*>::const_iterator it;
    for (it = agents.begin(); it != agents.end(); ++it) {
        Agent* agent = *it;
        if ((cycleCount % agent->frequency()) == 0)
            agent->run(this);
    }
}

bool CogServer::registerAgent(const std::string& id, AbstractFactory<Agent> const* factory)
{
    return Registry<Agent>::register_(id, factory);
}

bool CogServer::unregisterAgent(const std::string& id)
{
    destroyAllAgents(id);
    return Registry<Agent>::unregister(id);
}

std::list<const char*> CogServer::agentIds() const
{
    return Registry<Agent>::all();
}

Agent* CogServer::createAgent(const std::string& id, const bool start)
{
    Agent* a = Registry<Agent>::create(id);
    if (start) startAgent(a);
    return a;
}

void CogServer::startAgent(Agent* agent)
{
    agents.push_back(agent);
}

void CogServer::stopAgent(Agent* agent)
{
    agents.erase(std::find(agents.begin(), agents.end(), agent));
}

void CogServer::destroyAgent(Agent *agent)
{
    stopAgent(agent);
    delete agent;
}

void CogServer::destroyAllAgents(const std::string& id)
{
    // place agents with classinfo().id == id at the end of the container
    std::vector<Agent*>::iterator last =
        std::partition(agents.begin(), agents.end(),
                       std::tr1::bind(equal_to_id(), std::tr1::placeholders::_1, id));

    // save the agents that should be deleted on a temporary container
    std::vector<Agent*> to_delete(last, agents.end());

    // remove those agents from the main container
    agents.erase(last, agents.end());

    // delete the selected agents; NOTE: we must ensure that this is executed
    // after the 'agents.erase' call above, because the agent's destructor might
    // include a recursive call to destroyAllAgents
    std::for_each(to_delete.begin(), to_delete.end(), safe_deleter<Agent>());
}

bool CogServer::registerRequest(const std::string& name, AbstractFactory<Request> const* factory)
{
    return Registry<Request>::register_(name, factory);
}

bool CogServer::unregisterRequest(const std::string& name)
{
    return Registry<Request>::unregister(name);
}

Request* CogServer::createRequest(const std::string& name)
{
    return Registry<Request>::create(name);
}

const RequestClassInfo& CogServer::requestInfo(const std::string& name) const
{
    return static_cast<const RequestClassInfo&>(Registry<Request>::classinfo(name));
}

std::list<const char*> CogServer::requestIds() const
{
    return Registry<Request>::all();
}

long CogServer::getCycleCount()
{
    return cycleCount;
}

void CogServer::stop()
{
    running = false;
}

Request* CogServer::popRequest()
{

    Request* request;

    pthread_mutex_lock(&messageQueueLock);
    if (requestQueue.empty()) {
        request = NULL;
    } else {
        request = requestQueue.front();
        requestQueue.pop();
    }
    pthread_mutex_unlock(&messageQueueLock);

    return request;
}

void CogServer::pushRequest(Request* request)
{
    pthread_mutex_lock(&messageQueueLock);
    requestQueue.push(request);
    pthread_mutex_unlock(&messageQueueLock);
}

int CogServer::getRequestQueueSize()
{
    pthread_mutex_lock(&messageQueueLock);
    int size = requestQueue.size();
    pthread_mutex_unlock(&messageQueueLock);
    return size;
}

bool CogServer::loadModule(const std::string& filename)
{
    if (modules.find(filename) !=  modules.end()) {
        logger().info("Module \"%s\" is already loaded.", filename.c_str());
        return false;
    }

    // reset error
    dlerror();

    logger().info("Loading module \"%s\"", filename.c_str());
    void *dynLibrary = dlopen(filename.c_str(), RTLD_LAZY);
    const char* dlsymError = dlerror();
    if ((dynLibrary == NULL) || (dlsymError)) {
        logger().error("Unable to load module \"%s\": %s", filename.c_str(), dlsymError);
        return false;
    }

    // reset error
    dlerror();

    // search for id function
    Module::IdFunction* id_func = (Module::IdFunction*) dlsym(dynLibrary, Module::id_function_name());
    dlsymError = dlerror();
    if (dlsymError) {
        logger().error("Unable to find symbol \"opencog_module_id\": %s (module %s)", dlsymError, filename.c_str());
        return false;
    }

    // get and check module id
    const char *module_id = (*id_func)();
    if (module_id == NULL) {
        logger().error("Invalid module id (module \"%s\")", filename.c_str());
        return false;
    }

    // search for 'load' & 'unload' symbols
    Module::LoadFunction* load_func = (Module::LoadFunction*) dlsym(dynLibrary, Module::load_function_name());
    dlsymError = dlerror();
    if (dlsymError) {
        logger().error("Unable to find symbol \"opencog_module_load\": %s", dlsymError);
        return false;
    }

    Module::UnloadFunction* unload_func = (Module::UnloadFunction*) dlsym(dynLibrary, Module::unload_function_name());
    dlsymError = dlerror();
    if (dlsymError) {
        logger().error("Unable to find symbol \"opencog_module_unload\": %s", dlsymError);
        return false;
    }

    // load and init module
    Module* module = (Module*) (*load_func)();

    // store two entries in the module map:
    //    1: filename => <struct module data>
    //    2: moduleid => <struct module data>
    // we rely on the assumption that no module id will match the filename of
    // another module (and vice-versa). This is probably reasonable since most
    // module filenames should have a .dll or .so suffix, and module ids should
    // (by convention) be prefixed with its class namespace (i.e., "opencog::")
    std::string i = module_id;
    std::string f = filename;
    ModuleData mdata = {module, i, f, load_func, unload_func, dynLibrary};
    modules[i] = mdata;
    modules[f] = mdata;

    // after registration, call the module's init() method
    module->init();

    return true;
}

bool CogServer::unloadModule(const std::string& moduleId)
{
    logger().info("[CogServer] unloadModule(%s)", moduleId.c_str());
    ModuleMap::const_iterator it = modules.find(moduleId);
    if (it == modules.end()) {
        logger().info("[CogServer::unloadModule] module \"%s\" is not loaded.", moduleId.c_str());
        return false;
    }
    ModuleData mdata = it->second;

    // cache filename, id and handle
    std::string filename = mdata.filename;
    std::string id       = mdata.id;
    void*       handle   = mdata.handle;

    // invoke the module's unload function
    (*mdata.unloadFunction)(mdata.module);

    // erase the map entries (one with the filename as key, and one with the module
    // id as key
    modules.erase(filename);
    modules.erase(id);

    // unload dynamically loadable library
    logger().info("Unloading module \"%s\"", filename.c_str());

    dlerror(); // reset error
    if (dlclose(handle) != 0) {
        const char* dlsymError = dlerror();
        if (dlsymError) {
            logger().error("Unable to unload module \"%s\": %s", filename.c_str(), dlsymError);
            return false;
        }
    }

    return true;
}

CogServer::ModuleData CogServer::getModuleData(const std::string& moduleId)
{
    ModuleMap::const_iterator it = modules.find(moduleId);
    if (it == modules.end()) {
        logger().info("[CogServer::getModuleData] module \"%s\" was not found.", moduleId.c_str());
        ModuleData nulldata = {NULL, "", "", NULL, NULL, NULL};
        return nulldata;
    }
    return it->second;
}

Module* CogServer::getModule(const std::string& moduleId)
{
    return getModuleData(moduleId).module;
}

// Used for debug purposes on unit tests
void CogServer::unitTestServerLoop(int nCycles)
{
    for (int i = 0; (nCycles == 0) || (i < nCycles); ++i) {
        processRequests();
        processAgents();
        cycleCount++;
        if (cycleCount < 0) cycleCount = 0;
    }
}
<commit_msg>fix file path<commit_after>/*
 * opencog/server/CogServer.cc
 *
 * Copyright (C) 2002-2007 Novamente LLC
 * Copyright (C) 2008 by Singularity Institute for Artificial Intelligence
 * All Rights Reserved
 *
 * Written by Andre Senna <senna@vettalabs.com>
 *            Gustavo Gama <gama@vettalabs.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * 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 Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "CogServer.h"

#include <tr1/memory>
#include <tr1/functional>

#include <time.h>
#ifdef WIN32
#include <winsock2.h>
#else
#include <dlfcn.h>
#include <unistd.h>
#include <sys/time.h>
#endif

#include <opencog/atomspace/AtomSpace.h>
#include <opencog/server/Agent.h>
#include <opencog/server/ConsoleSocket.h>
#include <opencog/server/NetworkServer.h>
#include <opencog/server/Request.h>
#include <opencog/util/Config.h>
#include <opencog/util/Logger.h>
#include <opencog/util/exceptions.h>
#include <opencog/util/misc.h>
#include "load-file.h"

using namespace opencog;

namespace opencog {
struct equal_to_id : public std::binary_function<const Agent*, const std::string&, bool>
{
    bool operator()(const Agent* a, const std::string& cid) const {
        return (a->classinfo().id != cid);
    }
};
}

CogServer* CogServer::createInstance() {
    return new CogServer();
}

CogServer::~CogServer()
{
    disableNetworkServer();

    // unload all modules
    for (ModuleMap::iterator it = modules.begin(); it != modules.end(); ++it) {
        // retest the key because it might have been removed already
        if (modules.find(it->first) != modules.end()) {
            logger().debug("[CogServer] removing module %s\"", it->first.c_str());
            ModuleData mdata = it->second;

            // cache filename and id to erase the entries from the modules map
            std::string filename = mdata.filename;
            std::string id = mdata.id;

            // invoke the module's unload function
            (*mdata.unloadFunction)(mdata.module);

            // erase the map entries (one with the filename as key, and one with the module)
            // id as key
            modules.erase(filename);
            modules.erase(id);
        }
    }
}

CogServer::CogServer() : cycleCount(1)
{
    if (atomSpace != NULL) delete atomSpace;
    atomSpace = new AtomSpace();

    pthread_mutex_init(&messageQueueLock, NULL);
}

NetworkServer& CogServer::networkServer()
{
    return _networkServer;
}

void CogServer::enableNetworkServer()
{
    _networkServer.start();
    _networkServer.addListener<ConsoleSocket>(config().get_int("SERVER_PORT"));

}

void CogServer::disableNetworkServer()
{
    _networkServer.stop();
}

void CogServer::serverLoop()
{
    struct timeval timer_start, timer_end;
    time_t elapsed_time;
    time_t cycle_duration = config().get_int("SERVER_CYCLE_DURATION") * 1000;

    // XXX I've hard-coded a file path here, it assumes that the
    // cog server is running from the "bin" directory, (where "make"
    // was typed). Without a more general file-path mangling mechanism,
    // I am not sure what else to do. But this should be fixed XXX.
    load_scm_file("../opencog/scm/type_constructors.scm");

    logger().info("opencog server ready.");

    gettimeofday(&timer_start, NULL);
    for (running = true; running;) {

        if (getRequestQueueSize() != 0) {
            processRequests();
        }

        processAgents();

        cycleCount++;
        if (cycleCount < 0) cycleCount = 0;

        // sleep long enough so that the next cycle will only start
        // after config["SERVER_CYCLE_DURATION"] milliseconds
        gettimeofday(&timer_end, NULL);
        elapsed_time = ((timer_end.tv_sec - timer_start.tv_sec) * 1000000) +
                       (timer_end.tv_usec - timer_start.tv_usec);
        if ((cycle_duration - elapsed_time) > 0)
            usleep((unsigned int) (cycle_duration - elapsed_time));
        timer_start = timer_end;
    }
}

void CogServer::processRequests(void)
{
    Request* request;
    while ((request = popRequest()) != NULL) {
        request->execute();
        delete request;
    }
}

void CogServer::processAgents(void)
{
    std::vector<Agent*>::const_iterator it;
    for (it = agents.begin(); it != agents.end(); ++it) {
        Agent* agent = *it;
        if ((cycleCount % agent->frequency()) == 0)
            agent->run(this);
    }
}

bool CogServer::registerAgent(const std::string& id, AbstractFactory<Agent> const* factory)
{
    return Registry<Agent>::register_(id, factory);
}

bool CogServer::unregisterAgent(const std::string& id)
{
    destroyAllAgents(id);
    return Registry<Agent>::unregister(id);
}

std::list<const char*> CogServer::agentIds() const
{
    return Registry<Agent>::all();
}

Agent* CogServer::createAgent(const std::string& id, const bool start)
{
    Agent* a = Registry<Agent>::create(id);
    if (start) startAgent(a);
    return a;
}

void CogServer::startAgent(Agent* agent)
{
    agents.push_back(agent);
}

void CogServer::stopAgent(Agent* agent)
{
    agents.erase(std::find(agents.begin(), agents.end(), agent));
}

void CogServer::destroyAgent(Agent *agent)
{
    stopAgent(agent);
    delete agent;
}

void CogServer::destroyAllAgents(const std::string& id)
{
    // place agents with classinfo().id == id at the end of the container
    std::vector<Agent*>::iterator last =
        std::partition(agents.begin(), agents.end(),
                       std::tr1::bind(equal_to_id(), std::tr1::placeholders::_1, id));

    // save the agents that should be deleted on a temporary container
    std::vector<Agent*> to_delete(last, agents.end());

    // remove those agents from the main container
    agents.erase(last, agents.end());

    // delete the selected agents; NOTE: we must ensure that this is executed
    // after the 'agents.erase' call above, because the agent's destructor might
    // include a recursive call to destroyAllAgents
    std::for_each(to_delete.begin(), to_delete.end(), safe_deleter<Agent>());
}

bool CogServer::registerRequest(const std::string& name, AbstractFactory<Request> const* factory)
{
    return Registry<Request>::register_(name, factory);
}

bool CogServer::unregisterRequest(const std::string& name)
{
    return Registry<Request>::unregister(name);
}

Request* CogServer::createRequest(const std::string& name)
{
    return Registry<Request>::create(name);
}

const RequestClassInfo& CogServer::requestInfo(const std::string& name) const
{
    return static_cast<const RequestClassInfo&>(Registry<Request>::classinfo(name));
}

std::list<const char*> CogServer::requestIds() const
{
    return Registry<Request>::all();
}

long CogServer::getCycleCount()
{
    return cycleCount;
}

void CogServer::stop()
{
    running = false;
}

Request* CogServer::popRequest()
{

    Request* request;

    pthread_mutex_lock(&messageQueueLock);
    if (requestQueue.empty()) {
        request = NULL;
    } else {
        request = requestQueue.front();
        requestQueue.pop();
    }
    pthread_mutex_unlock(&messageQueueLock);

    return request;
}

void CogServer::pushRequest(Request* request)
{
    pthread_mutex_lock(&messageQueueLock);
    requestQueue.push(request);
    pthread_mutex_unlock(&messageQueueLock);
}

int CogServer::getRequestQueueSize()
{
    pthread_mutex_lock(&messageQueueLock);
    int size = requestQueue.size();
    pthread_mutex_unlock(&messageQueueLock);
    return size;
}

bool CogServer::loadModule(const std::string& filename)
{
    if (modules.find(filename) !=  modules.end()) {
        logger().info("Module \"%s\" is already loaded.", filename.c_str());
        return false;
    }

    // reset error
    dlerror();

    logger().info("Loading module \"%s\"", filename.c_str());
    void *dynLibrary = dlopen(filename.c_str(), RTLD_LAZY);
    const char* dlsymError = dlerror();
    if ((dynLibrary == NULL) || (dlsymError)) {
        logger().error("Unable to load module \"%s\": %s", filename.c_str(), dlsymError);
        return false;
    }

    // reset error
    dlerror();

    // search for id function
    Module::IdFunction* id_func = (Module::IdFunction*) dlsym(dynLibrary, Module::id_function_name());
    dlsymError = dlerror();
    if (dlsymError) {
        logger().error("Unable to find symbol \"opencog_module_id\": %s (module %s)", dlsymError, filename.c_str());
        return false;
    }

    // get and check module id
    const char *module_id = (*id_func)();
    if (module_id == NULL) {
        logger().error("Invalid module id (module \"%s\")", filename.c_str());
        return false;
    }

    // search for 'load' & 'unload' symbols
    Module::LoadFunction* load_func = (Module::LoadFunction*) dlsym(dynLibrary, Module::load_function_name());
    dlsymError = dlerror();
    if (dlsymError) {
        logger().error("Unable to find symbol \"opencog_module_load\": %s", dlsymError);
        return false;
    }

    Module::UnloadFunction* unload_func = (Module::UnloadFunction*) dlsym(dynLibrary, Module::unload_function_name());
    dlsymError = dlerror();
    if (dlsymError) {
        logger().error("Unable to find symbol \"opencog_module_unload\": %s", dlsymError);
        return false;
    }

    // load and init module
    Module* module = (Module*) (*load_func)();

    // store two entries in the module map:
    //    1: filename => <struct module data>
    //    2: moduleid => <struct module data>
    // we rely on the assumption that no module id will match the filename of
    // another module (and vice-versa). This is probably reasonable since most
    // module filenames should have a .dll or .so suffix, and module ids should
    // (by convention) be prefixed with its class namespace (i.e., "opencog::")
    std::string i = module_id;
    std::string f = filename;
    ModuleData mdata = {module, i, f, load_func, unload_func, dynLibrary};
    modules[i] = mdata;
    modules[f] = mdata;

    // after registration, call the module's init() method
    module->init();

    return true;
}

bool CogServer::unloadModule(const std::string& moduleId)
{
    logger().info("[CogServer] unloadModule(%s)", moduleId.c_str());
    ModuleMap::const_iterator it = modules.find(moduleId);
    if (it == modules.end()) {
        logger().info("[CogServer::unloadModule] module \"%s\" is not loaded.", moduleId.c_str());
        return false;
    }
    ModuleData mdata = it->second;

    // cache filename, id and handle
    std::string filename = mdata.filename;
    std::string id       = mdata.id;
    void*       handle   = mdata.handle;

    // invoke the module's unload function
    (*mdata.unloadFunction)(mdata.module);

    // erase the map entries (one with the filename as key, and one with the module
    // id as key
    modules.erase(filename);
    modules.erase(id);

    // unload dynamically loadable library
    logger().info("Unloading module \"%s\"", filename.c_str());

    dlerror(); // reset error
    if (dlclose(handle) != 0) {
        const char* dlsymError = dlerror();
        if (dlsymError) {
            logger().error("Unable to unload module \"%s\": %s", filename.c_str(), dlsymError);
            return false;
        }
    }

    return true;
}

CogServer::ModuleData CogServer::getModuleData(const std::string& moduleId)
{
    ModuleMap::const_iterator it = modules.find(moduleId);
    if (it == modules.end()) {
        logger().info("[CogServer::getModuleData] module \"%s\" was not found.", moduleId.c_str());
        ModuleData nulldata = {NULL, "", "", NULL, NULL, NULL};
        return nulldata;
    }
    return it->second;
}

Module* CogServer::getModule(const std::string& moduleId)
{
    return getModuleData(moduleId).module;
}

// Used for debug purposes on unit tests
void CogServer::unitTestServerLoop(int nCycles)
{
    for (int i = 0; (nCycles == 0) || (i < nCycles); ++i) {
        processRequests();
        processAgents();
        cycleCount++;
        if (cycleCount < 0) cycleCount = 0;
    }
}
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

/*
 * This file includes unit tests for the RTCPSender.
 */

#include <gtest/gtest.h>

#include "common_types.h"
#include "rtp_utility.h"
#include "rtcp_sender.h"
#include "rtcp_receiver.h"
#include "rtp_rtcp_impl.h"

namespace webrtc {

void CreateRtpPacket(const bool marker_bit, const WebRtc_UWord8 payload,
    const WebRtc_UWord16 seq_num, const WebRtc_UWord32 timestamp,
    const WebRtc_UWord32 ssrc, WebRtc_UWord8* array,
    WebRtc_UWord16* cur_pos) {
  ASSERT_TRUE(payload <= 127);
  array[(*cur_pos)++] = 0x80;
  array[(*cur_pos)++] = payload | (marker_bit ? 0x80 : 0);
  array[(*cur_pos)++] = seq_num >> 8;
  array[(*cur_pos)++] = seq_num;
  array[(*cur_pos)++] = timestamp >> 24;
  array[(*cur_pos)++] = timestamp >> 16;
  array[(*cur_pos)++] = timestamp >> 8;
  array[(*cur_pos)++] = timestamp;
  array[(*cur_pos)++] = ssrc >> 24;
  array[(*cur_pos)++] = ssrc >> 16;
  array[(*cur_pos)++] = ssrc >> 8;
  array[(*cur_pos)++] = ssrc;
  // VP8 payload header
  array[(*cur_pos)++] = 0x90;  // X bit = 1
  array[(*cur_pos)++] = 0x20;  // T bit = 1
  array[(*cur_pos)++] = 0x00;  // TID = 0
  array[(*cur_pos)++] = 0x00;  // Key frame
  array[(*cur_pos)++] = 0x00;
  array[(*cur_pos)++] = 0x00;
  array[(*cur_pos)++] = 0x9d;
  array[(*cur_pos)++] = 0x01;
  array[(*cur_pos)++] = 0x2a;
  array[(*cur_pos)++] = 128;
  array[(*cur_pos)++] = 0;
  array[(*cur_pos)++] = 96;
  array[(*cur_pos)++] = 0;
}

class TestTransport : public Transport,
                      public RtpData {
 public:
  TestTransport(RTCPReceiver* rtcp_receiver) :
    rtcp_receiver_(rtcp_receiver) {
  }

  virtual int SendPacket(int /*ch*/, const void* /*data*/, int /*len*/) {
    return -1;
  }

  virtual int SendRTCPPacket(int /*ch*/, const void *packet, int packet_len) {
    RTCPUtility::RTCPParserV2 rtcpParser((WebRtc_UWord8*)packet,
                                         (WebRtc_Word32)packet_len,
                                         true); // Allow non-compound RTCP

    EXPECT_TRUE(rtcpParser.IsValid());
    RTCPHelp::RTCPPacketInformation rtcpPacketInformation;
    EXPECT_EQ(0, rtcp_receiver_->IncomingRTCPPacket(rtcpPacketInformation,
                                                   &rtcpParser));
    rtcp_packet_info_ = rtcpPacketInformation;

    return packet_len;
  }

  virtual int OnReceivedPayloadData(const WebRtc_UWord8* payloadData,
                                    const WebRtc_UWord16 payloadSize,
                                    const WebRtcRTPHeader* rtpHeader)
                                    {return 0;}
  RTCPReceiver* rtcp_receiver_;
  RTCPHelp::RTCPPacketInformation rtcp_packet_info_;
};

class RtcpSenderTest : public ::testing::Test {
 protected:
  RtcpSenderTest() {
    system_clock_ = ModuleRTPUtility::GetSystemClock();
    rtp_rtcp_impl_ = new ModuleRtpRtcpImpl(0, false, system_clock_);
    rtcp_sender_ = new RTCPSender(0, false, system_clock_, rtp_rtcp_impl_);
    rtcp_receiver_ = new RTCPReceiver(0, system_clock_, rtp_rtcp_impl_);
    test_transport_ = new TestTransport(rtcp_receiver_);
    // Initialize
    EXPECT_EQ(0, rtcp_sender_->Init());
    EXPECT_EQ(0, rtcp_sender_->RegisterSendTransport(test_transport_));
    EXPECT_EQ(0, rtp_rtcp_impl_->RegisterIncomingDataCallback(test_transport_));
  }
  ~RtcpSenderTest() {
    delete rtcp_sender_;
    delete rtcp_receiver_;
    delete rtp_rtcp_impl_;
    delete test_transport_;
    delete system_clock_;
  }

  // Helper function: Incoming RTCP has a specific packet type.
  bool gotPacketType(RTCPPacketType packet_type) {
    return ((test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags) &
	    packet_type) != 0U;
  }

  RtpRtcpClock* system_clock_;
  ModuleRtpRtcpImpl* rtp_rtcp_impl_;
  RTCPSender* rtcp_sender_;
  RTCPReceiver* rtcp_receiver_;
  TestTransport* test_transport_;

  enum {kMaxPacketLength = 1500};
  uint8_t packet_[kMaxPacketLength];
};

TEST_F(RtcpSenderTest, RtcpOff) {
  EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpOff));
  EXPECT_EQ(-1, rtcp_sender_->SendRTCP(kRtcpSr));
}

TEST_F(RtcpSenderTest, IJStatus) {
  ASSERT_FALSE(rtcp_sender_->IJ());
  EXPECT_EQ(0, rtcp_sender_->SetIJStatus(true));
  ASSERT_TRUE(rtcp_sender_->IJ());
}

TEST_F(RtcpSenderTest, TestCompound) {
  const bool marker_bit = false;
  const WebRtc_UWord8 payload = 100;
  const WebRtc_UWord16 seq_num = 11111;
  const WebRtc_UWord32 timestamp = 1234567;
  const WebRtc_UWord32 ssrc = 0x11111111;
  WebRtc_UWord16 packet_length = 0;
  CreateRtpPacket(marker_bit, payload, seq_num, timestamp, ssrc, packet_,
      &packet_length);
  EXPECT_EQ(25, packet_length);

  VideoCodec codec_inst;
  strncpy(codec_inst.plName, "VP8", webrtc::kPayloadNameSize - 1);
  codec_inst.codecType = webrtc::kVideoCodecVP8;
  codec_inst.plType = payload;
  EXPECT_EQ(0, rtp_rtcp_impl_->RegisterReceivePayload(codec_inst));

  // Make sure RTP packet has been received.
  EXPECT_EQ(0, rtp_rtcp_impl_->IncomingPacket(packet_, packet_length));

  EXPECT_EQ(0, rtcp_sender_->SetIJStatus(true));
  EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound));
  EXPECT_EQ(0, rtcp_sender_->SendRTCP(kRtcpRr));

  // Transmission time offset packet should be received.
  ASSERT_TRUE(test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags &
      kRtcpTransmissionTimeOffset);
}

TEST_F(RtcpSenderTest, TestCompound_NoRtpReceived) {
  EXPECT_EQ(0, rtcp_sender_->SetIJStatus(true));
  EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound));
  EXPECT_EQ(0, rtcp_sender_->SendRTCP(kRtcpRr));

  // Transmission time offset packet should not be received.
  ASSERT_FALSE(test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags &
      kRtcpTransmissionTimeOffset);
}

// This test is written to verify actual behaviour. It does not seem
// to make much sense to send an empty TMMBN, since there is no place
// to put an actual limit here. It's just information that no limit
// is set, which is kind of the starting assumption.
// See http://code.google.com/p/webrtc/issues/detail?id=468 for one
// situation where this caused confusion.
TEST_F(RtcpSenderTest, SendsTmmbnIfSetAndEmpty) {
  EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound));
  TMMBRSet bounding_set;
  EXPECT_EQ(0, rtcp_sender_->SetTMMBN(&bounding_set, 3));
  ASSERT_EQ(0U, test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags);
  EXPECT_EQ(0, rtcp_sender_->SendRTCP(kRtcpSr));
  // We now expect the packet to show up in the rtcp_packet_info_ of
  // test_transport_.
  ASSERT_NE(0U, test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags);
  EXPECT_TRUE(gotPacketType(kRtcpTmmbn));
  TMMBRSet* incoming_set = NULL;
  bool owner = false;
  // The BoundingSet function returns the number of members of the
  // bounding set, and touches the incoming set only if there's > 1.
  // TODO(hta): Change the type of the BoundingSet second argument from
  // TMMBRSet*& to TMMBRSet* - it never writes to the pointer.
  EXPECT_EQ(0, test_transport_->rtcp_receiver_->BoundingSet(owner,
      incoming_set));
  EXPECT_TRUE(incoming_set == NULL);
}
}  // namespace webrtc
<commit_msg>untabify<commit_after>/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

/*
 * This file includes unit tests for the RTCPSender.
 */

#include <gtest/gtest.h>

#include "common_types.h"
#include "rtp_utility.h"
#include "rtcp_sender.h"
#include "rtcp_receiver.h"
#include "rtp_rtcp_impl.h"

namespace webrtc {

void CreateRtpPacket(const bool marker_bit, const WebRtc_UWord8 payload,
    const WebRtc_UWord16 seq_num, const WebRtc_UWord32 timestamp,
    const WebRtc_UWord32 ssrc, WebRtc_UWord8* array,
    WebRtc_UWord16* cur_pos) {
  ASSERT_TRUE(payload <= 127);
  array[(*cur_pos)++] = 0x80;
  array[(*cur_pos)++] = payload | (marker_bit ? 0x80 : 0);
  array[(*cur_pos)++] = seq_num >> 8;
  array[(*cur_pos)++] = seq_num;
  array[(*cur_pos)++] = timestamp >> 24;
  array[(*cur_pos)++] = timestamp >> 16;
  array[(*cur_pos)++] = timestamp >> 8;
  array[(*cur_pos)++] = timestamp;
  array[(*cur_pos)++] = ssrc >> 24;
  array[(*cur_pos)++] = ssrc >> 16;
  array[(*cur_pos)++] = ssrc >> 8;
  array[(*cur_pos)++] = ssrc;
  // VP8 payload header
  array[(*cur_pos)++] = 0x90;  // X bit = 1
  array[(*cur_pos)++] = 0x20;  // T bit = 1
  array[(*cur_pos)++] = 0x00;  // TID = 0
  array[(*cur_pos)++] = 0x00;  // Key frame
  array[(*cur_pos)++] = 0x00;
  array[(*cur_pos)++] = 0x00;
  array[(*cur_pos)++] = 0x9d;
  array[(*cur_pos)++] = 0x01;
  array[(*cur_pos)++] = 0x2a;
  array[(*cur_pos)++] = 128;
  array[(*cur_pos)++] = 0;
  array[(*cur_pos)++] = 96;
  array[(*cur_pos)++] = 0;
}

class TestTransport : public Transport,
                      public RtpData {
 public:
  TestTransport(RTCPReceiver* rtcp_receiver) :
    rtcp_receiver_(rtcp_receiver) {
  }

  virtual int SendPacket(int /*ch*/, const void* /*data*/, int /*len*/) {
    return -1;
  }

  virtual int SendRTCPPacket(int /*ch*/, const void *packet, int packet_len) {
    RTCPUtility::RTCPParserV2 rtcpParser((WebRtc_UWord8*)packet,
                                         (WebRtc_Word32)packet_len,
                                         true); // Allow non-compound RTCP

    EXPECT_TRUE(rtcpParser.IsValid());
    RTCPHelp::RTCPPacketInformation rtcpPacketInformation;
    EXPECT_EQ(0, rtcp_receiver_->IncomingRTCPPacket(rtcpPacketInformation,
                                                   &rtcpParser));
    rtcp_packet_info_ = rtcpPacketInformation;

    return packet_len;
  }

  virtual int OnReceivedPayloadData(const WebRtc_UWord8* payloadData,
                                    const WebRtc_UWord16 payloadSize,
                                    const WebRtcRTPHeader* rtpHeader)
                                    {return 0;}
  RTCPReceiver* rtcp_receiver_;
  RTCPHelp::RTCPPacketInformation rtcp_packet_info_;
};

class RtcpSenderTest : public ::testing::Test {
 protected:
  RtcpSenderTest() {
    system_clock_ = ModuleRTPUtility::GetSystemClock();
    rtp_rtcp_impl_ = new ModuleRtpRtcpImpl(0, false, system_clock_);
    rtcp_sender_ = new RTCPSender(0, false, system_clock_, rtp_rtcp_impl_);
    rtcp_receiver_ = new RTCPReceiver(0, system_clock_, rtp_rtcp_impl_);
    test_transport_ = new TestTransport(rtcp_receiver_);
    // Initialize
    EXPECT_EQ(0, rtcp_sender_->Init());
    EXPECT_EQ(0, rtcp_sender_->RegisterSendTransport(test_transport_));
    EXPECT_EQ(0, rtp_rtcp_impl_->RegisterIncomingDataCallback(test_transport_));
  }
  ~RtcpSenderTest() {
    delete rtcp_sender_;
    delete rtcp_receiver_;
    delete rtp_rtcp_impl_;
    delete test_transport_;
    delete system_clock_;
  }

  // Helper function: Incoming RTCP has a specific packet type.
  bool gotPacketType(RTCPPacketType packet_type) {
    return ((test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags) &
            packet_type) != 0U;
  }

  RtpRtcpClock* system_clock_;
  ModuleRtpRtcpImpl* rtp_rtcp_impl_;
  RTCPSender* rtcp_sender_;
  RTCPReceiver* rtcp_receiver_;
  TestTransport* test_transport_;

  enum {kMaxPacketLength = 1500};
  uint8_t packet_[kMaxPacketLength];
};

TEST_F(RtcpSenderTest, RtcpOff) {
  EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpOff));
  EXPECT_EQ(-1, rtcp_sender_->SendRTCP(kRtcpSr));
}

TEST_F(RtcpSenderTest, IJStatus) {
  ASSERT_FALSE(rtcp_sender_->IJ());
  EXPECT_EQ(0, rtcp_sender_->SetIJStatus(true));
  ASSERT_TRUE(rtcp_sender_->IJ());
}

TEST_F(RtcpSenderTest, TestCompound) {
  const bool marker_bit = false;
  const WebRtc_UWord8 payload = 100;
  const WebRtc_UWord16 seq_num = 11111;
  const WebRtc_UWord32 timestamp = 1234567;
  const WebRtc_UWord32 ssrc = 0x11111111;
  WebRtc_UWord16 packet_length = 0;
  CreateRtpPacket(marker_bit, payload, seq_num, timestamp, ssrc, packet_,
      &packet_length);
  EXPECT_EQ(25, packet_length);

  VideoCodec codec_inst;
  strncpy(codec_inst.plName, "VP8", webrtc::kPayloadNameSize - 1);
  codec_inst.codecType = webrtc::kVideoCodecVP8;
  codec_inst.plType = payload;
  EXPECT_EQ(0, rtp_rtcp_impl_->RegisterReceivePayload(codec_inst));

  // Make sure RTP packet has been received.
  EXPECT_EQ(0, rtp_rtcp_impl_->IncomingPacket(packet_, packet_length));

  EXPECT_EQ(0, rtcp_sender_->SetIJStatus(true));
  EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound));
  EXPECT_EQ(0, rtcp_sender_->SendRTCP(kRtcpRr));

  // Transmission time offset packet should be received.
  ASSERT_TRUE(test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags &
      kRtcpTransmissionTimeOffset);
}

TEST_F(RtcpSenderTest, TestCompound_NoRtpReceived) {
  EXPECT_EQ(0, rtcp_sender_->SetIJStatus(true));
  EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound));
  EXPECT_EQ(0, rtcp_sender_->SendRTCP(kRtcpRr));

  // Transmission time offset packet should not be received.
  ASSERT_FALSE(test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags &
      kRtcpTransmissionTimeOffset);
}

// This test is written to verify actual behaviour. It does not seem
// to make much sense to send an empty TMMBN, since there is no place
// to put an actual limit here. It's just information that no limit
// is set, which is kind of the starting assumption.
// See http://code.google.com/p/webrtc/issues/detail?id=468 for one
// situation where this caused confusion.
TEST_F(RtcpSenderTest, SendsTmmbnIfSetAndEmpty) {
  EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound));
  TMMBRSet bounding_set;
  EXPECT_EQ(0, rtcp_sender_->SetTMMBN(&bounding_set, 3));
  ASSERT_EQ(0U, test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags);
  EXPECT_EQ(0, rtcp_sender_->SendRTCP(kRtcpSr));
  // We now expect the packet to show up in the rtcp_packet_info_ of
  // test_transport_.
  ASSERT_NE(0U, test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags);
  EXPECT_TRUE(gotPacketType(kRtcpTmmbn));
  TMMBRSet* incoming_set = NULL;
  bool owner = false;
  // The BoundingSet function returns the number of members of the
  // bounding set, and touches the incoming set only if there's > 1.
  // TODO(hta): Change the type of the BoundingSet second argument from
  // TMMBRSet*& to TMMBRSet* - it never writes to the pointer.
  EXPECT_EQ(0, test_transport_->rtcp_receiver_->BoundingSet(owner,
      incoming_set));
  EXPECT_TRUE(incoming_set == NULL);
}
}  // namespace webrtc
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkPointSet.cc
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

Copyright (c) 1993-1995 Ken Martin, Will Schroeder, Bill Lorensen.

This software is copyrighted by Ken Martin, Will Schroeder and Bill Lorensen.
The following terms apply to all files associated with the software unless
explicitly disclaimed in individual files. This copyright specifically does
not apply to the related textbook "The Visualization Toolkit" ISBN
013199837-4 published by Prentice Hall which is covered by its own copyright.

The authors hereby grant permission to use, copy, and distribute this
software and its documentation for any purpose, provided that existing
copyright notices are retained in all copies and that this notice is included
verbatim in any distributions. Additionally, the authors grant permission to
modify this software and its documentation for any purpose, provided that
such modifications are not distributed without the explicit consent of the
authors and that existing copyright notices are retained in all copies. Some
of the algorithms implemented by this software are patented, observe all
applicable patent law.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

=========================================================================*/
#include "vtkPointSet.hh"

vtkPointSet::vtkPointSet ()
{
  this->Points = NULL;
  this->Locator = NULL;
}

vtkPointSet::vtkPointSet(const vtkPointSet& ps) :
vtkDataSet(ps)
{
  this->Points = ps.Points;
  if (this->Points) this->Points->Register(this);

  this->Locator = ps.Locator;
}

vtkPointSet::~vtkPointSet ()
{
  this->Initialize();
  if ( this->Locator ) this->Locator->Delete();
}

// Description:
// Copy the geometric structure of an input point set object.
void vtkPolyData::CopyStructure(vtkDataSet *ds)
{
  vtkPointSet *ps=(vtkPointSet *)ds;
  this->Initialize();

  this->Points = ps->Points;
  if (this->Points) this->Points->Register(this);
}

void vtkPointSet::Initialize()
{
  vtkDataSet::Initialize();

  if ( this->Points )
  {
    this->Points->UnRegister(this);
    this->Points = NULL;
  }

  if ( this->Locator )
  {
    this->Locator->Initialize();
  }
}
void vtkPointSet::ComputeBounds()
{
  float *bounds;

  if ( this->Points )
    {
    bounds = this->Points->GetBounds();
    for (int i=0; i<6; i++) this->Bounds[i] = bounds[i];
    this->ComputeTime.Modified();
    }
}

unsigned long int vtkPointSet::GetMTime()
{
  unsigned long int dsTime = vtkDataSet::GetMTime();

  if ( this->Points )
    {
    if ( this->Points->GetMTime() > dsTime ) dsTime = this->Points->GetMTime();
    }

  // don't get locator's mtime because its an internal object that cannot be
  // modified directly from outside. Causes problems due to FindCell()
  // SetPoints() method.

  return dsTime;
}

int vtkPointSet::FindCell(float x[3], vtkCell *cell, float tol2, int& subId,
                         float pcoords[3], float weights[MAX_CELL_SIZE])
{
  int i, j;
  int closestCell = -1;
  int ptId, cellId;
  float dist2, minDist2=LARGE_FLOAT;
  static vtkIdList cellIds(MAX_CELL_SIZE);
  int sId;
  float pc[3], w[MAX_CELL_SIZE];
  float closestPoint[3];

  if ( !this->Points ) return -1;

  if ( !this->Locator )
    {
    this->Locator = new vtkLocator;
    this->Locator->SetPoints(this->Points);
    }

  if ( this->Points->GetMTime() > this->Locator->GetMTime() )
    {
    this->Locator->SetPoints(this->Points);
    }

// Find the closest point to the input position.  Then get the cells that
// use the point.  Then determine if point is in any of the cells.

  if ( (ptId = this->Locator->FindClosestPoint(x)) >= 0 )
    {
    this->GetPointCells(ptId, cellIds);
    for (i=0; i<cellIds.GetNumberOfIds(); i++)
      {
      cellId = cellIds.GetId(i);
      cell = this->GetCell(cellId);
      if ( cell->EvaluatePosition(x,closestPoint,sId,pc,dist2,w) != -1 &&
      dist2 <= tol2 && dist2 < minDist2 )
        {
        minDist2 = dist2;
        closestCell = cellId;
        subId = sId;
        pcoords[0] = pc[0]; pcoords[1] = pc[1]; pcoords[2] = pc[2];
        for (j=0; j < cell->GetNumberOfPoints(); j++) weights[j] = w[j];
        }
      }
    }
  return closestCell;
}

void vtkPointSet::Squeeze()
{
  if ( this->Points ) this->Points->Squeeze();
  vtkDataSet::Squeeze();
}

void vtkPointSet::PrintSelf(ostream& os, vtkIndent indent)
{
  vtkDataSet::PrintSelf(os,indent);

  os << indent << "Number Of Points: " << this->GetNumberOfPoints() << "\n";
  os << indent << "Point Data: " << this->Points << "\n";
  os << indent << "Locator: " << this->Locator << "\n";
}

<commit_msg>*** empty log message ***<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkPointSet.cc
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

Copyright (c) 1993-1995 Ken Martin, Will Schroeder, Bill Lorensen.

This software is copyrighted by Ken Martin, Will Schroeder and Bill Lorensen.
The following terms apply to all files associated with the software unless
explicitly disclaimed in individual files. This copyright specifically does
not apply to the related textbook "The Visualization Toolkit" ISBN
013199837-4 published by Prentice Hall which is covered by its own copyright.

The authors hereby grant permission to use, copy, and distribute this
software and its documentation for any purpose, provided that existing
copyright notices are retained in all copies and that this notice is included
verbatim in any distributions. Additionally, the authors grant permission to
modify this software and its documentation for any purpose, provided that
such modifications are not distributed without the explicit consent of the
authors and that existing copyright notices are retained in all copies. Some
of the algorithms implemented by this software are patented, observe all
applicable patent law.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

=========================================================================*/
#include "vtkPointSet.hh"

vtkPointSet::vtkPointSet ()
{
  this->Points = NULL;
  this->Locator = NULL;
}

vtkPointSet::vtkPointSet(const vtkPointSet& ps) :
vtkDataSet(ps)
{
  this->Points = ps.Points;
  if (this->Points) this->Points->Register(this);

  this->Locator = ps.Locator;
}

vtkPointSet::~vtkPointSet ()
{
  this->Initialize();
  if ( this->Locator ) this->Locator->Delete();
}

// Description:
// Copy the geometric structure of an input point set object.
void vtkPointSet::CopyStructure(vtkDataSet *ds)
{
  vtkPointSet *ps=(vtkPointSet *)ds;
  this->Initialize();

  this->Points = ps->Points;
  if (this->Points) this->Points->Register(this);
}

void vtkPointSet::Initialize()
{
  vtkDataSet::Initialize();

  if ( this->Points )
  {
    this->Points->UnRegister(this);
    this->Points = NULL;
  }

  if ( this->Locator )
  {
    this->Locator->Initialize();
  }
}
void vtkPointSet::ComputeBounds()
{
  float *bounds;

  if ( this->Points )
    {
    bounds = this->Points->GetBounds();
    for (int i=0; i<6; i++) this->Bounds[i] = bounds[i];
    this->ComputeTime.Modified();
    }
}

unsigned long int vtkPointSet::GetMTime()
{
  unsigned long int dsTime = vtkDataSet::GetMTime();

  if ( this->Points )
    {
    if ( this->Points->GetMTime() > dsTime ) dsTime = this->Points->GetMTime();
    }

  // don't get locator's mtime because its an internal object that cannot be
  // modified directly from outside. Causes problems due to FindCell()
  // SetPoints() method.

  return dsTime;
}

int vtkPointSet::FindCell(float x[3], vtkCell *cell, float tol2, int& subId,
                         float pcoords[3], float weights[MAX_CELL_SIZE])
{
  int i, j;
  int closestCell = -1;
  int ptId, cellId;
  float dist2, minDist2=LARGE_FLOAT;
  static vtkIdList cellIds(MAX_CELL_SIZE);
  int sId;
  float pc[3], w[MAX_CELL_SIZE];
  float closestPoint[3];

  if ( !this->Points ) return -1;

  if ( !this->Locator )
    {
    this->Locator = new vtkLocator;
    this->Locator->SetPoints(this->Points);
    }

  if ( this->Points->GetMTime() > this->Locator->GetMTime() )
    {
    this->Locator->SetPoints(this->Points);
    }

// Find the closest point to the input position.  Then get the cells that
// use the point.  Then determine if point is in any of the cells.

  if ( (ptId = this->Locator->FindClosestPoint(x)) >= 0 )
    {
    this->GetPointCells(ptId, cellIds);
    for (i=0; i<cellIds.GetNumberOfIds(); i++)
      {
      cellId = cellIds.GetId(i);
      cell = this->GetCell(cellId);
      if ( cell->EvaluatePosition(x,closestPoint,sId,pc,dist2,w) != -1 &&
      dist2 <= tol2 && dist2 < minDist2 )
        {
        minDist2 = dist2;
        closestCell = cellId;
        subId = sId;
        pcoords[0] = pc[0]; pcoords[1] = pc[1]; pcoords[2] = pc[2];
        for (j=0; j < cell->GetNumberOfPoints(); j++) weights[j] = w[j];
        }
      }
    }
  return closestCell;
}

void vtkPointSet::Squeeze()
{
  if ( this->Points ) this->Points->Squeeze();
  vtkDataSet::Squeeze();
}

void vtkPointSet::PrintSelf(ostream& os, vtkIndent indent)
{
  vtkDataSet::PrintSelf(os,indent);

  os << indent << "Number Of Points: " << this->GetNumberOfPoints() << "\n";
  os << indent << "Point Data: " << this->Points << "\n";
  os << indent << "Locator: " << this->Locator << "\n";
}

<|endoftext|>


<commit_before>/*

This file is part of VROOM.

Copyright (c) 2015-2020, Julien Coupey.
All rights reserved (see LICENSE).

*/

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>

#include "routing/http_wrapper.h"

using boost::asio::ip::tcp;

namespace vroom {
namespace routing {

const std::string HttpWrapper::HTTPS_PORT = "443";

HttpWrapper::HttpWrapper(const std::string& profile,
                         const Server& server,
                         const std::string& matrix_service,
                         const std::string& route_service,
                         const std::string& extra_args)
  : Wrapper(profile),
    _server(server),
    _matrix_service(matrix_service),
    _route_service(route_service),
    _extra_args(extra_args) {
}

std::string HttpWrapper::send_then_receive(const std::string& query) const {
  std::string response;

  try {
    boost::asio::io_service io_service;

    tcp::resolver r(io_service);

    tcp::resolver::query q(_server.host, _server.port);

    tcp::socket s(io_service);
    boost::asio::connect(s, r.resolve(q));

    boost::asio::write(s, boost::asio::buffer(query));

    char buf[512];
    boost::system::error_code error;
    for (;;) {
      std::size_t len = s.read_some(boost::asio::buffer(buf), error);
      response.append(buf, len);
      if (error == boost::asio::error::eof) {
        // Connection closed cleanly.
        break;
      } else {
        if (error) {
          throw boost::system::system_error(error);
        }
      }
    }
  } catch (boost::system::system_error& e) {
    throw Exception(ERROR::ROUTING,
                    "Failed to connect to " + _server.host + ":" +
                      _server.port);
  }

  // Removing headers.
  auto start = response.find("{");
  assert(start != std::string::npos);
  auto end = response.rfind("}");
  assert(end != std::string::npos);

  std::string json_content = response.substr(start, end - start + 1);

  return json_content;
}

std::string HttpWrapper::ssl_send_then_receive(const std::string& query) const {
  std::string response;

  try {
    boost::asio::io_service io_service;

    boost::asio::ssl::context ctx(
      boost::asio::ssl::context::method::sslv23_client);
    boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssock(io_service,
                                                                 ctx);

    tcp::resolver r(io_service);

    tcp::resolver::query q(_server.host, _server.port);

    boost::asio::connect(ssock.lowest_layer(), r.resolve(q));
    ssock.handshake(boost::asio::ssl::stream_base::handshake_type::client);

    boost::asio::write(ssock, boost::asio::buffer(query));

    char buf[512];
    boost::system::error_code error;
    for (;;) {
      std::size_t len = ssock.read_some(boost::asio::buffer(buf), error);
      response.append(buf, len);
      if (error == boost::asio::error::eof) {
        // Connection closed cleanly.
        break;
      } else {
        if (error) {
          throw boost::system::system_error(error);
        }
      }
    }
  } catch (boost::system::system_error& e) {
    throw Exception(ERROR::ROUTING,
                    "Failed to connect to " + _server.host + ":" +
                      _server.port);
  }

  // Removing headers.
  auto start = response.find("{");
  assert(start != std::string::npos);
  auto end = response.rfind("}");
  assert(end != std::string::npos);

  std::string json_content = response.substr(start, end - start + 1);

  return json_content;
}

std::string HttpWrapper::run_query(const std::string& query) const {
  return (_server.port == HTTPS_PORT) ? ssl_send_then_receive(query)
                                      : send_then_receive(query);
}

Matrix<Cost> HttpWrapper::get_matrix(const std::vector<Location>& locs) const {
  std::string query = this->build_query(locs, _matrix_service);
  std::string json_content = this->run_query(query);

  // Expected matrix size.
  std::size_t m_size = locs.size();

  rapidjson::Document infos;
  this->parse_response(infos, json_content);

  assert(infos["durations"].Size() == m_size);

  // Build matrix while checking for unfound routes ('null' values) to
  // avoid unexpected behavior.
  Matrix<Cost> m(m_size);

  std::vector<unsigned> nb_unfound_from_loc(m_size, 0);
  std::vector<unsigned> nb_unfound_to_loc(m_size, 0);

  for (rapidjson::SizeType i = 0; i < infos["durations"].Size(); ++i) {
    const auto& line = infos["durations"][i];
    assert(line.Size() == m_size);
    for (rapidjson::SizeType j = 0; j < line.Size(); ++j) {
      if (line[j].IsNull()) {
        // No route found between i and j. Just storing info as we
        // don't know yet which location is responsible between i
        // and j.
        ++nb_unfound_from_loc[i];
        ++nb_unfound_to_loc[j];
      } else {
        auto cost = round_cost(line[j].GetDouble());
        m[i][j] = cost;
      }
    }
  }

  check_unfound(locs, nb_unfound_from_loc, nb_unfound_to_loc);

  return m;
}

void HttpWrapper::add_route_info(Route& route) const {
  // Ordering locations for the given steps.
  std::vector<Location> ordered_locations;
  for (const auto& step : route.steps) {
    ordered_locations.push_back(step.location);
  }

  std::string query =
    build_query(ordered_locations, _route_service, _extra_args);
  std::string json_content = this->run_query(query);

  rapidjson::Document infos;
  parse_response(infos, json_content);

  // Total distance and route geometry.
  route.distance = round_cost(get_total_distance(infos["routes"][0]));
  route.geometry = std::move(infos["routes"][0]["geometry"].GetString());

  auto nb_legs = get_legs_number(infos["routes"][0]);
  assert(nb_legs == route.steps.size() - 1);

  // Accumulated travel distance stored for each step.
  double current_distance = 0;

  route.steps[0].distance = 0;

  for (rapidjson::SizeType i = 0; i < nb_legs; ++i) {
    // Update distance for step after current route leg.
    current_distance += get_distance_for_leg(infos["routes"][0], i);
    route.steps[i + 1].distance = round_cost(current_distance);
  }
}

} // namespace routing
} // namespace vroom
<commit_msg>Adjust HttpWrapper::add_route_info with breaks.<commit_after>/*

This file is part of VROOM.

Copyright (c) 2015-2020, Julien Coupey.
All rights reserved (see LICENSE).

*/

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>

#include "routing/http_wrapper.h"

using boost::asio::ip::tcp;

namespace vroom {
namespace routing {

const std::string HttpWrapper::HTTPS_PORT = "443";

HttpWrapper::HttpWrapper(const std::string& profile,
                         const Server& server,
                         const std::string& matrix_service,
                         const std::string& route_service,
                         const std::string& extra_args)
  : Wrapper(profile),
    _server(server),
    _matrix_service(matrix_service),
    _route_service(route_service),
    _extra_args(extra_args) {
}

std::string HttpWrapper::send_then_receive(const std::string& query) const {
  std::string response;

  try {
    boost::asio::io_service io_service;

    tcp::resolver r(io_service);

    tcp::resolver::query q(_server.host, _server.port);

    tcp::socket s(io_service);
    boost::asio::connect(s, r.resolve(q));

    boost::asio::write(s, boost::asio::buffer(query));

    char buf[512];
    boost::system::error_code error;
    for (;;) {
      std::size_t len = s.read_some(boost::asio::buffer(buf), error);
      response.append(buf, len);
      if (error == boost::asio::error::eof) {
        // Connection closed cleanly.
        break;
      } else {
        if (error) {
          throw boost::system::system_error(error);
        }
      }
    }
  } catch (boost::system::system_error& e) {
    throw Exception(ERROR::ROUTING,
                    "Failed to connect to " + _server.host + ":" +
                      _server.port);
  }

  // Removing headers.
  auto start = response.find("{");
  assert(start != std::string::npos);
  auto end = response.rfind("}");
  assert(end != std::string::npos);

  std::string json_content = response.substr(start, end - start + 1);

  return json_content;
}

std::string HttpWrapper::ssl_send_then_receive(const std::string& query) const {
  std::string response;

  try {
    boost::asio::io_service io_service;

    boost::asio::ssl::context ctx(
      boost::asio::ssl::context::method::sslv23_client);
    boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssock(io_service,
                                                                 ctx);

    tcp::resolver r(io_service);

    tcp::resolver::query q(_server.host, _server.port);

    boost::asio::connect(ssock.lowest_layer(), r.resolve(q));
    ssock.handshake(boost::asio::ssl::stream_base::handshake_type::client);

    boost::asio::write(ssock, boost::asio::buffer(query));

    char buf[512];
    boost::system::error_code error;
    for (;;) {
      std::size_t len = ssock.read_some(boost::asio::buffer(buf), error);
      response.append(buf, len);
      if (error == boost::asio::error::eof) {
        // Connection closed cleanly.
        break;
      } else {
        if (error) {
          throw boost::system::system_error(error);
        }
      }
    }
  } catch (boost::system::system_error& e) {
    throw Exception(ERROR::ROUTING,
                    "Failed to connect to " + _server.host + ":" +
                      _server.port);
  }

  // Removing headers.
  auto start = response.find("{");
  assert(start != std::string::npos);
  auto end = response.rfind("}");
  assert(end != std::string::npos);

  std::string json_content = response.substr(start, end - start + 1);

  return json_content;
}

std::string HttpWrapper::run_query(const std::string& query) const {
  return (_server.port == HTTPS_PORT) ? ssl_send_then_receive(query)
                                      : send_then_receive(query);
}

Matrix<Cost> HttpWrapper::get_matrix(const std::vector<Location>& locs) const {
  std::string query = this->build_query(locs, _matrix_service);
  std::string json_content = this->run_query(query);

  // Expected matrix size.
  std::size_t m_size = locs.size();

  rapidjson::Document infos;
  this->parse_response(infos, json_content);

  assert(infos["durations"].Size() == m_size);

  // Build matrix while checking for unfound routes ('null' values) to
  // avoid unexpected behavior.
  Matrix<Cost> m(m_size);

  std::vector<unsigned> nb_unfound_from_loc(m_size, 0);
  std::vector<unsigned> nb_unfound_to_loc(m_size, 0);

  for (rapidjson::SizeType i = 0; i < infos["durations"].Size(); ++i) {
    const auto& line = infos["durations"][i];
    assert(line.Size() == m_size);
    for (rapidjson::SizeType j = 0; j < line.Size(); ++j) {
      if (line[j].IsNull()) {
        // No route found between i and j. Just storing info as we
        // don't know yet which location is responsible between i
        // and j.
        ++nb_unfound_from_loc[i];
        ++nb_unfound_to_loc[j];
      } else {
        auto cost = round_cost(line[j].GetDouble());
        m[i][j] = cost;
      }
    }
  }

  check_unfound(locs, nb_unfound_from_loc, nb_unfound_to_loc);

  return m;
}

void HttpWrapper::add_route_info(Route& route) const {
  // Ordering locations for the given steps, excluding
  // breaks.
  std::vector<Location> non_break_locations;
  std::vector<unsigned> number_breaks_after;

  for (const auto& step : route.steps) {
    if (step.step_type == STEP_TYPE::BREAK) {
      if (!number_breaks_after.empty()) {
        ++(number_breaks_after.back());
      }
    } else {
      non_break_locations.push_back(step.location);
      number_breaks_after.push_back(0);
    }
  }
  assert(!non_break_locations.empty());

  std::string query =
    build_query(non_break_locations, _route_service, _extra_args);

  std::string json_content = this->run_query(query);

  rapidjson::Document infos;
  parse_response(infos, json_content);

  // Total distance and route geometry.
  route.distance = round_cost(get_total_distance(infos["routes"][0]));
  route.geometry = std::move(infos["routes"][0]["geometry"].GetString());

  auto nb_legs = get_legs_number(infos["routes"][0]);
  assert(nb_legs == non_break_locations.size() - 1);

  double sum_distance = 0;

  // Locate first non-break stop.
  const auto first_non_break =
    std::find_if(route.steps.begin(), route.steps.end(), [&](const auto& s) {
      return s.step_type != STEP_TYPE::BREAK;
    });
  unsigned steps_rank = std::distance(route.steps.begin(), first_non_break);

  // Zero distance up to first non-break step.
  for (unsigned i = 0; i <= steps_rank; ++i) {
    route.steps[i].distance = 0;
  }

  for (rapidjson::SizeType i = 0; i < nb_legs; ++i) {
    const auto& step = route.steps[steps_rank];

    // Next element in steps that is not a break and associated
    // distance after current route leg.
    auto& next_step = route.steps[steps_rank + number_breaks_after[i] + 1];
    Duration next_duration = next_step.duration - step.duration;
    double next_distance = get_distance_for_leg(infos["routes"][0], i);

    // Pro rata temporis distance update for breaks between current
    // non-breaks steps.
    for (unsigned b = 1; b <= number_breaks_after[i]; ++b) {
      auto& break_step = route.steps[steps_rank + b];
      break_step.distance = round_cost(
        sum_distance + ((break_step.duration - step.duration) * next_distance) /
                         next_duration);
    }

    sum_distance += next_distance;
    next_step.distance = round_cost(sum_distance);

    steps_rank += number_breaks_after[i] + 1;
  }

  // Unchanged distance after last non-break step.
  for (auto i = steps_rank; i < route.steps.size(); ++i) {
    route.steps[i].distance = sum_distance;
  }
}

} // namespace routing
} // namespace vroom
<|endoftext|>


<commit_before>/// @file
/// @version 3.3
///
/// @section LICENSE
///
/// This program is free software; you can redistribute it and/or modify it under
/// the terms of the BSD license: http://opensource.org/licenses/BSD-3-Clause

#ifdef _OPENSLES
#include <stdio.h>
#include <string.h>
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>

#include <hltypes/hlog.h>
#include <hltypes/hplatform.h>
#include <hltypes/hstring.h>

#include "AudioManager.h"
#include "Buffer.h"
#include "Category.h"
#include "OpenSLES_AudioManager.h"
#include "OpenSLES_Player.h"
#include "Sound.h"
#include "xal.h"

#define OPENSLES_MANAGER ((OpenSLES_AudioManager*)xal::mgr)

// this is just here to improve readability of code
#define CHECK_ERROR(message) \
	if (result != SL_RESULT_SUCCESS) \
	{ \
		hlog::error(xal::logTag, message); \
		this->player = NULL; \
		this->playerVolume = NULL; \
		this->playerBufferQueue = NULL; \
		if (this->playerObject != NULL) \
		{ \
			__CPP_WRAP(this->playerObject, Destroy); \
			this->playerObject = NULL; \
		} \
		return false; \
	}

namespace xal
{
	static const SLInterfaceID ids[] = { SL_IID_VOLUME, SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
	static const SLboolean reqs[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
	static const int reqsSize = 2;

	void OpenSLES_Player::_playCallback(SLPlayItf player, void* context, SLuint32 event)
	{
		if (event & SL_PLAYEVENT_HEADATEND)
		{
			OpenSLES_Player* player = (OpenSLES_Player*)context;
			if (player->sound->isStreamed())
			{
				player->mutex.lock();
				++player->_buffersProcessed;
				player->mutex.unlock();
			}
			else
			{
				player->active = false;
			}
		}
	}

	OpenSLES_Player::OpenSLES_Player(Sound* sound) : Player(sound), playing(false), active(false), stillPlaying(false),
		playerObject(NULL), player(NULL), playerVolume(NULL), playerBufferQueue(NULL)
	{
		for_iter (i, 0, STREAM_BUFFER_COUNT)
		{
			this->streamBuffers[i] = NULL;
		}
		if (this->sound->isStreamed())
		{
			for_iter(i, 0, STREAM_BUFFER_COUNT)
			{
				this->streamBuffers[i] = new unsigned char[STREAM_BUFFER_SIZE];
			}
		}
	}

	OpenSLES_Player::~OpenSLES_Player()
	{
		for_iter (i, 0, STREAM_BUFFER_COUNT)
		{
			delete [] this->streamBuffers[i];
			this->streamBuffers[i] = NULL;
		}
		if (this->playerObject != NULL)
		{
			__CPP_WRAP(this->playerObject, Destroy);
		}
	}

	void OpenSLES_Player::_update(float timeDelta)
	{
		this->stillPlaying = this->active;
		Player::_update(timeDelta);
		if (!this->stillPlaying && this->playing)
		{
			this->_stop();
		}
	}

	bool OpenSLES_Player::_systemIsPlaying()
	{
		return this->playing;
	}

	unsigned int OpenSLES_Player::_systemGetBufferPosition()
	{
		int bytes = 0;
		SLresult result = __CPP_WRAP_ARGS(this->player, GetMarkerPosition, (SLmillisecond*)&bytes);
		if (result != SL_RESULT_SUCCESS)
		{
			bytes = 0;
		}
		return bytes;
	}

	bool OpenSLES_Player::_systemPreparePlay()
	{
		if (this->playerObject != NULL)
		{
			return true;
		}
		// input / source
		SLDataLocator_AndroidSimpleBufferQueue inLocator;
		inLocator.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
		inLocator.numBuffers = (!this->sound->isStreamed() ? 1 : STREAM_BUFFER_COUNT);
		SLDataFormat_PCM format;
		format.formatType = SL_DATAFORMAT_PCM;
		format.numChannels = this->buffer->getChannels();
		format.samplesPerSec = this->buffer->getSamplingRate() * 1000; // in mHz, this parameter is misnamed for some reason
		int bitsPerSample = this->buffer->getBitsPerSample();
		if (bitsPerSample == 8)
		{
			format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_8;
		}
		else if (bitsPerSample == 16)
		{
			format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
		}
		else if (bitsPerSample == 20)
		{
			format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_20;
		}
		else if (bitsPerSample == 24)
		{
			format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_24;
		}
		else if (bitsPerSample == 28)
		{
			format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_28;
		}
		else if (bitsPerSample == 32)
		{
			format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32;
		}
		else
		{
			return false;
		}
		format.containerSize = (bitsPerSample + 7) / 8 * 8; // assuming all bits-per-sample formats are byte-aligned which not be the right way to do things
		if (format.numChannels == 1)
		{
			format.channelMask = SL_SPEAKER_FRONT_CENTER;
		}
		else
		{
			format.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
		}
		format.endianness = SL_BYTEORDER_LITTLEENDIAN;
		SLDataSource source;
		source.pLocator = &inLocator;
		source.pFormat = &format;
		// output / destination
		SLDataLocator_OutputMix outLocator;
		outLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX;
		outLocator.outputMix = OPENSLES_MANAGER->outputMixObject;
		SLDataSink destination;
		destination.pLocator = &outLocator;
		destination.pFormat = NULL;
		// create player
		SLresult result;
		result = __CPP_WRAP_ARGS(OPENSLES_MANAGER->engine, CreateAudioPlayer, &this->playerObject, &source, &destination, reqsSize, ids, reqs);
		CHECK_ERROR("Could not create player object!");
		result = __CPP_WRAP_ARGS(this->playerObject, Realize, SL_BOOLEAN_FALSE);
		CHECK_ERROR("Could not realize player object!");
		result = __CPP_WRAP_ARGS(this->playerObject, GetInterface, SL_IID_PLAY, &this->player);
		CHECK_ERROR("Could not get player play interface!");
		result = __CPP_WRAP_ARGS(this->playerObject, GetInterface, SL_IID_VOLUME, &this->playerVolume);
		CHECK_ERROR("Could not get player volume interface!");
		result = __CPP_WRAP_ARGS(this->playerObject, GetInterface, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &this->playerBufferQueue);
		CHECK_ERROR("Could not get player buffer queue interface!");
		result = __CPP_WRAP_ARGS(this->player, RegisterCallback, &OpenSLES_Player::_playCallback, this);
		CHECK_ERROR("Could not register callback!");
		result = __CPP_WRAP_ARGS(this->player, SetCallbackEventsMask, SL_PLAYEVENT_HEADATEND);
		CHECK_ERROR("Could not set callback mask!");
		//__CPP_WRAP_ARGS(this->player, SetPositionUpdatePeriod, 1);
		//(*this->player)->SetPositionUpdatePeriod
		return true;
	}

	void OpenSLES_Player::_systemPrepareBuffer()
	{
		if (!this->sound->isStreamed())
		{
			if (!this->paused)
			{
				this->_submitBuffer(this->buffer->getStream(), this->buffer->getSize());
			}
			return;
		}
		int count = STREAM_BUFFER_COUNT;
		if (this->paused)
		{
			count -= this->buffersSubmitted;
		}
		else
		{
			this->buffersSubmitted = 0;
		}
		if (count > 0)
		{
			count = this->_fillStreamBuffers(count);
			if (count > 0)
			{
				this->_submitStreamBuffers(count);
			}
		}
	}

	void OpenSLES_Player::_systemUpdateGain()
	{
		if (this->playerVolume != NULL)
		{
			float gain = this->_calcGain();
			SLmillibel value = -9600; // minimum possible attenuation/volume in mB
			if (gain > 0.01f)
			{
				value = (SLmillibel)(log10(gain) * 2000);
			}
			__CPP_WRAP_ARGS(this->playerVolume, SetVolumeLevel, value);
		}
	}

	void OpenSLES_Player::_systemUpdatePitch()
	{
		static bool checked = false;
		if (!checked)
		{
			hlog::warn(xal::logTag, "Pitch change is not supported in this implementation! This message is only logged once.");
		}
		checked = true;
	}

	void OpenSLES_Player::_systemPlay()
	{
		this->_getLastProcessedBuffersCount();
		SLresult result = __CPP_WRAP_ARGS(this->player, SetPlayState, SL_PLAYSTATE_PLAYING);
		if (result == SL_RESULT_SUCCESS)
		{
			__CPP_WRAP_ARGS(this->player, SetMarkerPosition, 0); // required to avoid a constant warning by OpenSLES when using GetMarkerPosition()
			this->playing = true;
			this->stillPlaying = true;
			this->active = true; // required, because otherwise the buffer will think it's done
		}
		else
		{
			hlog::warn(xal::logTag, "Could not start: " + this->sound->getFilename());
		}
	}

	int OpenSLES_Player::_systemStop()
	{
		if (this->playing)
		{
			SLresult result = __CPP_WRAP_ARGS(this->player, SetPlayState, this->paused ? SL_PLAYSTATE_PAUSED : SL_PLAYSTATE_STOPPED);
			if (result == SL_RESULT_SUCCESS)
			{
				if (this->paused)
				{
					if (this->sound->isStreamed())
					{
						int processed = this->_getLastProcessedBuffersCount();
						this->buffersSubmitted -= processed;
						result = processed * STREAM_BUFFER_SIZE;
					}
				}
				else
				{
					this->buffer->rewind();
					__CPP_WRAP(this->playerBufferQueue, Clear);
					if (this->sound->isStreamed())
					{
						this->_getLastProcessedBuffersCount(); // to reset the processed buffers count to 0
						this->buffersSubmitted = 0;
					}
				}
				this->playing = false;
				this->stillPlaying = false;
			}
			else
			{
				hlog::warn(xal::logTag, "Could not stop: " + this->sound->getFilename());
			}
		}
		return 0;
	}

	int OpenSLES_Player::_systemUpdateStream()
	{
		int processed = this->_getLastProcessedBuffersCount();
		if (processed == 0)
		{
			this->stillPlaying = true; // don't remove, it prevents streamed sounds from being stopped
			return 0;
		}
		this->buffersSubmitted -= processed;
		int count = this->_fillStreamBuffers(processed);
		if (count > 0)
		{
			this->_submitStreamBuffers(count);
			this->stillPlaying = true; // in case underrun happened, sound is regarded as stopped so let's just bitch-slap it and get this over with
		}
		if (this->buffersSubmitted == 0)
		{
			this->_stop();
		}
		return (processed * STREAM_BUFFER_SIZE);
	}

	void OpenSLES_Player::_submitBuffer(hstream& stream)
	{
		SLresult result = __CPP_WRAP_ARGS(this->playerBufferQueue, Enqueue, &stream[0], stream.size());
		if (result != SL_RESULT_SUCCESS)
		{
			hlog::warn(xal::logTag, "Could not queue buffer!");
		}
	}

	int OpenSLES_Player::_fillStreamBuffers(int count)
	{
		int size = this->buffer->load(this->looping, count * STREAM_BUFFER_SIZE);
		int filled = (size + STREAM_BUFFER_SIZE - 1) / STREAM_BUFFER_SIZE;
		hstream& stream = this->buffer->getStream();
		int currentSize = 0;
		for_iter (i, 0, filled)
		{
			currentSize = hmin(size, STREAM_BUFFER_SIZE);
			memcpy(this->streamBuffers[this->bufferIndex], &stream[i * STREAM_BUFFER_SIZE], currentSize);
			if (currentSize < STREAM_BUFFER_SIZE)
			{
				memset(&this->streamBuffers[this->bufferIndex][currentSize], 0, STREAM_BUFFER_SIZE - currentSize);
			}
			this->bufferIndex = (this->bufferIndex + 1) % STREAM_BUFFER_COUNT;
			size -= STREAM_BUFFER_SIZE;
		}
		return filled;
	}

	void OpenSLES_Player::_submitStreamBuffers(int count)
	{
		int queued = 0;
		int index = (this->bufferIndex + STREAM_BUFFER_COUNT - count) % STREAM_BUFFER_COUNT;
		for_iter (i, 0, count)
		{
			SLresult result = __CPP_WRAP_ARGS(this->playerBufferQueue, Enqueue, this->streamBuffers[index], STREAM_BUFFER_SIZE);
			if (result != SL_RESULT_SUCCESS)
			{
				hlog::warn(xal::logTag, "Could not queue streamed buffer!");
				break;
			}
			++queued;
			index = (index + 1) % STREAM_BUFFER_COUNT;
		}
		this->buffersSubmitted += queued;
	}

	int OpenSLES_Player::_getLastProcessedBuffersCount()
	{
		this->mutex.lock();
		int value = this->_buffersProcessed;
		this->_buffersProcessed = 0;
		this->mutex.unlock();
		return value;
	}

}
#endif
<commit_msg>- small OpenSLES fix<commit_after>/// @file
/// @version 3.3
///
/// @section LICENSE
///
/// This program is free software; you can redistribute it and/or modify it under
/// the terms of the BSD license: http://opensource.org/licenses/BSD-3-Clause

#ifdef _OPENSLES
#include <stdio.h>
#include <string.h>
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>

#include <hltypes/hlog.h>
#include <hltypes/hplatform.h>
#include <hltypes/hstring.h>

#include "AudioManager.h"
#include "Buffer.h"
#include "Category.h"
#include "OpenSLES_AudioManager.h"
#include "OpenSLES_Player.h"
#include "Sound.h"
#include "xal.h"

#define OPENSLES_MANAGER ((OpenSLES_AudioManager*)xal::mgr)

// this is just here to improve readability of code
#define CHECK_ERROR(message) \
	if (result != SL_RESULT_SUCCESS) \
	{ \
		hlog::error(xal::logTag, message); \
		this->player = NULL; \
		this->playerVolume = NULL; \
		this->playerBufferQueue = NULL; \
		if (this->playerObject != NULL) \
		{ \
			__CPP_WRAP(this->playerObject, Destroy); \
			this->playerObject = NULL; \
		} \
		return false; \
	}

namespace xal
{
	static const SLInterfaceID ids[] = { SL_IID_VOLUME, SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
	static const SLboolean reqs[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
	static const int reqsSize = 2;

	void OpenSLES_Player::_playCallback(SLPlayItf player, void* context, SLuint32 event)
	{
		if (event & SL_PLAYEVENT_HEADATEND)
		{
			OpenSLES_Player* player = (OpenSLES_Player*)context;
			if (player->sound->isStreamed())
			{
				player->mutex.lock();
				++player->_buffersProcessed;
				player->mutex.unlock();
			}
			else
			{
				player->active = false;
			}
		}
	}

	OpenSLES_Player::OpenSLES_Player(Sound* sound) : Player(sound), playing(false), active(false), stillPlaying(false),
		playerObject(NULL), player(NULL), playerVolume(NULL), playerBufferQueue(NULL)
	{
		for_iter (i, 0, STREAM_BUFFER_COUNT)
		{
			this->streamBuffers[i] = NULL;
		}
		if (this->sound->isStreamed())
		{
			for_iter(i, 0, STREAM_BUFFER_COUNT)
			{
				this->streamBuffers[i] = new unsigned char[STREAM_BUFFER_SIZE];
			}
		}
	}

	OpenSLES_Player::~OpenSLES_Player()
	{
		for_iter (i, 0, STREAM_BUFFER_COUNT)
		{
			delete [] this->streamBuffers[i];
			this->streamBuffers[i] = NULL;
		}
		if (this->playerObject != NULL)
		{
			__CPP_WRAP(this->playerObject, Destroy);
		}
	}

	void OpenSLES_Player::_update(float timeDelta)
	{
		this->stillPlaying = this->active;
		Player::_update(timeDelta);
		if (!this->stillPlaying && this->playing)
		{
			this->_stop();
		}
	}

	bool OpenSLES_Player::_systemIsPlaying()
	{
		return this->playing;
	}

	unsigned int OpenSLES_Player::_systemGetBufferPosition()
	{
		int bytes = 0;
		SLresult result = __CPP_WRAP_ARGS(this->player, GetMarkerPosition, (SLmillisecond*)&bytes);
		if (result != SL_RESULT_SUCCESS)
		{
			bytes = 0;
		}
		return bytes;
	}

	bool OpenSLES_Player::_systemPreparePlay()
	{
		if (this->playerObject != NULL)
		{
			return true;
		}
		// input / source
		SLDataLocator_AndroidSimpleBufferQueue inLocator;
		inLocator.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
		inLocator.numBuffers = (!this->sound->isStreamed() ? 1 : STREAM_BUFFER_COUNT);
		SLDataFormat_PCM format;
		format.formatType = SL_DATAFORMAT_PCM;
		format.numChannels = this->buffer->getChannels();
		format.samplesPerSec = this->buffer->getSamplingRate() * 1000; // in mHz, this parameter is misnamed for some reason
		int bitsPerSample = this->buffer->getBitsPerSample();
		if (bitsPerSample == 8)
		{
			format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_8;
		}
		else if (bitsPerSample == 16)
		{
			format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
		}
		else if (bitsPerSample == 20)
		{
			format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_20;
		}
		else if (bitsPerSample == 24)
		{
			format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_24;
		}
		else if (bitsPerSample == 28)
		{
			format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_28;
		}
		else if (bitsPerSample == 32)
		{
			format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32;
		}
		else
		{
			return false;
		}
		format.containerSize = (bitsPerSample + 7) / 8 * 8; // assuming all bits-per-sample formats are byte-aligned which not be the right way to do things
		if (format.numChannels == 1)
		{
			format.channelMask = SL_SPEAKER_FRONT_CENTER;
		}
		else
		{
			format.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
		}
		format.endianness = SL_BYTEORDER_LITTLEENDIAN;
		SLDataSource source;
		source.pLocator = &inLocator;
		source.pFormat = &format;
		// output / destination
		SLDataLocator_OutputMix outLocator;
		outLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX;
		outLocator.outputMix = OPENSLES_MANAGER->outputMixObject;
		SLDataSink destination;
		destination.pLocator = &outLocator;
		destination.pFormat = NULL;
		// create player
		SLresult result;
		result = __CPP_WRAP_ARGS(OPENSLES_MANAGER->engine, CreateAudioPlayer, &this->playerObject, &source, &destination, reqsSize, ids, reqs);
		CHECK_ERROR("Could not create player object!");
		result = __CPP_WRAP_ARGS(this->playerObject, Realize, SL_BOOLEAN_FALSE);
		CHECK_ERROR("Could not realize player object!");
		result = __CPP_WRAP_ARGS(this->playerObject, GetInterface, SL_IID_PLAY, &this->player);
		CHECK_ERROR("Could not get player play interface!");
		result = __CPP_WRAP_ARGS(this->playerObject, GetInterface, SL_IID_VOLUME, &this->playerVolume);
		CHECK_ERROR("Could not get player volume interface!");
		result = __CPP_WRAP_ARGS(this->playerObject, GetInterface, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &this->playerBufferQueue);
		CHECK_ERROR("Could not get player buffer queue interface!");
		result = __CPP_WRAP_ARGS(this->player, RegisterCallback, &OpenSLES_Player::_playCallback, this);
		CHECK_ERROR("Could not register callback!");
		result = __CPP_WRAP_ARGS(this->player, SetCallbackEventsMask, SL_PLAYEVENT_HEADATEND);
		CHECK_ERROR("Could not set callback mask!");
		//__CPP_WRAP_ARGS(this->player, SetPositionUpdatePeriod, 1);
		//(*this->player)->SetPositionUpdatePeriod
		return true;
	}

	void OpenSLES_Player::_systemPrepareBuffer()
	{
		if (!this->sound->isStreamed())
		{
			if (!this->paused)
			{
				this->_submitBuffer(this->buffer->getStream());
			}
			return;
		}
		int count = STREAM_BUFFER_COUNT;
		if (this->paused)
		{
			count -= this->buffersSubmitted;
		}
		else
		{
			this->buffersSubmitted = 0;
		}
		if (count > 0)
		{
			count = this->_fillStreamBuffers(count);
			if (count > 0)
			{
				this->_submitStreamBuffers(count);
			}
		}
	}

	void OpenSLES_Player::_systemUpdateGain()
	{
		if (this->playerVolume != NULL)
		{
			float gain = this->_calcGain();
			SLmillibel value = -9600; // minimum possible attenuation/volume in mB
			if (gain > 0.01f)
			{
				value = (SLmillibel)(log10(gain) * 2000);
			}
			__CPP_WRAP_ARGS(this->playerVolume, SetVolumeLevel, value);
		}
	}

	void OpenSLES_Player::_systemUpdatePitch()
	{
		static bool checked = false;
		if (!checked)
		{
			hlog::warn(xal::logTag, "Pitch change is not supported in this implementation! This message is only logged once.");
		}
		checked = true;
	}

	void OpenSLES_Player::_systemPlay()
	{
		this->_getLastProcessedBuffersCount();
		SLresult result = __CPP_WRAP_ARGS(this->player, SetPlayState, SL_PLAYSTATE_PLAYING);
		if (result == SL_RESULT_SUCCESS)
		{
			__CPP_WRAP_ARGS(this->player, SetMarkerPosition, 0); // required to avoid a constant warning by OpenSLES when using GetMarkerPosition()
			this->playing = true;
			this->stillPlaying = true;
			this->active = true; // required, because otherwise the buffer will think it's done
		}
		else
		{
			hlog::warn(xal::logTag, "Could not start: " + this->sound->getFilename());
		}
	}

	int OpenSLES_Player::_systemStop()
	{
		if (this->playing)
		{
			SLresult result = __CPP_WRAP_ARGS(this->player, SetPlayState, this->paused ? SL_PLAYSTATE_PAUSED : SL_PLAYSTATE_STOPPED);
			if (result == SL_RESULT_SUCCESS)
			{
				if (this->paused)
				{
					if (this->sound->isStreamed())
					{
						int processed = this->_getLastProcessedBuffersCount();
						this->buffersSubmitted -= processed;
						result = processed * STREAM_BUFFER_SIZE;
					}
				}
				else
				{
					this->buffer->rewind();
					__CPP_WRAP(this->playerBufferQueue, Clear);
					if (this->sound->isStreamed())
					{
						this->_getLastProcessedBuffersCount(); // to reset the processed buffers count to 0
						this->buffersSubmitted = 0;
					}
				}
				this->playing = false;
				this->stillPlaying = false;
			}
			else
			{
				hlog::warn(xal::logTag, "Could not stop: " + this->sound->getFilename());
			}
		}
		return 0;
	}

	int OpenSLES_Player::_systemUpdateStream()
	{
		int processed = this->_getLastProcessedBuffersCount();
		if (processed == 0)
		{
			this->stillPlaying = true; // don't remove, it prevents streamed sounds from being stopped
			return 0;
		}
		this->buffersSubmitted -= processed;
		int count = this->_fillStreamBuffers(processed);
		if (count > 0)
		{
			this->_submitStreamBuffers(count);
			this->stillPlaying = true; // in case underrun happened, sound is regarded as stopped so let's just bitch-slap it and get this over with
		}
		if (this->buffersSubmitted == 0)
		{
			this->_stop();
		}
		return (processed * STREAM_BUFFER_SIZE);
	}

	void OpenSLES_Player::_submitBuffer(hstream& stream)
	{
		SLresult result = __CPP_WRAP_ARGS(this->playerBufferQueue, Enqueue, &stream[0], stream.size());
		if (result != SL_RESULT_SUCCESS)
		{
			hlog::warn(xal::logTag, "Could not queue buffer!");
		}
	}

	int OpenSLES_Player::_fillStreamBuffers(int count)
	{
		int size = this->buffer->load(this->looping, count * STREAM_BUFFER_SIZE);
		int filled = (size + STREAM_BUFFER_SIZE - 1) / STREAM_BUFFER_SIZE;
		hstream& stream = this->buffer->getStream();
		int currentSize = 0;
		for_iter (i, 0, filled)
		{
			currentSize = hmin(size, STREAM_BUFFER_SIZE);
			memcpy(this->streamBuffers[this->bufferIndex], &stream[i * STREAM_BUFFER_SIZE], currentSize);
			if (currentSize < STREAM_BUFFER_SIZE)
			{
				memset(&this->streamBuffers[this->bufferIndex][currentSize], 0, STREAM_BUFFER_SIZE - currentSize);
			}
			this->bufferIndex = (this->bufferIndex + 1) % STREAM_BUFFER_COUNT;
			size -= STREAM_BUFFER_SIZE;
		}
		return filled;
	}

	void OpenSLES_Player::_submitStreamBuffers(int count)
	{
		int queued = 0;
		int index = (this->bufferIndex + STREAM_BUFFER_COUNT - count) % STREAM_BUFFER_COUNT;
		for_iter (i, 0, count)
		{
			SLresult result = __CPP_WRAP_ARGS(this->playerBufferQueue, Enqueue, this->streamBuffers[index], STREAM_BUFFER_SIZE);
			if (result != SL_RESULT_SUCCESS)
			{
				hlog::warn(xal::logTag, "Could not queue streamed buffer!");
				break;
			}
			++queued;
			index = (index + 1) % STREAM_BUFFER_COUNT;
		}
		this->buffersSubmitted += queued;
	}

	int OpenSLES_Player::_getLastProcessedBuffersCount()
	{
		this->mutex.lock();
		int value = this->_buffersProcessed;
		this->_buffersProcessed = 0;
		this->mutex.unlock();
		return value;
	}

}
#endif
<|endoftext|>