<commit_before>/*

    ui_features.hpp - Support file for writing LV2 plugins in C++

    Copyright (C) 2013 Michael Fisher <mfisher31@gmail.com>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307  USA

 */

#ifndef LVTK_UI_FEATURES_HPP
#define LVTK_UI_FEATURES_HPP

#include <lv2/lv2plug.in/ns/extensions/ui/ui.h>

namespace lvtk {

    /** The PortSubscribe Mixin.
        @ingroup guimixins
        @see The internal struct I for details.
     */
    template<bool Required = false>
    struct PortSubscribe
    {
        template<class Derived>
        struct I : Extension<Required>
        {
            I() { memset ((void*)&m_subscribe, 0, sizeof (LV2UI_Port_Subscribe)); }

            /** @internal */
            static void
            map_feature_handlers (FeatureHandlerMap& hmap)
            {
                hmap[LV2_UI__portSubscribe] = &I<Derived>::handle_feature;
            }

            /** @internal */
            static void
            handle_feature (void* instance, FeatureData data)
            {
                Derived* d = reinterpret_cast<Derived*>(instance);
                I<Derived>* mixin = static_cast<I<Derived>*>(d);

                LV2UI_Port_Subscribe* ps (reinterpret_cast<LV2UI_Port_Subscribe*> (data));
                memcpy (&mixin->m_subscribe, ps, sizeof (LV2UI_Port_Subscribe));
                mixin->m_ok = (ps->handle      != NULL &&
                               ps->subscribe   != NULL &&
                               ps->unsubscribe != NULL);
            }

            bool
            check_ok()
            {
                if (! Required)
                    this->m_ok = true;

                if (LVTK_DEBUG)
                {
                    std::clog << "    [PortSubscribe] Validation "
                              << (this->m_ok ? "succeeded" : "failed")
                              << "." << std::endl;
                }

                return this->m_ok;
            }

        protected:

            /**
                Subscribe to updates for a port.

                This means that the host will call the UI's port_event() function when
                the port value changes (as defined by protocol).

                Calling this function with the same @p port_index and @p port_protocol
                as an already active subscription has no effect.

                @param port The index of the port.
                @param protocol The URID of the ui:PortProtocol.
                @param features Features for this subscription.
                @return true on success.
            */
            bool
            subscribe (uint32_t port, uint32_t protocol, const Feature* const* features)
            {
                if (portsubscribe_is_valid())
                    return (0 == m_subscribe.subscribe (m_subscribe.handle, port, protocol, features));
                return false;
            }

            /**
                Unsubscribe from updates for a port.

                This means that the host will cease calling calling port_event() when
                the port value changes.

                Calling this function with a @p port_index and @p port_protocol that
                does not refer to an active port subscription has no effect.

                @param port_index The index of the port.
                @param port_protocol The URID of the ui:PortProtocol.
                @param features Features for this subscription.
                @return true on success.
            */
            bool
            unsubscribe (uint32_t port, uint32_t protocol, const Feature* const* features)
            {
                if (portsubscribe_is_valid())
                    return (0 == m_subscribe.unsubscribe (m_subscribe.handle, port, protocol, features));
                return false;
            }

        private:
            LV2UI_Port_Subscribe m_subscribe;

            /** @internal */
            bool portsubscribe_is_valid() const
            {
                return (m_subscribe.handle      != NULL &&
                        m_subscribe.subscribe   != NULL &&
                        m_subscribe.unsubscribe != NULL);
            }
        };
    };

    /** The PortMap Mixin.
        @ingroup guimixins
        @see The internal struct I for details.
     */
    template<bool Required = false>
    struct PortMap
    {
        template<class Derived>
        struct I : Extension<Required>
        {
            I() { memset (&m_pmap, 0, sizeof (LV2UI_Port_Map)); }

            /** @internal */
            static void
            map_feature_handlers (FeatureHandlerMap& hmap)
            {
                hmap [LV2_UI__portMap] = &I<Derived>::handle_feature;
            }

            /** @internal */
            static void
            handle_feature (void* instance, void* data)
            {
                Derived* d = reinterpret_cast<Derived*>(instance);
                I<Derived>* mixin = static_cast<I<Derived>*>(d);

                if (LV2UI_Port_Map* pm = reinterpret_cast<LV2UI_Port_Map*> (data))
                    memcpy (&mixin->m_pmap, pm, sizeof (LV2UI_Port_Map));

                mixin->m_ok = (mixin->m_pmap.handle != 0 && mixin->m_pmap.port_index != 0);
            }

            bool
            check_ok()
            {
                if (! Required)
                    this->m_ok = true;

                if (LVTK_DEBUG)
                {
                    std::clog << "    [PortMap] Validation "
                              << (this->m_ok ? "succeeded" : "failed")
                              << "." << std::endl;
                }

                return this->m_ok;
            }

        protected:

            /** Return the port index for a given symbol
                @param symbol The port symbol to get an index for
             */
            uint32_t
            port_index (const char* symbol)
            {
                if (portmap_is_valid())
                    return m_pmap.port_index (m_pmap.handle, symbol);
                return LV2UI_INVALID_PORT_INDEX;
            }

        private:
            LV2UI_Port_Map m_pmap;

            /** @internal */
            bool portmap_is_valid() const
            {
                return (m_pmap.handle     != NULL &&
                        m_pmap.port_index != NULL);
            }

        };
    };

    /** The Touch Mixin.
        @ingroup guimixins
        @see The internal struct I for details.
     */
    template<bool Required = false>
    struct Touch
    {
        template<class Derived>
        struct I : Extension<Required>
        {
            I() { memset (&m_touch, 0, sizeof (LV2UI_Touch)); }

            /** @internal */
            static void
            map_feature_handlers (FeatureHandlerMap& hmap)
            {
                hmap[LV2_UI__touch] = &I<Derived>::handle_feature;
            }

            /** @internal */
            static void
            handle_feature (void* instance, void* data)
            {
                Derived* d = reinterpret_cast<Derived*>(instance);
                I<Derived>* mixin = static_cast<I<Derived>*>(d);

                LV2UI_Touch* t (reinterpret_cast<LV2UI_Touch*> (data));
                mixin->m_touch.handle = t->handle;
                mixin->m_touch.touch  = t->touch;
                mixin->m_ok = (mixin->m_touch.handle != NULL &&
                               mixin->m_touch.touch  != NULL);
            }

            bool
            check_ok()
            {
                if (! Required)
                    this->m_ok = true;

                if (LVTK_DEBUG)
                {
                    std::clog << "    [Touch] Validation "
                              << (this->m_ok ? "succeeded" : "failed")
                              << "." << std::endl;
                }

                return this->m_ok;
            }

        protected:

           /**
               Notify the host that a control has been grabbed or released.

               @param port The index of the port associated with the control.
               @param grabbed If true, the control has been grabbed, otherwise the
               control has been released.
            */
            void
            touch (uint32_t port, bool grabbed)
            {
                if (touch_is_valid())
                    m_touch.touch (m_touch.handle, port, grabbed);
            }

        private:
            LV2UI_Touch m_touch;

            /** @internal */
            bool touch_is_valid() const
            {
                return m_touch.handle != NULL &&
                       m_touch.touch  != NULL;
            }

        };
    };

    /** The Parent Mixin.
        @ingroup guimixins
        @see The internal struct I for details.
     */
    template<bool Required = false>
    struct Parent
    {
        template<class Derived>
        struct I : Extension<Required>
        {
            I() : p_parent (0) { }

            /** @internal */
            static void
            map_feature_handlers(FeatureHandlerMap& hmap)
            {
                hmap[LV2_UI__parent] = &I<Derived>::handle_feature;
            }

            /** @internal */
            static void
            handle_feature(void* instance, void* data)
            {
                Derived* d = reinterpret_cast<Derived*>(instance);
                I<Derived>* mixin = static_cast<I<Derived>*>(d);

                mixin->p_parent = reinterpret_cast<LV2UI_Widget*> (data);
                mixin->m_ok = mixin->p_parent != 0;
            }

            bool
            check_ok()
            {
                if (! Required)
                    this->m_ok = true;

                if (LVTK_DEBUG)
                {
                    std::clog << "    [Parent] Validation "
                            << (this->m_ok ? "succeeded" : "failed")
                            << "." << std::endl;
                }
                return this->m_ok;
            }

        protected:

            /** Get the parent widget if any
                @return The parent LV2_Widget or NULL if not provided
             */
            LV2UI_Widget*
            get_parent()
            {
               return p_parent;
            }

        private:

            LV2UI_Widget* p_parent;

        };
    };

    /** The UIResize Mixin.
        @ingroup guimixins
        @see The internal struct I for details.
     */
    template<bool Required = true>
    struct UIResize
    {
        template<class Derived>
        struct I : Extension<Required>
        {
            I()
            {
                m_resize.handle    = NULL;
                m_resize.ui_resize = NULL;
            }

            /** @internal */
            static void
            map_feature_handlers(FeatureHandlerMap& hmap)
            {
                hmap[LV2_UI__resize] = &I<Derived>::handle_feature;
            }

            /** @internal */
            static void
            handle_feature(void* instance, void* data)
            {
                Derived* d = reinterpret_cast<Derived*>(instance);
                I<Derived>* mixin = static_cast<I<Derived>*>(d);

                LV2UI_Resize* rz (reinterpret_cast<LV2UI_Resize*> (data));
                mixin->m_resize.handle    = rz->handle;
                mixin->m_resize.ui_resize = rz->ui_resize;
                mixin->m_ok = true;
            }

            /** @skip */
            static const void*
            extension_data (const char* uri)
            {
                // TODO: Need to implement ui resize host-side.
                // FIXME: Need to provide a pointer, somehow, to
                //        extension_data
                return 0;
            }

            bool
            check_ok()
            {
                if (! Required) {
                   this->m_ok = true;
                } else {
                   this->m_ok = plugin_rsz_is_valid();
                }

                if (LVTK_DEBUG)
                {
                    std::clog << "    [UIResize] Validation "
                              << (this->m_ok ? "succeeded" : "failed")
                              << "." << std::endl;
                }
                return this->m_ok;
            }

        protected:

            /**
               Request a size change.
               Call this method to inform the host about the size of the UI.

               @return true on success.
             */
            bool
            ui_resize (int width, int height)
            {
                if (plugin_rsz_is_valid())
                   return (0 == m_resize.ui_resize (m_resize.handle, width, height));

                return false;
            }

        private:

            LV2UI_Resize m_resize;
            bool plugin_rsz_is_valid() const { return m_resize.ui_resize != NULL && m_resize.handle != NULL; }

        };
    };

}

#endif /* LVTK_UI_FEATURES_HPP */
<commit_msg>housekeeping<commit_after>/*

    ui_features.hpp - Support file for writing LV2 plugins in C++

    Copyright (C) 2013 Michael Fisher <mfisher31@gmail.com>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307  USA

 */

#ifndef LVTK_UI_FEATURES_HPP
#define LVTK_UI_FEATURES_HPP

#include <lv2/lv2plug.in/ns/extensions/ui/ui.h>

namespace lvtk {

    /** The PortSubscribe Mixin.
        @ingroup guimixins
        @see The internal struct I for details.
     */
    template<bool Required = false>
    struct PortSubscribe
    {
        template<class Derived>
        struct I : Extension<Required>
        {
            I() { memset ((void*)&m_subscribe, 0, sizeof (LV2UI_Port_Subscribe)); }

            /** @internal */
            static void
            map_feature_handlers (FeatureHandlerMap& hmap)
            {
                hmap[LV2_UI__portSubscribe] = &I<Derived>::handle_feature;
            }

            /** @internal */
            static void
            handle_feature (void* instance, FeatureData data)
            {
                Derived* d = reinterpret_cast<Derived*>(instance);
                I<Derived>* mixin = static_cast<I<Derived>*>(d);

                LV2UI_Port_Subscribe* ps (reinterpret_cast<LV2UI_Port_Subscribe*> (data));
                memcpy (&mixin->m_subscribe, ps, sizeof (LV2UI_Port_Subscribe));
                mixin->m_ok = (ps->handle      != NULL &&
                               ps->subscribe   != NULL &&
                               ps->unsubscribe != NULL);
            }

            bool
            check_ok()
            {
                if (! Required)
                    this->m_ok = true;

                if (LVTK_DEBUG)
                {
                    std::clog << "    [PortSubscribe] Validation "
                              << (this->m_ok ? "succeeded" : "failed")
                              << "." << std::endl;
                }

                return this->m_ok;
            }

        protected:

            /**
                Subscribe to updates for a port.

                This means that the host will call the UI's port_event() function when
                the port value changes (as defined by protocol).

                Calling this function with the same @p port_index and @p port_protocol
                as an already active subscription has no effect.

                @param port The index of the port.
                @param protocol The URID of the ui:PortProtocol.
                @param features Features for this subscription.
                @return true on success.
            */
            bool
            subscribe (uint32_t port, uint32_t protocol, const Feature* const* features)
            {
                if (portsubscribe_is_valid())
                    return (0 == m_subscribe.subscribe (m_subscribe.handle, port, protocol, features));
                return false;
            }

            /**
                Unsubscribe from updates for a port.

                This means that the host will cease calling calling port_event() when
                the port value changes.

                Calling this function with a @p port_index and @p port_protocol that
                does not refer to an active port subscription has no effect.

                @param port_index The index of the port.
                @param port_protocol The URID of the ui:PortProtocol.
                @param features Features for this subscription.
                @return true on success.
            */
            bool
            unsubscribe (uint32_t port, uint32_t protocol, const Feature* const* features)
            {
                if (portsubscribe_is_valid())
                    return (0 == m_subscribe.unsubscribe (m_subscribe.handle, port, protocol, features));
                return false;
            }

        private:
            LV2UI_Port_Subscribe m_subscribe;

            /** @internal */
            bool portsubscribe_is_valid() const
            {
                return (m_subscribe.handle      != NULL &&
                        m_subscribe.subscribe   != NULL &&
                        m_subscribe.unsubscribe != NULL);
            }
        };
    };

    /** The PortMap Mixin.
        @ingroup guimixins
        @see The internal struct I for details.
     */
    template<bool Required = false>
    struct PortMap
    {
        template<class Derived>
        struct I : Extension<Required>
        {
            I() { memset (&m_pmap, 0, sizeof (LV2UI_Port_Map)); }

            /** @internal */
            static void
            map_feature_handlers (FeatureHandlerMap& hmap)
            {
                hmap [LV2_UI__portMap] = &I<Derived>::handle_feature;
            }

            /** @internal */
            static void
            handle_feature (void* instance, void* data)
            {
                Derived* d = reinterpret_cast<Derived*>(instance);
                I<Derived>* mixin = static_cast<I<Derived>*>(d);

                if (LV2UI_Port_Map* pm = reinterpret_cast<LV2UI_Port_Map*> (data))
                    memcpy (&mixin->m_pmap, pm, sizeof (LV2UI_Port_Map));

                mixin->m_ok = (mixin->m_pmap.handle != 0 && mixin->m_pmap.port_index != 0);
            }

            bool
            check_ok()
            {
                if (! Required)
                    this->m_ok = true;

                if (LVTK_DEBUG)
                {
                    std::clog << "    [PortMap] Validation "
                              << (this->m_ok ? "succeeded" : "failed")
                              << "." << std::endl;
                }

                return this->m_ok;
            }

        protected:

            /** Return the port index for a given symbol
                @param symbol The port symbol to get an index for
             */
            uint32_t
            port_index (const char* symbol)
            {
                if (portmap_is_valid())
                    return m_pmap.port_index (m_pmap.handle, symbol);
                return LV2UI_INVALID_PORT_INDEX;
            }

        private:
            LV2UI_Port_Map m_pmap;

            /** @internal */
            bool portmap_is_valid() const
            {
                return (m_pmap.handle     != NULL &&
                        m_pmap.port_index != NULL);
            }

        };
    };

    /** The Touch Mixin.
        @ingroup guimixins
        @see The internal struct I for details.
     */
    template<bool Required = false>
    struct Touch
    {
        template<class Derived>
        struct I : Extension<Required>
        {
            I() { memset (&m_touch, 0, sizeof (LV2UI_Touch)); }

            /** @internal */
            static void
            map_feature_handlers (FeatureHandlerMap& hmap)
            {
                hmap[LV2_UI__touch] = &I<Derived>::handle_feature;
            }

            /** @internal */
            static void
            handle_feature (void* instance, void* data)
            {
                Derived* d = reinterpret_cast<Derived*>(instance);
                I<Derived>* mixin = static_cast<I<Derived>*>(d);

                LV2UI_Touch* t (reinterpret_cast<LV2UI_Touch*> (data));
                mixin->m_touch.handle = t->handle;
                mixin->m_touch.touch  = t->touch;
                mixin->m_ok = (mixin->m_touch.handle != NULL &&
                               mixin->m_touch.touch  != NULL);
            }

            bool
            check_ok()
            {
                if (! Required)
                    this->m_ok = true;

                if (LVTK_DEBUG)
                {
                    std::clog << "    [Touch] Validation "
                              << (this->m_ok ? "succeeded" : "failed")
                              << "." << std::endl;
                }

                return this->m_ok;
            }

        protected:

           /**
               Notify the host that a control has been grabbed or released.

               @param port The index of the port associated with the control.
               @param grabbed If true, the control has been grabbed, otherwise the
               control has been released.
            */
            void
            touch (uint32_t port, bool grabbed)
            {
                if (touch_is_valid())
                    m_touch.touch (m_touch.handle, port, grabbed);
            }

        private:
            LV2UI_Touch m_touch;

            /** @internal */
            bool touch_is_valid() const
            {
                return m_touch.handle != NULL &&
                       m_touch.touch  != NULL;
            }

        };
    };

    /** The Parent Mixin.
        @ingroup guimixins
        @see The internal struct I for details.
     */
    template<bool Required = false>
    struct Parent
    {
        template<class Derived>
        struct I : Extension<Required>
        {
            I() : p_parent (0) { }

            /** @internal */
            static void
            map_feature_handlers(FeatureHandlerMap& hmap)
            {
                hmap[LV2_UI__parent] = &I<Derived>::handle_feature;
            }

            /** @internal */
            static void
            handle_feature(void* instance, void* data)
            {
                Derived* d = reinterpret_cast<Derived*>(instance);
                I<Derived>* mixin = static_cast<I<Derived>*>(d);

                mixin->p_parent = reinterpret_cast<LV2UI_Widget*> (data);
                mixin->m_ok = mixin->p_parent != 0;
            }

            bool
            check_ok()
            {
                if (! Required)
                    this->m_ok = true;

                if (LVTK_DEBUG)
                {
                    std::clog << "    [Parent] Validation "
                            << (this->m_ok ? "succeeded" : "failed")
                            << "." << std::endl;
                }
                return this->m_ok;
            }

        protected:

            /** Get the parent widget if any
                @return The parent LV2_Widget or NULL if not provided
             */
            LV2UI_Widget*
            get_parent()
            {
               return p_parent;
            }

        private:

            LV2UI_Widget* p_parent;

        };
    };

    /** The UIResize Mixin.
        @ingroup guimixins
        @see The internal struct I for details.
     */
    template<bool Required = true>
    struct UIResize
    {
        template<class Derived>
        struct I : Extension<Required>
        {
            I()
            {
                m_resize.handle    = NULL;
                m_resize.ui_resize = NULL;
            }

            /** @internal */
            static void
            map_feature_handlers(FeatureHandlerMap& hmap)
            {
                hmap[LV2_UI__resize] = &I<Derived>::handle_feature;
            }

            /** @internal */
            static void
            handle_feature(void* instance, void* data)
            {
                Derived* d = reinterpret_cast<Derived*>(instance);
                I<Derived>* mixin = static_cast<I<Derived>*>(d);

                LV2UI_Resize* rz (reinterpret_cast<LV2UI_Resize*> (data));
                mixin->m_resize.handle    = rz->handle;
                mixin->m_resize.ui_resize = rz->ui_resize;
                mixin->m_ok = true;
            }

            /** @skip */
            static const void*
            extension_data (const char* uri)
            {
                // TODO: Need to implement ui resize host-side.
                // FIXME: Need to provide a pointer, somehow, to extension_data
                return 0;
            }

            bool
            check_ok()
            {
                if (! Required) {
                   this->m_ok = true;
                } else {
                   this->m_ok = plugin_rsz_is_valid();
                }

                if (LVTK_DEBUG)
                {
                    std::clog << "    [UIResize] Validation "
                              << (this->m_ok ? "succeeded" : "failed")
                              << "." << std::endl;
                }
                return this->m_ok;
            }

        protected:

            /**
               Request a size change.
               Call this method to inform the host about the size of the UI.

               @return true on success.
             */
            bool
            ui_resize (int width, int height)
            {
                if (plugin_rsz_is_valid())
                   return (0 == m_resize.ui_resize (m_resize.handle, width, height));

                return false;
            }

        private:

            LV2UI_Resize m_resize;
            bool plugin_rsz_is_valid() const { return m_resize.ui_resize != NULL && m_resize.handle != NULL; }

        };
    };

}

#endif /* LVTK_UI_FEATURES_HPP */
<|endoftext|>


<commit_before>#include "bufferAgent.h"

using boost::shared_ptr;
using boost::thread;
using boost::bind;

using std::string;

namespace veil {
namespace helpers {

BufferAgent::BufferAgent(write_fun w, read_fun r)
  : m_agentActive(false),
    doWrite(w),
    doRead(r)
{
    agentStart();
}
BufferAgent::~BufferAgent()
{
    agentStop();
}

int BufferAgent::onOpen(std::string path, ffi_type ffi)
{
    unique_lock guard(m_loopMutex);
    m_cacheMap.erase(ffi->fh_old);

    buffer_ptr lCache(new LockableCache());
    lCache->fileName = path;
    lCache->buffer = newFileCache();
    lCache->ffi = *ffi;

    m_cacheMap[ffi->fh_old] = lCache;

    return 0;
}

int BufferAgent::onWrite(std::string path, const std::string &buf, size_t size, off_t offset, ffi_type ffi)
{
    unique_lock guard(m_loopMutex);
        buffer_ptr wrapper = m_cacheMap[ffi->fh_old];
    guard.unlock();

    unique_lock buff_guard(wrapper->mutex);

    while(wrapper->buffer->byteSize() > 1024 * 1024 * 10) {
        wrapper->cond.wait(buff_guard);
    }

    wrapper->buffer->writeData(offset, buf);

    guard.lock();
    m_jobQueue.push_back(ffi->fh_old);

    return EOPNOTSUPP;
}

int BufferAgent::onRead(std::string path, std::string &buf, size_t size, off_t offset, ffi_type ffi)
{
    boost::unique_lock<boost::recursive_mutex> guard(m_loopMutex);
    return EOPNOTSUPP;
}

int BufferAgent::onFlush(std::string path, ffi_type ffi)
{
    unique_lock guard(m_loopMutex);
        buffer_ptr wrapper = m_cacheMap[ffi->fh_old];
    guard.unlock();

    unique_lock buff_guard(wrapper->mutex);

    while(wrapper->buffer->blockCount() > 0)
    {
        block_ptr block = wrapper->buffer->removeOldestBlock();
        int res = doWrite(wrapper->fileName, block->data, block->data.size(), block->offset, &wrapper->ffi);
        if(res < 0)
        {
            while(wrapper->buffer->blockCount() > 0)
            {
                (void) wrapper->buffer->removeOldestBlock();
            }
            return res;
        }
    }

    guard.lock();
    m_jobQueue.remove(ffi->fh_old);

    return 0;
}

int BufferAgent::onRelease(std::string path, ffi_type ffi)
{
    boost::unique_lock<boost::recursive_mutex> guard(m_loopMutex);
    m_cacheMap.erase(ffi->fh_old);

    return 0;
}

void BufferAgent::agentStart(int worker_count)
{
    m_workers.clear();
    m_agentActive = true;

    while(worker_count--)
    {
        m_workers.push_back(shared_ptr<thread>(new thread(bind(&BufferAgent::workerLoop, this))));
    }
}

void BufferAgent::agentStop()
{
    m_agentActive = false;
    m_loopCond.notify_all();
    while(m_workers.size() > 0)
    {
        m_workers.back()->join();
        m_workers.pop_back();
    }
}

void BufferAgent::workerLoop()
{
    unique_lock guard(m_loopMutex);
    while(m_agentActive)
    {
        while(m_jobQueue.empty() && m_agentActive)
            m_loopCond.wait(guard);

        if(!m_agentActive)
            return;

        fd_type file = m_jobQueue.front();
        buffer_ptr wrapper = m_cacheMap[file];
        m_jobQueue.pop_front();
        m_loopCond.notify_all();

        if(!wrapper)
            continue;

        {
            guard.unlock();

            unique_lock buff_guard(wrapper->mutex);

            block_ptr block = wrapper->buffer->removeOldestBlock();
            int res = doWrite(wrapper->fileName, block->data, block->data.size(), block->offset, &wrapper->ffi);

            wrapper->cond.notify_all();

            guard.lock();
            if(wrapper->buffer->blockCount() > 0)
            {
                m_jobQueue.push_back(file);
            }
        }

    }
}

boost::shared_ptr<FileCache> BufferAgent::newFileCache()
{
    return boost::shared_ptr<FileCache>(new FileCache(100 * 1024));
}

} // namespace helpers
} // namespace veil

<commit_msg>VFS-292: debug<commit_after>#include "bufferAgent.h"

using boost::shared_ptr;
using boost::thread;
using boost::bind;

using std::string;

namespace veil {
namespace helpers {

BufferAgent::BufferAgent(write_fun w, read_fun r)
  : m_agentActive(false),
    doWrite(w),
    doRead(r)
{
    agentStart();
}
BufferAgent::~BufferAgent()
{
    agentStop();
}

int BufferAgent::onOpen(std::string path, ffi_type ffi)
{
    unique_lock guard(m_loopMutex);
    m_cacheMap.erase(ffi->fh_old);

    buffer_ptr lCache(new LockableCache());
    lCache->fileName = path;
    lCache->buffer = newFileCache();
    lCache->ffi = *ffi;

    m_cacheMap[ffi->fh_old] = lCache;

    return 0;
}

int BufferAgent::onWrite(std::string path, const std::string &buf, size_t size, off_t offset, ffi_type ffi)
{
    unique_lock guard(m_loopMutex);
        buffer_ptr wrapper = m_cacheMap[ffi->fh_old];
    guard.unlock();

    unique_lock buff_guard(wrapper->mutex);

    while(wrapper->buffer->byteSize() > 1024 * 1024 * 10) {
        wrapper->cond.wait(buff_guard);
    }

    wrapper->buffer->writeData(offset, buf);

    guard.lock();
    m_jobQueue.push_back(ffi->fh_old);

    return 0;
}

int BufferAgent::onRead(std::string path, std::string &buf, size_t size, off_t offset, ffi_type ffi)
{
    boost::unique_lock<boost::recursive_mutex> guard(m_loopMutex);
    return EOPNOTSUPP;
}

int BufferAgent::onFlush(std::string path, ffi_type ffi)
{
    unique_lock guard(m_loopMutex);
        buffer_ptr wrapper = m_cacheMap[ffi->fh_old];
    guard.unlock();

    unique_lock buff_guard(wrapper->mutex);

    while(wrapper->buffer->blockCount() > 0)
    {
        block_ptr block = wrapper->buffer->removeOldestBlock();
        int res = doWrite(wrapper->fileName, block->data, block->data.size(), block->offset, &wrapper->ffi);
        if(res < 0)
        {
            while(wrapper->buffer->blockCount() > 0)
            {
                (void) wrapper->buffer->removeOldestBlock();
            }
            return res;
        }
    }

    guard.lock();
    m_jobQueue.remove(ffi->fh_old);

    return 0;
}

int BufferAgent::onRelease(std::string path, ffi_type ffi)
{
    boost::unique_lock<boost::recursive_mutex> guard(m_loopMutex);
    m_cacheMap.erase(ffi->fh_old);

    return 0;
}

void BufferAgent::agentStart(int worker_count)
{
    m_workers.clear();
    m_agentActive = true;

    while(worker_count--)
    {
        m_workers.push_back(shared_ptr<thread>(new thread(bind(&BufferAgent::workerLoop, this))));
    }
}

void BufferAgent::agentStop()
{
    m_agentActive = false;
    m_loopCond.notify_all();
    while(m_workers.size() > 0)
    {
        m_workers.back()->join();
        m_workers.pop_back();
    }
}

void BufferAgent::workerLoop()
{
    unique_lock guard(m_loopMutex);
    while(m_agentActive)
    {
        while(m_jobQueue.empty() && m_agentActive)
            m_loopCond.wait(guard);

        if(!m_agentActive)
            return;

        fd_type file = m_jobQueue.front();
        buffer_ptr wrapper = m_cacheMap[file];
        m_jobQueue.pop_front();
        m_loopCond.notify_all();

        if(!wrapper)
            continue;

        {
            guard.unlock();

            unique_lock buff_guard(wrapper->mutex);

            block_ptr block = wrapper->buffer->removeOldestBlock();
            int res = doWrite(wrapper->fileName, block->data, block->data.size(), block->offset, &wrapper->ffi);

            wrapper->cond.notify_all();

            guard.lock();
            if(wrapper->buffer->blockCount() > 0)
            {
                m_jobQueue.push_back(file);
            }
        }

    }
}

boost::shared_ptr<FileCache> BufferAgent::newFileCache()
{
    return boost::shared_ptr<FileCache>(new FileCache(100 * 1024));
}

} // namespace helpers
} // namespace veil

<|endoftext|>


<commit_before>#include <stdint.h>
#include <bfd.h>
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <algorithm>

#include <vfs.h>

using std::cout;
using std::endl;
using std::string;
using std::vector;
using std::map;
using std::for_each;
using std::mem_fun_ref;
using std::bind1st;

struct Symbol
{
    string name;
    uint8_t type;
    uint64_t address;
    uint64_t addend;

    enum SymbolType
    {
	LOCAL = 0x01,
	GLOBAL = 0x02,

	UNRESOLVED = 0x04,

	FUNCTION = 0x08,
	VARIABLE = 0x10,

	RELATIVE = 0x20,
    };
};

struct Section
{
    string name;
    size_t vma_offset;
    size_t size;

    bfd_byte* data;
};

struct Object
{
    public:
	string name;
	bfd* image;
	Section text;
	Section data;
	map<string, Symbol> symbols;
	vector<Symbol> relocs;
	long offset;

	bool read_object(char* file);
	bool write_object(FILE* file);
	bool read_relocation();
	bool perform_local_relocations(FILE* file);
	bool perform_global_relocations(FILE* file);

	uint64_t find_init_symbol();
	uint64_t find_start_symbol();
};

vector<Object> objects;
FILE* output;

int main(int argc, char** argv)
{
    if (argc <= 2)
    {
	cout << argv[0] << " <output> <kernel> <modules>" << endl;
	return -1;
    }

    // Open output file.
    output = fopen(argv[1], "w+");
    if (NULL == output)
    {
	int error = errno;
	cout << "Error opening " << argv[1] << endl;
	cout << strerror(error) << endl;
    }

    // Read input objects.
    for (int files = 2; files < argc; files++)
    {
	Object o;
	if (o.read_object(argv[files]))
	{
	    o.read_relocation();
	    objects.push_back(o);
	    cout << endl;
	}
    }

    for_each(objects.begin(), objects.end(),
	     bind2nd(mem_fun_ref(&Object::write_object), output));
    uint64_t last_address = ftell(output);

    cout << "Local relocations..." << endl;
    for_each(objects.begin(), objects.end(),
	     bind2nd(mem_fun_ref(&Object::perform_local_relocations), output));
    cout << endl;

    cout << "Global relocations..." << endl;
    for_each(objects.begin(), objects.end(),
	     bind2nd(mem_fun_ref(&Object::perform_global_relocations), output));
    cout << endl;

    // Create module table.
    uint64_t module_table_address =
	    objects[0].symbols[VFS_TOSTRING(VFS_MODULES)].address +
	    objects[0].offset + objects[0].data.vma_offset;

    fseek(output, module_table_address, SEEK_SET);

    if ((objects.size()-1) > VFS_MODULE_MAX)
    {
	cout << "Error: Too many modules." << endl;
	return -1;
    }

    cout << "Updating module table... " << endl;
    for (vector<Object>::iterator i = ++objects.begin();
         i != objects.end();
	 ++i)
    {
	string object_name = i->name;
	object_name.erase(0, object_name.find_last_of("/")+1);

	char obj_name[VFS_MODULE_NAME_MAX];
	memset(obj_name, '\0', VFS_MODULE_NAME_MAX);
	strncpy(obj_name, object_name.c_str(), VFS_MODULE_NAME_MAX-1);
	fwrite(obj_name, VFS_MODULE_NAME_MAX, 1, output);

	uint64_t init_symbol = i->find_init_symbol();
	uint64_t start_symbol = i->find_start_symbol();

	char data[sizeof(uint64_t)];

	bfd_putb64(init_symbol, data);
	fwrite(data, sizeof(uint64_t), 1, output);
	bfd_putb64(start_symbol, data);
	fwrite(data, sizeof(uint64_t), 1, output);

	cout << "\tAdded module " << object_name << " with init at "
	     << std::hex << init_symbol << " and start at "
	     << start_symbol << endl;
    }

    cout << "Updating last address..." << std::hex;
    uint64_t last_address_entry_address =
	    objects[0].symbols[VFS_TOSTRING(VFS_LAST_ADDRESS)].address +
	    objects[0].offset + objects[0].data.vma_offset;

    fseek(output, last_address_entry_address, SEEK_SET);
    char last_addr_data[sizeof(uint64_t)];
    bfd_putb64(last_address, last_addr_data);
    fwrite(last_addr_data, sizeof(uint64_t), 1, output);

    cout << last_address << " to " << last_address_entry_address << endl;

    return 0;
}

bool Object::read_object(char* file)
{
    // Open BFD file.
    image = bfd_openr(file, NULL);
    if (!bfd_check_format(image, bfd_object))
    {
	cout << "Unsupported file format: " << file << endl;
	return false;
    }

    name = file;
    cout << "File " << file << endl;

    // Read sections.
    bfd_section* image_section = image->sections;
    while(image_section != NULL)
    {
	Section* s = NULL;
	if (string(".text") == bfd_get_section_name(image, image_section))
	{
	    s = &this->text;
	}
	if (string(".data") == bfd_get_section_name(image, image_section))
	{
	    s = &this->data;
	}
	if (NULL != s)
	{
	    s->name = 		bfd_get_section_name(image, image_section);
	    s->vma_offset = 	bfd_get_section_vma(image, image_section);
	    s->size = 		bfd_get_section_size(image_section);

	    bfd_malloc_and_get_section(image, image_section, &s->data);

	    cout << "Section " << s->name << endl;
	    cout << "\tSize " << std::dec << s->size << endl;
	    cout << "\tVMA " << std::hex << s->vma_offset << endl;
	    cout << "\tData " << std::hex << bfd_getb64(s->data)
	         << "..." << endl;
	}

	image_section = image_section->next;
    }

    return true;
}

#define advance_to_page_align(f) \
    { \
	long pos = ftell(f); \
	if (pos % 4096) \
	{ \
	    fseek((f), 4096 - (pos % 4096), SEEK_CUR); \
	} \
    }

bool Object::write_object(FILE* file)
{
    // Start outputing object at page boundary.
    advance_to_page_align(file);
    offset = ftell(file);

    // Output TEXT section.
    fseek(file, text.vma_offset, SEEK_CUR);
    if (text.size != fwrite(text.data, 1, text.size, file))
    {
	int error = errno;
	cout << "Error writing to output." << endl;
	cout << strerror(error) << endl;
    }

    // Output DATA section.
    fseek(file, offset + data.vma_offset, SEEK_SET);
    if (data.size != fwrite(data.data, 1, data.size, file))
    {
	int error = errno;
	cout << "Error writing to output." << endl;
	cout << strerror(error) << endl;
    }
}

bool Object::read_relocation()
{
    asymbol** syms = NULL;
    long symbols = 0;
    arelent** loc = NULL;
    long locs = 0;

    // Read symbol tables.
    {
	long symbol_size = bfd_get_dynamic_symtab_upper_bound(image);
	if (0 < symbol_size)
	{
	    syms = (asymbol**) malloc(symbol_size);
	    symbols = bfd_canonicalize_dynamic_symtab(image, syms);
	}
    }
    if (0 >= symbols)
    {
	long symbol_size = bfd_get_symtab_upper_bound(image);
	if (0 < symbol_size)
	{
	    syms = (asymbol**) malloc(symbol_size);
	    symbols = bfd_canonicalize_symtab(image, syms);
	}
    }
    if (0 >= symbols)
    {
	cout << "Couldn't find symbol table." << endl;
	return false;
    }
    cout << "Symbols: " << std::dec << symbols << endl;
    for (int i = 0; i < symbols; i++)
    {
	Symbol s;
	s.name = syms[i]->name;
	s.address = syms[i]->value;
	s.type = 0;

	cout << "\tSymbol: " << syms[i]->name << endl;
	cout << "\t\tAddress: " << std::hex << syms[i]->value << endl;

	// Determine symbol types.
	if (syms[i]->flags & BSF_GLOBAL)
	{
	    s.type |= Symbol::GLOBAL;
	    cout << "\t\tGLOBAL" << endl;
	}
	else if (syms[i]->flags & (BSF_LOCAL | BSF_WEAK))
	{
	    s.type |= Symbol::LOCAL;
	    cout << "\t\tLOCAL" << endl;
	}
	else
	{
	    s.type |= Symbol::UNRESOLVED;
	    cout << "\t\tUNDEFINED " << std::hex << syms[i]->flags << endl;
	}

	if (syms[i]->flags & BSF_FUNCTION)
	{
	    s.type |= Symbol::FUNCTION;
	    cout << "\t\tFUNCTION" << endl;
	}
	else if (!(s.type & Symbol::UNRESOLVED))
	{
	    s.type |= Symbol::VARIABLE;
	    cout << "\t\tVARIABLE" << endl;
	}

	// Add symbol to table.
	if (!(s.type & Symbol::UNRESOLVED))
	    this->symbols[s.name] = s;
    }

    // Read relocations.
    {
	long loc_size = bfd_get_dynamic_reloc_upper_bound(image);
	if (0 < loc_size)
	{
	    loc = (arelent**) malloc(loc_size);
	    memset(loc, '\0', loc_size);
	    locs = bfd_canonicalize_dynamic_reloc(image, loc, syms);
	}
    }
    if (0 >= locs)
    {
	goto cleanup;
    }

    cout << "Relocs: " << std::dec << locs << endl;
    for (int i = 0; i < locs; i++)
    {
	Symbol s;

	s.name = loc[i]->sym_ptr_ptr[0]->name;
	s.address = loc[i]->address;
	s.addend = loc[i]->addend;
	if ((s.name == BFD_ABS_SECTION_NAME) ||
	    (this->symbols.find(s.name) != this->symbols.end()))
	{
	    s.type = Symbol::RELATIVE;
	}
	else
	{
	    s.type = Symbol::UNRESOLVED;
	}
	this->relocs.push_back(s);

	cout << "\tSymbol: " << loc[i]->sym_ptr_ptr[0]->name;
	cout << "\tAddress: " << std::hex << loc[i]->address << ", "
	     << loc[i]->addend << endl;
    }

cleanup:
    if (NULL != loc)
	free(loc);
    if (NULL != syms)
    	free(syms);

    return true;
}

bool Object::perform_local_relocations(FILE* file)
{
    cout << "File " << name << endl;

    for(vector<Symbol>::iterator i = relocs.begin();
        i != relocs.end();
	++i)
    {
	if (i->type & Symbol::UNRESOLVED)
	    continue;

	cout << "\tSymbol: " << i->name << endl;

	uint64_t relocation = i->addend + offset;
	uint64_t address = 0;
	char data[sizeof(uint64_t)];

	fseek(file, offset + i->address, SEEK_SET);
	fread(data, sizeof(uint64_t), 1, file);

	address = bfd_getb64(data);
	if (address != i->addend)
	{
	    cout << "Expected " << i->addend << " found " << address << endl;
	    continue;
	}

	address = relocation;
	bfd_putb64(address, data);

	fseek(file, offset + i->address, SEEK_SET);
	fwrite(data, sizeof(uint64_t), 1, file);

	cout << "\tRelocated " << i->addend << " to "
	     << relocation << endl;
    }
}

bool Object::perform_global_relocations(FILE* file)
{
    cout << "File " << name << endl;

    for(vector<Symbol>::iterator i = relocs.begin();
        i != relocs.end();
	++i)
    {
	if (!(i->type & Symbol::UNRESOLVED))
	    continue;

	cout << "\tSymbol: " << i->name << endl;

	char data[sizeof(uint64_t)*3];

	for(vector<Object>::iterator j = objects.begin();
	    j != objects.end();
	    ++j)
	{
	    if (j->symbols.find(i->name) != j->symbols.end())
	    {
		Symbol s = j->symbols[i->name];
		uint64_t symbol_addr =
			j->offset + s.address + j->data.vma_offset;

		if (s.type & Symbol::UNRESOLVED)
		    continue;
		if (s.type & Symbol::LOCAL)
		    continue;
		if (!(s.type & Symbol::GLOBAL))
		    continue;

		if (s.type & Symbol::FUNCTION)
		{
		    fseek(file, symbol_addr, SEEK_SET);
		    fread(data, sizeof(uint64_t), 3, file);

		    fseek(file, offset + i->address, SEEK_SET);
		    fwrite(data, sizeof(uint64_t), 3, file);

		    cout << "\tCopied relocation from " << std::hex
			<< symbol_addr << " to "
			<< offset + i->address << "." << endl;
		}
		else
		{
		    bfd_putb64(symbol_addr, data);
		    fseek(file, offset + i->address, SEEK_SET);
		    fwrite(data, sizeof(uint64_t), 1, file);

		    cout << "\tRelocated from " << std::hex
			 << symbol_addr << " to "
			 << offset + i->address << "." << endl;
		}
		break;
	    }
	}
    }
}

uint64_t Object::find_init_symbol()
{
    if (symbols.find(VFS_TOSTRING(VFS_SYMBOL_INIT)) == symbols.end())
	return 0;

    return symbols[VFS_TOSTRING(VFS_SYMBOL_INIT)].address +
	   offset + data.vma_offset;
}

uint64_t Object::find_start_symbol()
{
    if (symbols.find(VFS_TOSTRING(VFS_SYMBOL_START)) == symbols.end())
	return 0;

    return symbols[VFS_TOSTRING(VFS_SYMBOL_START)].address +
	   offset + data.vma_offset;
}
<commit_msg>Missed deleting original linker when moving.<commit_after><|endoftext|>


<commit_before>#include "callwindow.h"
#include "ui_callwindow.h"

#include "ui_callingwidget.h"
#include "ui_about.h"

#include "statisticswindow.h"

#include <QCloseEvent>
#include <QHostAddress>
#include <QtEndian>
#include <QTimer>

const uint16_t MAXOPENPORTS = 42;
const uint16_t PORTSPERPARTICIPANT = 4;

CallWindow::CallWindow(QWidget *parent):
  QMainWindow(parent),
  ui_(new Ui::CallWindow),
  settingsView_(),
  stats_(new StatisticsWindow(this)),
  conference_(this),
  callNeg_(),
  media_(stats_),
  timer_(new QTimer(this)),
  portsOpen_(0)
{
  ui_->setupUi(this);

  // GUI updates are handled solely by timer
  timer_->setInterval(30);
  timer_->setSingleShot(false);
  connect(timer_, SIGNAL(timeout()), ui_->participants, SLOT(update()));
  connect(timer_, SIGNAL(timeout()), ui_->SelfView, SLOT(update()));
  connect(timer_, SIGNAL(timeout()), stats_, SLOT(update()));
  timer_->start();

  setWindowTitle("HEVC Conferencing");
}

CallWindow::~CallWindow()
{
  timer_->stop();
  delete timer_;
  stats_->close();
  delete stats_;
  delete ui_;
}

void CallWindow::addContact()
{
  contacts_.addContact(this, ui_->peerName->text(), "anonymous", ui_->ip->text());
}

void CallWindow::startStream()
{
  contacts_.initializeList(ui_->contactList, this);

  Ui::CallerWidget *ui_widget = new Ui::CallerWidget;
  QWidget* holderWidget = new QWidget;
  ui_widget->setupUi(holderWidget);
  connect(ui_widget->AcceptButton, SIGNAL(clicked()), this, SLOT(acceptCall()));
  connect(ui_widget->DeclineButton, SIGNAL(clicked()), this, SLOT(rejectCall()));

  aboutWidget_ = new Ui::AboutWidget;
  aboutWidget_->setupUi(&about_);

  QObject::connect(&callNeg_, SIGNAL(incomingINVITE(QString, QString)),
                   this, SLOT(incomingCall(QString, QString)));

  QObject::connect(&callNeg_, SIGNAL(callingOurselves(QString, std::shared_ptr<SDPMessageInfo>)),
                   this, SLOT(callOurselves(QString, std::shared_ptr<SDPMessageInfo>)));

  QObject::connect(&callNeg_, SIGNAL(callNegotiated(QString, std::shared_ptr<SDPMessageInfo>,
                                                              std::shared_ptr<SDPMessageInfo>)),
                   this, SLOT(callNegotiated(QString, std::shared_ptr<SDPMessageInfo>,
                                                      std::shared_ptr<SDPMessageInfo>)));

  QObject::connect(&callNeg_, SIGNAL(ringing(QString)),
                   this, SLOT(ringing(QString)));

  QObject::connect(&callNeg_, SIGNAL(ourCallAccepted(QString, std::shared_ptr<SDPMessageInfo>,
                                                               std::shared_ptr<SDPMessageInfo>)),
                   this, SLOT(callNegotiated(QString, std::shared_ptr<SDPMessageInfo>,
                                                      std::shared_ptr<SDPMessageInfo>)));

  QObject::connect(&callNeg_, SIGNAL(ourCallRejected(QString)),
                   this, SLOT(ourCallRejected(QString)));

  QObject::connect(&callNeg_, SIGNAL(callEnded(QString, QString)),
                   this, SLOT(endCall(QString, QString)));

  QObject::connect(&settingsView_, SIGNAL(settingsChanged()),
                   this, SLOT(recordChangedSettings()));
  conferenceMutex_.lock();
  conference_.init(ui_->participantLayout, ui_->participants, ui_widget, holderWidget);
  conferenceMutex_.unlock();

  // TODO move these closer to useagepoint
  QSettings settings;
  QString localName = settings.value("local/Name").toString();
  QString localUsername = settings.value("local/Username").toString();

  callNeg_.init(localName, localUsername);

  media_.init();
  media_.startCall(ui_->SelfView);
}

void CallWindow::recordChangedSettings()
{
  // TODO call update settings on everything?
  media_.updateSettings();
}

void CallWindow::callToParticipant(QString name, QString username, QString ip)
{
  qDebug() << "User wants to add participant. Ports required:"
           << portsOpen_ + PORTSPERPARTICIPANT << "/" << MAXOPENPORTS;
  if(portsOpen_ <= MAXOPENPORTS - PORTSPERPARTICIPANT)
  {
    QString ip_str = ip;

    Contact con;
    con.contactAddress = ip_str;
    con.name = "Anonymous";
    con.username = "unknown";

    QList<Contact> list;
    list.append(con);

    //start negotiations for this connection

    QList<QString> callIDs = callNeg_.startCall(list);

    for(auto callID : callIDs)
    {
      conferenceMutex_.lock();
      conference_.callingTo(callID, "Contact List Missing!"); // TODO get name from contact list
      conferenceMutex_.unlock();
    }
  }
}

void CallWindow::chatWithParticipant(QString name, QString username, QString ip)
{
  qDebug("Chat not implemented yet");
}

void CallWindow::createParticipant(QString& callID, std::shared_ptr<SDPMessageInfo> peerInfo,
                                   const std::shared_ptr<SDPMessageInfo> localInfo)
{
  qDebug() << "Adding participant to conference.";

  portsOpen_ +=PORTSPERPARTICIPANT;

  QHostAddress address;
  address.setAddress(peerInfo->globalAddress);

  in_addr ip;
  ip.S_un.S_addr = qToBigEndian(address.toIPv4Address());

  conferenceMutex_.lock();
  VideoWidget* view = conference_.addVideoStream(callID);
  conferenceMutex_.unlock();

  if(view == NULL)
  {
    qWarning() << "WARNING: NULL widget got from";
    return;
  }

  // TODO: have these be arrays and send video to all of them in case SDP describes it so
  uint16_t sendAudioPort = 0;
  uint16_t recvAudioPort = 0;
  uint16_t sendVideoPort = 0;
  uint16_t recvVideoPort = 0;

  for(auto media : localInfo->media)
  {
    if(media.type == "audio" && recvAudioPort == 0)
    {
      recvAudioPort = media.port;
    }
    else if(media.type == "video" && recvVideoPort == 0)
    {
      recvVideoPort = media.port;
    }
  }

  for(auto media : peerInfo->media)
  {
    if(media.type == "audio" && sendAudioPort == 0)
    {
      sendAudioPort = media.port;
    }
    else if(media.type == "video" && sendVideoPort == 0)
    {
      sendVideoPort = media.port;
    }
  }

  // TODO send all the media if more than one are specified and if required to more than one participant.
  if(sendAudioPort == 0 || recvAudioPort == 0 || sendVideoPort == 0 || recvVideoPort == 0)
  {
    qWarning() << "WARNING: All media ports were not found in given SDP. SendAudio:" << sendAudioPort
               << "recvAudio:" << recvAudioPort << "SendVideo:" << sendVideoPort << "RecvVideo:" << recvVideoPort;
    return;
  }

  if(stats_)
    stats_->addParticipant(peerInfo->globalAddress,
                           QString::number(sendAudioPort),
                           QString::number(sendVideoPort));

  qDebug() << "Sending mediastreams to:" << peerInfo->globalAddress << "audioPort:" << sendAudioPort
           << "VideoPort:" << sendVideoPort;
  media_.addParticipant(callID, ip, sendAudioPort, recvAudioPort, sendVideoPort, recvVideoPort, view);
}

void CallWindow::openStatistics()
{
  stats_->show();
}

void CallWindow::micState()
{
  bool state = media_.toggleMic();

  if(state)
  {
    ui_->mic->setText("Turn mic off");
  }
  else
  {
    ui_->mic->setText("Turn mic on");
  }
}

void CallWindow::cameraState()
{
  bool state = media_.toggleCamera();
  if(state)
  {
    ui_->camera->setText("Turn camera off");
  }
  else
  {
    ui_->camera->setText("Turn camera on");
  }
}

void CallWindow::closeEvent(QCloseEvent *event)
{
  endAllCalls();

  callNeg_.uninit();
  media_.uninit();

  stats_->hide();
  stats_->finished(0);

  QMainWindow::closeEvent(event);
}

void CallWindow::incomingCall(QString callID, QString caller)
{
  if(portsOpen_ > MAXOPENPORTS - PORTSPERPARTICIPANT)
  {
    qWarning() << "WARNING: Ran out of ports:" << portsOpen_ << "/" << MAXOPENPORTS;
    //rejectCall(); // TODO: send a not possible message instead of reject.
    return;
  }

  conferenceMutex_.lock();
  conference_.incomingCall(callID, caller);
  conferenceMutex_.unlock();
}

void CallWindow::callOurselves(QString callID, std::shared_ptr<SDPMessageInfo> info)
{
  if(portsOpen_ <= MAXOPENPORTS - PORTSPERPARTICIPANT)
  {
    qDebug() << "Calling ourselves, how boring.";
    createParticipant(callID, info, info);
  }
}

void CallWindow::acceptCall()
{
  qDebug() << "We accepted";
  conferenceMutex_.lock();
  QString callID = conference_.acceptNewest();
  conferenceMutex_.unlock();
  callNeg_.acceptCall(callID);
}

void CallWindow::rejectCall()
{
  qDebug() << "We rejected";
  conferenceMutex_.lock();
  QString callID = conference_.rejectNewest();
  conferenceMutex_.unlock();
  callNeg_.rejectCall(callID);
}

void CallWindow::ringing(QString callID)
{
  qDebug() << "call is ringing. TODO: display it to user";
}

void CallWindow::ourCallRejected(QString callID)
{
  qDebug() << "Our Call was rejected. TODO: display it to user";
  conferenceMutex_.lock();
  conference_.removeCaller(callID);
  conferenceMutex_.unlock();
}

void CallWindow::callNegotiated(QString callID, std::shared_ptr<SDPMessageInfo> peerInfo,
                                std::shared_ptr<SDPMessageInfo> localInfo)
{
  qDebug() << "Our call has been accepted.";

  qDebug() << "Sending media to IP:" << peerInfo->globalAddress
           << "to port:" << peerInfo->media.first().port;

  // TODO check the SDP info and do ports and rtp numbers properly
  createParticipant(callID, peerInfo, localInfo);
}

void CallWindow::endCall(QString callID, QString ip)
{
  qDebug() << "Received the end of call message";

  // hack to go around the situation where we are ending our own call
  if(portsOpen_ != 0)
  {
    media_.removeParticipant(callID); // must be ended first because of the view.

    conferenceMutex_.lock();
    conference_.removeCaller(callID);
    conferenceMutex_.unlock();

    stats_->removeParticipant(ip);
    portsOpen_ -= PORTSPERPARTICIPANT;
  }
}

void CallWindow::on_settings_clicked()
{
  settingsView_.showBasicSettings();
}
void CallWindow::on_advanced_settings_clicked()
{
  settingsView_.showAdvancedSettings();
}

void CallWindow::on_about_clicked()
{
  about_.show();
}

void CallWindow::endAllCalls()
{
  callNeg_.endAllCalls();

  media_.endAllCalls();
  conferenceMutex_.lock();
  conference_.close();
  conferenceMutex_.unlock();
  portsOpen_ = 0;
}
<commit_msg>Fix to some weird error about not having refistered qvector<int>. First appeared earlier.<commit_after>#include "callwindow.h"
#include "ui_callwindow.h"

#include "ui_callingwidget.h"
#include "ui_about.h"

#include "statisticswindow.h"

#include <QCloseEvent>
#include <QHostAddress>
#include <QtEndian>
#include <QTimer>
#include <QMetaType>

const uint16_t MAXOPENPORTS = 42;
const uint16_t PORTSPERPARTICIPANT = 4;

CallWindow::CallWindow(QWidget *parent):
  QMainWindow(parent),
  ui_(new Ui::CallWindow),
  settingsView_(),
  stats_(new StatisticsWindow(this)),
  conference_(this),
  callNeg_(),
  media_(stats_),
  timer_(new QTimer(this)),
  portsOpen_(0)
{
  ui_->setupUi(this);

  // GUI updates are handled solely by timer
  timer_->setInterval(30);
  timer_->setSingleShot(false);
  connect(timer_, SIGNAL(timeout()), ui_->participants, SLOT(update()));
  connect(timer_, SIGNAL(timeout()), ui_->SelfView, SLOT(update()));
  connect(timer_, SIGNAL(timeout()), stats_, SLOT(update()));
  timer_->start();

  setWindowTitle("HEVC Conferencing");
}

CallWindow::~CallWindow()
{
  timer_->stop();
  delete timer_;
  stats_->close();
  delete stats_;
  delete ui_;
}

void CallWindow::addContact()
{
  contacts_.addContact(this, ui_->peerName->text(), "anonymous", ui_->ip->text());
}

void CallWindow::startStream()
{
  contacts_.initializeList(ui_->contactList, this);

  Ui::CallerWidget *ui_widget = new Ui::CallerWidget;
  QWidget* holderWidget = new QWidget;
  ui_widget->setupUi(holderWidget);
  connect(ui_widget->AcceptButton, SIGNAL(clicked()), this, SLOT(acceptCall()));
  connect(ui_widget->DeclineButton, SIGNAL(clicked()), this, SLOT(rejectCall()));

  aboutWidget_ = new Ui::AboutWidget;
  aboutWidget_->setupUi(&about_);

  // TODO: I don't know why this is required.
  qRegisterMetaType<QVector<int> >("QVector<int>");

  QObject::connect(&callNeg_, SIGNAL(incomingINVITE(QString, QString)),
                   this, SLOT(incomingCall(QString, QString)));

  QObject::connect(&callNeg_, SIGNAL(callingOurselves(QString, std::shared_ptr<SDPMessageInfo>)),
                   this, SLOT(callOurselves(QString, std::shared_ptr<SDPMessageInfo>)));

  QObject::connect(&callNeg_, SIGNAL(callNegotiated(QString, std::shared_ptr<SDPMessageInfo>,
                                                              std::shared_ptr<SDPMessageInfo>)),
                   this, SLOT(callNegotiated(QString, std::shared_ptr<SDPMessageInfo>,
                                                      std::shared_ptr<SDPMessageInfo>)));

  QObject::connect(&callNeg_, SIGNAL(ringing(QString)),
                   this, SLOT(ringing(QString)));

  QObject::connect(&callNeg_, SIGNAL(ourCallAccepted(QString, std::shared_ptr<SDPMessageInfo>,
                                                               std::shared_ptr<SDPMessageInfo>)),
                   this, SLOT(callNegotiated(QString, std::shared_ptr<SDPMessageInfo>,
                                                      std::shared_ptr<SDPMessageInfo>)));

  QObject::connect(&callNeg_, SIGNAL(ourCallRejected(QString)),
                   this, SLOT(ourCallRejected(QString)));

  QObject::connect(&callNeg_, SIGNAL(callEnded(QString, QString)),
                   this, SLOT(endCall(QString, QString)));

  QObject::connect(&settingsView_, SIGNAL(settingsChanged()),
                   this, SLOT(recordChangedSettings()));
  conferenceMutex_.lock();
  conference_.init(ui_->participantLayout, ui_->participants, ui_widget, holderWidget);
  conferenceMutex_.unlock();

  // TODO move these closer to useagepoint
  QSettings settings;
  QString localName = settings.value("local/Name").toString();
  QString localUsername = settings.value("local/Username").toString();

  callNeg_.init(localName, localUsername);

  media_.init();
  media_.startCall(ui_->SelfView);
}

void CallWindow::recordChangedSettings()
{
  // TODO call update settings on everything?
  media_.updateSettings();
}

void CallWindow::callToParticipant(QString name, QString username, QString ip)
{
  qDebug() << "User wants to add participant. Ports required:"
           << portsOpen_ + PORTSPERPARTICIPANT << "/" << MAXOPENPORTS;
  if(portsOpen_ <= MAXOPENPORTS - PORTSPERPARTICIPANT)
  {
    QString ip_str = ip;

    Contact con;
    con.contactAddress = ip_str;
    con.name = "Anonymous";
    con.username = "unknown";

    QList<Contact> list;
    list.append(con);

    //start negotiations for this connection

    QList<QString> callIDs = callNeg_.startCall(list);

    for(auto callID : callIDs)
    {
      conferenceMutex_.lock();
      conference_.callingTo(callID, "Contact List Missing!"); // TODO get name from contact list
      conferenceMutex_.unlock();
    }
  }
}

void CallWindow::chatWithParticipant(QString name, QString username, QString ip)
{
  qDebug("Chat not implemented yet");
}

void CallWindow::createParticipant(QString& callID, std::shared_ptr<SDPMessageInfo> peerInfo,
                                   const std::shared_ptr<SDPMessageInfo> localInfo)
{
  qDebug() << "Adding participant to conference.";

  portsOpen_ +=PORTSPERPARTICIPANT;

  QHostAddress address;
  address.setAddress(peerInfo->globalAddress);

  in_addr ip;
  ip.S_un.S_addr = qToBigEndian(address.toIPv4Address());

  conferenceMutex_.lock();
  VideoWidget* view = conference_.addVideoStream(callID);
  conferenceMutex_.unlock();

  if(view == NULL)
  {
    qWarning() << "WARNING: NULL widget got from";
    return;
  }

  // TODO: have these be arrays and send video to all of them in case SDP describes it so
  uint16_t sendAudioPort = 0;
  uint16_t recvAudioPort = 0;
  uint16_t sendVideoPort = 0;
  uint16_t recvVideoPort = 0;

  for(auto media : localInfo->media)
  {
    if(media.type == "audio" && recvAudioPort == 0)
    {
      recvAudioPort = media.port;
    }
    else if(media.type == "video" && recvVideoPort == 0)
    {
      recvVideoPort = media.port;
    }
  }

  for(auto media : peerInfo->media)
  {
    if(media.type == "audio" && sendAudioPort == 0)
    {
      sendAudioPort = media.port;
    }
    else if(media.type == "video" && sendVideoPort == 0)
    {
      sendVideoPort = media.port;
    }
  }

  // TODO send all the media if more than one are specified and if required to more than one participant.
  if(sendAudioPort == 0 || recvAudioPort == 0 || sendVideoPort == 0 || recvVideoPort == 0)
  {
    qWarning() << "WARNING: All media ports were not found in given SDP. SendAudio:" << sendAudioPort
               << "recvAudio:" << recvAudioPort << "SendVideo:" << sendVideoPort << "RecvVideo:" << recvVideoPort;
    return;
  }

  if(stats_)
    stats_->addParticipant(peerInfo->globalAddress,
                           QString::number(sendAudioPort),
                           QString::number(sendVideoPort));

  qDebug() << "Sending mediastreams to:" << peerInfo->globalAddress << "audioPort:" << sendAudioPort
           << "VideoPort:" << sendVideoPort;
  media_.addParticipant(callID, ip, sendAudioPort, recvAudioPort, sendVideoPort, recvVideoPort, view);
}

void CallWindow::openStatistics()
{
  stats_->show();
}

void CallWindow::micState()
{
  bool state = media_.toggleMic();

  if(state)
  {
    ui_->mic->setText("Turn mic off");
  }
  else
  {
    ui_->mic->setText("Turn mic on");
  }
}

void CallWindow::cameraState()
{
  bool state = media_.toggleCamera();
  if(state)
  {
    ui_->camera->setText("Turn camera off");
  }
  else
  {
    ui_->camera->setText("Turn camera on");
  }
}

void CallWindow::closeEvent(QCloseEvent *event)
{
  endAllCalls();

  callNeg_.uninit();
  media_.uninit();

  stats_->hide();
  stats_->finished(0);

  QMainWindow::closeEvent(event);
}

void CallWindow::incomingCall(QString callID, QString caller)
{
  if(portsOpen_ > MAXOPENPORTS - PORTSPERPARTICIPANT)
  {
    qWarning() << "WARNING: Ran out of ports:" << portsOpen_ << "/" << MAXOPENPORTS;
    //rejectCall(); // TODO: send a not possible message instead of reject.
    return;
  }

  conferenceMutex_.lock();
  conference_.incomingCall(callID, caller);
  conferenceMutex_.unlock();
}

void CallWindow::callOurselves(QString callID, std::shared_ptr<SDPMessageInfo> info)
{
  if(portsOpen_ <= MAXOPENPORTS - PORTSPERPARTICIPANT)
  {
    qDebug() << "Calling ourselves, how boring.";
    createParticipant(callID, info, info);
  }
}

void CallWindow::acceptCall()
{
  qDebug() << "We accepted";
  conferenceMutex_.lock();
  QString callID = conference_.acceptNewest();
  conferenceMutex_.unlock();
  callNeg_.acceptCall(callID);
}

void CallWindow::rejectCall()
{
  qDebug() << "We rejected";
  conferenceMutex_.lock();
  QString callID = conference_.rejectNewest();
  conferenceMutex_.unlock();
  callNeg_.rejectCall(callID);
}

void CallWindow::ringing(QString callID)
{
  qDebug() << "call is ringing. TODO: display it to user";
}

void CallWindow::ourCallRejected(QString callID)
{
  qDebug() << "Our Call was rejected. TODO: display it to user";
  conferenceMutex_.lock();
  conference_.removeCaller(callID);
  conferenceMutex_.unlock();
}

void CallWindow::callNegotiated(QString callID, std::shared_ptr<SDPMessageInfo> peerInfo,
                                std::shared_ptr<SDPMessageInfo> localInfo)
{
  qDebug() << "Our call has been accepted.";

  qDebug() << "Sending media to IP:" << peerInfo->globalAddress
           << "to port:" << peerInfo->media.first().port;

  // TODO check the SDP info and do ports and rtp numbers properly
  createParticipant(callID, peerInfo, localInfo);
}

void CallWindow::endCall(QString callID, QString ip)
{
  qDebug() << "Received the end of call message";

  // hack to go around the situation where we are ending our own call
  if(portsOpen_ != 0)
  {
    media_.removeParticipant(callID); // must be ended first because of the view.

    conferenceMutex_.lock();
    conference_.removeCaller(callID);
    conferenceMutex_.unlock();

    stats_->removeParticipant(ip);
    portsOpen_ -= PORTSPERPARTICIPANT;
  }
}

void CallWindow::on_settings_clicked()
{
  settingsView_.showBasicSettings();
}
void CallWindow::on_advanced_settings_clicked()
{
  settingsView_.showAdvancedSettings();
}

void CallWindow::on_about_clicked()
{
  about_.show();
}

void CallWindow::endAllCalls()
{
  callNeg_.endAllCalls();

  media_.endAllCalls();
  conferenceMutex_.lock();
  conference_.close();
  conferenceMutex_.unlock();
  portsOpen_ = 0;
}
<|endoftext|>


<commit_before>// Copyright (c) 2010-2014, The Video Segmentation Project
// All rights reserved.

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the The Video Segmentation Project nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// ---

#include "base/base_impl.h"

#include "segment_util/segmentation_io.h"
#include "segment_util/segmentation_util.h"
#include "segment_util/segmentation.pb.h"

namespace segmentation {

namespace {
  template <class T> const char* ToConstCharPtr(const T* t) {
    return reinterpret_cast<const char*>(t);
  }
  template <class T> char* ToCharPtr(T* t) {
    return reinterpret_cast<char*>(t);
  }
}

bool SegmentationWriter::OpenFile(const vector<int>& header_entries) {
  header_entries_ = header_entries;

  // Open file to write
  VLOG(1) << "Writing segmentation to file " << filename_;
  ofs_.open(filename_.c_str(),
            std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);

  if (!ofs_) {
    LOG(ERROR) << "Could not open " << filename_ << " to write!\n";
    return false;
  }

  num_chunks_ = 0;
  curr_offset_ = 0;

  // Write header information.
  ofs_.write("HEAD", 4);
  const int32_t num_entries = header_entries_.size();
  ofs_.write(ToConstCharPtr(&num_entries), sizeof(num_entries));
  for (int i = 0; i < num_entries; ++i) {
    ofs_.write(ToConstCharPtr(&header_entries_[i]), sizeof(header_entries_[i]));
  }
  curr_offset_ = 4 + 4 + num_entries * 4;

  return true;
}

void SegmentationWriter::AddSegmentationToChunk(const SegmentationDesc& desc,
                                                int64_t pts) {
  std::string data;
  desc.SerializeToString(&data);
  AddSegmentationDataToChunk(data, pts);
}

void SegmentationWriter::AddSegmentationDataToChunk(const std::string& data,
                                                    int64_t pts) {
  // Buffer for later.
  file_offsets_.push_back(curr_offset_);
  chunk_buffer_.push_back(data);

  curr_offset_ += data.size() + 4 + sizeof(int32_t);    // SEG_FRAME total size.
  time_stamps_.push_back(pts);
}

void SegmentationWriter::WriteChunk() {
  const int32_t num_frames = file_offsets_.size();
  const int32_t chunk_id = num_chunks_++;

  CHECK_EQ(file_offsets_.size(), chunk_buffer_.size());
  CHECK_EQ(file_offsets_.size(), time_stamps_.size());

  ofs_.write("CHNK", 4);
  ofs_.write(ToConstCharPtr(&chunk_id), sizeof(chunk_id));
  ofs_.write(ToConstCharPtr(&num_frames), sizeof(num_frames));

  int64_t size_of_header =           // HEADER total size.
    4 +
    2 * sizeof(int32_t) +
    num_frames * 2 * sizeof(int64_t) +
    sizeof(int64_t);

  // Advance offsets by size of header.
  curr_offset_ += size_of_header;
  for (size_t i = 0; i < file_offsets_.size(); ++i) {
    file_offsets_[i] += size_of_header;
  }

  // Write offsets and pts.
  for (size_t i = 0; i < file_offsets_.size(); ++i) {
    ofs_.write(ToConstCharPtr(&file_offsets_[i]), sizeof(file_offsets_[i]));
  }

  CHECK_EQ(file_offsets_.size(), time_stamps_.size());
  for (size_t i = 0; i < time_stamps_.size(); ++i) {
    ofs_.write(ToConstCharPtr(&time_stamps_[i]), sizeof(time_stamps_[i]));
  }

  // Write offset of next header.
  ofs_.write(ToConstCharPtr(&curr_offset_), sizeof(curr_offset_));

  // Write frames.
  CHECK_EQ(file_offsets_.size(), chunk_buffer_.size());
  for (size_t i = 0; i < chunk_buffer_.size(); ++i) {
    ofs_.write("SEGD", 4);
    int32_t frame_size = chunk_buffer_[i].length();
    ofs_.write(ToConstCharPtr(&frame_size), sizeof(frame_size));
    ofs_.write(ToConstCharPtr(&chunk_buffer_[i][0]), frame_size);
  }

  total_frames_ += chunk_buffer_.size();

  // Clear chunk information.
  chunk_buffer_.clear();
  file_offsets_.clear();
  time_stamps_.clear();
}

void SegmentationWriter::WriteTermHeaderAndClose() {
   if (!chunk_buffer_.empty()) {
     WriteChunk();
   }

   ofs_.write("TERM", 4);
   ofs_.write(ToConstCharPtr(&num_chunks_), sizeof(num_chunks_));
   ofs_.close();

   LOG(INFO) << "Wrote a total of " << total_frames_ << " frames.";
}

void SegmentationWriter::FlushAndReopen(const std::string& filename) {
  if (!chunk_buffer_.empty()) {
    WriteChunk();
  }

  WriteTermHeaderAndClose();
  filename_ = filename;
  curr_offset_ = 0;
  num_chunks_ = 0;
  OpenFile();
}

bool SegmentationReader::OpenFileAndReadHeaders() {
  // Open file.
  VLOG(1) << "Reading segmentation from file " << filename_;
  ifs_.open(filename_.c_str(), std::ios_base::in | std::ios_base::binary);

  if (!ifs_) {
    LOG(ERROR) << "Could not open segmentation file " << filename_ << "\n";
    return false;
  }

  // Read file offsets until TERM header.
  char header_type[5] = {0, 0, 0, 0, 0};
  int prev_header_id = -1;
  while (true) {
    ifs_.read(header_type, 4);

    // End of file, return.
    if (strcmp(header_type, "TERM") == 0) {
      break;
    }

    if (strcmp(header_type, "HEAD") == 0) {
      // Read header, save entries and continue to next chunk.
      int32_t num_entries;
      ifs_.read(ToCharPtr(&num_entries), sizeof(num_entries));
      header_flags_.resize(num_entries);
      for (int i = 0; i < num_entries; ++i) {
        ifs_.read(ToCharPtr(&header_flags_[i]), sizeof(header_flags_[i]));
      }
      continue;
    }

    // We only process chunk headers while skipping over seg frames.
    if (strcmp(header_type, "CHNK") != 0) {
      LOG(ERROR) << "Parsing error, expected chunk header at current offset."
                 << " Found: " << header_type;
      return false;
    }

    int32_t header_id;
    ifs_.read(ToCharPtr(&header_id), sizeof(header_id));
    CHECK_EQ(prev_header_id + 1, header_id)
        << prev_header_id << " " << header_id;

    prev_header_id = header_id;

    int32_t num_frames_in_chunk;
    ifs_.read(ToCharPtr(&num_frames_in_chunk), sizeof(num_frames_in_chunk));

    // Read offsets.
    for (int f = 0; f < num_frames_in_chunk; ++f) {
      int64_t offset;
      ifs_.read(ToCharPtr(&offset), sizeof(offset));
      file_offsets_.push_back(offset);
    }

    // Read timestamps.
    for (int f = 0; f < num_frames_in_chunk; ++f) {
      int64_t timestamp;
      ifs_.read(ToCharPtr(&timestamp), sizeof(timestamp));
      time_stamps_.push_back(timestamp);
    }

    int64_t next_header_pos;
    ifs_.read(ToCharPtr(&next_header_pos), sizeof(next_header_pos));
    ifs_.seekg(next_header_pos);
  }

  return true;
}

void SegmentationReader::SegmentationResolution(int* width, int* height) {
  CHECK_NOTNULL(width);
  CHECK_NOTNULL(height);

  const int curr_playhead = curr_frame_;
  SeekToFrame(0);

  // Read via binary as we don't need rasterization here.
  std::string data;
  ReadNextFrameBinary(&data);

  SegmentationDesc segmentation;
  segmentation.ParseFromString(data);

  *width = segmentation.frame_width();
  *height = segmentation.frame_height();

  if (curr_playhead < NumFrames()) {
    SeekToFrame(curr_playhead);
  }
}

void SegmentationReader::SeekToFrame(int frame) {
  CHECK_LT(frame, file_offsets_.size()) << "Requested frame out of bound.";
  curr_frame_ = frame;
}

bool SegmentationReader::ReadNextFrameBinary(std::string* data) {
  // Seek to next frame (to skip chunk headers).
  ifs_.seekg(file_offsets_[curr_frame_]);
  char header_type[5] = {0, 0, 0, 0, 0};
  ifs_.read(header_type, 4);
  if (strcmp(header_type, "SEGD") != 0) {
    LOG(ERROR) << "Expecting segmentation header. Error parsing file.";
    return false;
  }

  ifs_.read(ToCharPtr(&frame_sz_), sizeof(frame_sz_));

  data->resize(frame_sz_);
  ifs_.read(&data->at(0), frame_sz_);
  ++curr_frame_;

  return true;
}

bool SegmentationReader::ReadNextFrame(SegmentationDesc* desc) {
  CHECK_NOTNULL(desc);
  std::string data;
  if (!ReadNextFrameBinary(&data)) {
    LOG(ERROR) << "Could not read from file.";
    return false;
  }
  if (!desc->ParseFromString(data)) {
    LOG(ERROR) << "Could not parse segmentation proto.";
    return false;
  }
  if (valid_rasterization_ && desc->rasterization_removed()) {
    CHECK(desc->has_vector_mesh());
    ReplaceRasterizationFromVectorization(desc);
  }
  return true;
}

namespace {

template <class T> void write(std::ostringstream& stream, const T& t) {
  stream.write((const char*)&t, sizeof(t));
}

}

void StripToEssentials(const SegmentationDesc& desc,
                       bool save_vectorization,
                       bool save_shape_moments,
                       std::string* binary_rep) {
  std::ostringstream seg_data;

  int frame_width = desc.frame_width();
  int frame_height = desc.frame_height();

  write(seg_data, frame_width);
  write(seg_data, frame_height);

  if (save_vectorization) {
    CHECK(desc.has_vector_mesh());
    int num_points = desc.vector_mesh().coord_size();
    CHECK_LT(num_points, std::numeric_limits<short>::max())
      << "Overlflow, too many points for vectorization format!!!";

    LOG(INFO) << "Number of points: " << num_points;

    write(seg_data, num_points);
    for (float coord : desc.vector_mesh().coord()) {
      // Save coord as short.
      short short_coord = (short)(coord);
      write(seg_data, short_coord);
    }
  }

  // Number of regions.
  int num_regions = desc.region_size();
  write(seg_data, num_regions);

  // Save regions.
  for(int i = 0; i < num_regions; ++i) {
    const SegmentationDesc::Region2D& r = desc.region(i);

    // Id.
    int id = r.id();
    write(seg_data, id);

    if (save_vectorization) {
      int num_polygons = r.vectorization().polygon_size();
      write(seg_data, num_polygons);

      for (const auto& polygon : r.vectorization().polygon()) {
        short num_coords = polygon.coord_idx_size();
        uint8_t is_hole = polygon.hole();
        write(seg_data, num_coords);
        write(seg_data, is_hole);

        for (int coord : polygon.coord_idx()) {
          short short_coord = coord;
          write(seg_data, short_coord);
        }
      }
    } else {
      // Scanlines.
      int num_scan_inter = r.raster().scan_inter_size();
      write(seg_data, num_scan_inter);

      for (int j = 0; j < num_scan_inter; ++j) {
        int16_t y = r.raster().scan_inter(j).y();
        int16_t left = r.raster().scan_inter(j).left_x();
        int16_t right = r.raster().scan_inter(j).right_x();

        write(seg_data, y);
        write(seg_data, left);
        write(seg_data, right);
      }
    }

    // ShapeMoments.
    if (save_shape_moments) {
      int shape_sz = r.shape_moments().size();
      write(seg_data, shape_sz);
      int mean_x = r.shape_moments().mean_x();
      write(seg_data, mean_x);
      int mean_y = r.shape_moments().mean_y();
      write(seg_data, mean_y);
      int moment_xx = r.shape_moments().moment_xx();
      write(seg_data, moment_xx);
      int moment_xy = r.shape_moments().moment_xy();
      write(seg_data, moment_xy);
      int moment_yy = r.shape_moments().moment_yy();
      write(seg_data, moment_yy);
    }
  }

  // Save hierarchies.
  int hierarchy_size = desc.hierarchy_size();
  write(seg_data, hierarchy_size);

  for (int i = 0; i < hierarchy_size; ++i) {
    int num_regions = desc.hierarchy(i).region_size();
    write(seg_data, num_regions);

    for (int j = 0; j < num_regions; ++j) {
      const SegmentationDesc::CompoundRegion& r = desc.hierarchy(i).region(j);

      // Id.
      int id = r.id();
      write(seg_data, id);

      // Size.
      int size = r.size();
      write(seg_data, size);

      // Parent id.
      int parent_id = r.parent_id();
      write(seg_data, parent_id);

      // Children.
      int num_children = r.child_id_size();
      write(seg_data, num_children);

      for (int c = 0; c < num_children; ++c) {
        int child_id = r.child_id(c);
        write(seg_data, child_id);
      }

      // Start and end frame.
      int start_frame = r.start_frame();
      write(seg_data, start_frame);
      int end_frame = r.end_frame();
      write(seg_data, end_frame);
    }
  }

  seg_data.flush();
  *binary_rep = std::string(seg_data.str());
}

}  // namespace segmentation.
<commit_msg>Fixed typo, toned down excitement.<commit_after>// Copyright (c) 2010-2014, The Video Segmentation Project
// All rights reserved.

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the The Video Segmentation Project nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// ---

#include "base/base_impl.h"

#include "segment_util/segmentation_io.h"
#include "segment_util/segmentation_util.h"
#include "segment_util/segmentation.pb.h"

namespace segmentation {

namespace {
  template <class T> const char* ToConstCharPtr(const T* t) {
    return reinterpret_cast<const char*>(t);
  }
  template <class T> char* ToCharPtr(T* t) {
    return reinterpret_cast<char*>(t);
  }
}

bool SegmentationWriter::OpenFile(const vector<int>& header_entries) {
  header_entries_ = header_entries;

  // Open file to write
  VLOG(1) << "Writing segmentation to file " << filename_;
  ofs_.open(filename_.c_str(),
            std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);

  if (!ofs_) {
    LOG(ERROR) << "Could not open " << filename_ << " to write!\n";
    return false;
  }

  num_chunks_ = 0;
  curr_offset_ = 0;

  // Write header information.
  ofs_.write("HEAD", 4);
  const int32_t num_entries = header_entries_.size();
  ofs_.write(ToConstCharPtr(&num_entries), sizeof(num_entries));
  for (int i = 0; i < num_entries; ++i) {
    ofs_.write(ToConstCharPtr(&header_entries_[i]), sizeof(header_entries_[i]));
  }
  curr_offset_ = 4 + 4 + num_entries * 4;

  return true;
}

void SegmentationWriter::AddSegmentationToChunk(const SegmentationDesc& desc,
                                                int64_t pts) {
  std::string data;
  desc.SerializeToString(&data);
  AddSegmentationDataToChunk(data, pts);
}

void SegmentationWriter::AddSegmentationDataToChunk(const std::string& data,
                                                    int64_t pts) {
  // Buffer for later.
  file_offsets_.push_back(curr_offset_);
  chunk_buffer_.push_back(data);

  curr_offset_ += data.size() + 4 + sizeof(int32_t);    // SEG_FRAME total size.
  time_stamps_.push_back(pts);
}

void SegmentationWriter::WriteChunk() {
  const int32_t num_frames = file_offsets_.size();
  const int32_t chunk_id = num_chunks_++;

  CHECK_EQ(file_offsets_.size(), chunk_buffer_.size());
  CHECK_EQ(file_offsets_.size(), time_stamps_.size());

  ofs_.write("CHNK", 4);
  ofs_.write(ToConstCharPtr(&chunk_id), sizeof(chunk_id));
  ofs_.write(ToConstCharPtr(&num_frames), sizeof(num_frames));

  int64_t size_of_header =           // HEADER total size.
    4 +
    2 * sizeof(int32_t) +
    num_frames * 2 * sizeof(int64_t) +
    sizeof(int64_t);

  // Advance offsets by size of header.
  curr_offset_ += size_of_header;
  for (size_t i = 0; i < file_offsets_.size(); ++i) {
    file_offsets_[i] += size_of_header;
  }

  // Write offsets and pts.
  for (size_t i = 0; i < file_offsets_.size(); ++i) {
    ofs_.write(ToConstCharPtr(&file_offsets_[i]), sizeof(file_offsets_[i]));
  }

  CHECK_EQ(file_offsets_.size(), time_stamps_.size());
  for (size_t i = 0; i < time_stamps_.size(); ++i) {
    ofs_.write(ToConstCharPtr(&time_stamps_[i]), sizeof(time_stamps_[i]));
  }

  // Write offset of next header.
  ofs_.write(ToConstCharPtr(&curr_offset_), sizeof(curr_offset_));

  // Write frames.
  CHECK_EQ(file_offsets_.size(), chunk_buffer_.size());
  for (size_t i = 0; i < chunk_buffer_.size(); ++i) {
    ofs_.write("SEGD", 4);
    int32_t frame_size = chunk_buffer_[i].length();
    ofs_.write(ToConstCharPtr(&frame_size), sizeof(frame_size));
    ofs_.write(ToConstCharPtr(&chunk_buffer_[i][0]), frame_size);
  }

  total_frames_ += chunk_buffer_.size();

  // Clear chunk information.
  chunk_buffer_.clear();
  file_offsets_.clear();
  time_stamps_.clear();
}

void SegmentationWriter::WriteTermHeaderAndClose() {
   if (!chunk_buffer_.empty()) {
     WriteChunk();
   }

   ofs_.write("TERM", 4);
   ofs_.write(ToConstCharPtr(&num_chunks_), sizeof(num_chunks_));
   ofs_.close();

   LOG(INFO) << "Wrote a total of " << total_frames_ << " frames.";
}

void SegmentationWriter::FlushAndReopen(const std::string& filename) {
  if (!chunk_buffer_.empty()) {
    WriteChunk();
  }

  WriteTermHeaderAndClose();
  filename_ = filename;
  curr_offset_ = 0;
  num_chunks_ = 0;
  OpenFile();
}

bool SegmentationReader::OpenFileAndReadHeaders() {
  // Open file.
  VLOG(1) << "Reading segmentation from file " << filename_;
  ifs_.open(filename_.c_str(), std::ios_base::in | std::ios_base::binary);

  if (!ifs_) {
    LOG(ERROR) << "Could not open segmentation file " << filename_ << "\n";
    return false;
  }

  // Read file offsets until TERM header.
  char header_type[5] = {0, 0, 0, 0, 0};
  int prev_header_id = -1;
  while (true) {
    ifs_.read(header_type, 4);

    // End of file, return.
    if (strcmp(header_type, "TERM") == 0) {
      break;
    }

    if (strcmp(header_type, "HEAD") == 0) {
      // Read header, save entries and continue to next chunk.
      int32_t num_entries;
      ifs_.read(ToCharPtr(&num_entries), sizeof(num_entries));
      header_flags_.resize(num_entries);
      for (int i = 0; i < num_entries; ++i) {
        ifs_.read(ToCharPtr(&header_flags_[i]), sizeof(header_flags_[i]));
      }
      continue;
    }

    // We only process chunk headers while skipping over seg frames.
    if (strcmp(header_type, "CHNK") != 0) {
      LOG(ERROR) << "Parsing error, expected chunk header at current offset."
                 << " Found: " << header_type;
      return false;
    }

    int32_t header_id;
    ifs_.read(ToCharPtr(&header_id), sizeof(header_id));
    CHECK_EQ(prev_header_id + 1, header_id)
        << prev_header_id << " " << header_id;

    prev_header_id = header_id;

    int32_t num_frames_in_chunk;
    ifs_.read(ToCharPtr(&num_frames_in_chunk), sizeof(num_frames_in_chunk));

    // Read offsets.
    for (int f = 0; f < num_frames_in_chunk; ++f) {
      int64_t offset;
      ifs_.read(ToCharPtr(&offset), sizeof(offset));
      file_offsets_.push_back(offset);
    }

    // Read timestamps.
    for (int f = 0; f < num_frames_in_chunk; ++f) {
      int64_t timestamp;
      ifs_.read(ToCharPtr(&timestamp), sizeof(timestamp));
      time_stamps_.push_back(timestamp);
    }

    int64_t next_header_pos;
    ifs_.read(ToCharPtr(&next_header_pos), sizeof(next_header_pos));
    ifs_.seekg(next_header_pos);
  }

  return true;
}

void SegmentationReader::SegmentationResolution(int* width, int* height) {
  CHECK_NOTNULL(width);
  CHECK_NOTNULL(height);

  const int curr_playhead = curr_frame_;
  SeekToFrame(0);

  // Read via binary as we don't need rasterization here.
  std::string data;
  ReadNextFrameBinary(&data);

  SegmentationDesc segmentation;
  segmentation.ParseFromString(data);

  *width = segmentation.frame_width();
  *height = segmentation.frame_height();

  if (curr_playhead < NumFrames()) {
    SeekToFrame(curr_playhead);
  }
}

void SegmentationReader::SeekToFrame(int frame) {
  CHECK_LT(frame, file_offsets_.size()) << "Requested frame out of bound.";
  curr_frame_ = frame;
}

bool SegmentationReader::ReadNextFrameBinary(std::string* data) {
  // Seek to next frame (to skip chunk headers).
  ifs_.seekg(file_offsets_[curr_frame_]);
  char header_type[5] = {0, 0, 0, 0, 0};
  ifs_.read(header_type, 4);
  if (strcmp(header_type, "SEGD") != 0) {
    LOG(ERROR) << "Expecting segmentation header. Error parsing file.";
    return false;
  }

  ifs_.read(ToCharPtr(&frame_sz_), sizeof(frame_sz_));

  data->resize(frame_sz_);
  ifs_.read(&data->at(0), frame_sz_);
  ++curr_frame_;

  return true;
}

bool SegmentationReader::ReadNextFrame(SegmentationDesc* desc) {
  CHECK_NOTNULL(desc);
  std::string data;
  if (!ReadNextFrameBinary(&data)) {
    LOG(ERROR) << "Could not read from file.";
    return false;
  }
  if (!desc->ParseFromString(data)) {
    LOG(ERROR) << "Could not parse segmentation proto.";
    return false;
  }
  if (valid_rasterization_ && desc->rasterization_removed()) {
    CHECK(desc->has_vector_mesh());
    ReplaceRasterizationFromVectorization(desc);
  }
  return true;
}

namespace {

template <class T> void write(std::ostringstream& stream, const T& t) {
  stream.write((const char*)&t, sizeof(t));
}

}

void StripToEssentials(const SegmentationDesc& desc,
                       bool save_vectorization,
                       bool save_shape_moments,
                       std::string* binary_rep) {
  std::ostringstream seg_data;

  int frame_width = desc.frame_width();
  int frame_height = desc.frame_height();

  write(seg_data, frame_width);
  write(seg_data, frame_height);

  if (save_vectorization) {
    CHECK(desc.has_vector_mesh());
    int num_points = desc.vector_mesh().coord_size();
    CHECK_LT(num_points, std::numeric_limits<short>::max())
      << "Overflow, too many points for vectorization format.";

    LOG(INFO) << "Number of points: " << num_points;

    write(seg_data, num_points);
    for (float coord : desc.vector_mesh().coord()) {
      // Save coord as short.
      short short_coord = (short)(coord);
      write(seg_data, short_coord);
    }
  }

  // Number of regions.
  int num_regions = desc.region_size();
  write(seg_data, num_regions);

  // Save regions.
  for(int i = 0; i < num_regions; ++i) {
    const SegmentationDesc::Region2D& r = desc.region(i);

    // Id.
    int id = r.id();
    write(seg_data, id);

    if (save_vectorization) {
      int num_polygons = r.vectorization().polygon_size();
      write(seg_data, num_polygons);

      for (const auto& polygon : r.vectorization().polygon()) {
        short num_coords = polygon.coord_idx_size();
        uint8_t is_hole = polygon.hole();
        write(seg_data, num_coords);
        write(seg_data, is_hole);

        for (int coord : polygon.coord_idx()) {
          short short_coord = coord;
          write(seg_data, short_coord);
        }
      }
    } else {
      // Scanlines.
      int num_scan_inter = r.raster().scan_inter_size();
      write(seg_data, num_scan_inter);

      for (int j = 0; j < num_scan_inter; ++j) {
        int16_t y = r.raster().scan_inter(j).y();
        int16_t left = r.raster().scan_inter(j).left_x();
        int16_t right = r.raster().scan_inter(j).right_x();

        write(seg_data, y);
        write(seg_data, left);
        write(seg_data, right);
      }
    }

    // ShapeMoments.
    if (save_shape_moments) {
      int shape_sz = r.shape_moments().size();
      write(seg_data, shape_sz);
      int mean_x = r.shape_moments().mean_x();
      write(seg_data, mean_x);
      int mean_y = r.shape_moments().mean_y();
      write(seg_data, mean_y);
      int moment_xx = r.shape_moments().moment_xx();
      write(seg_data, moment_xx);
      int moment_xy = r.shape_moments().moment_xy();
      write(seg_data, moment_xy);
      int moment_yy = r.shape_moments().moment_yy();
      write(seg_data, moment_yy);
    }
  }

  // Save hierarchies.
  int hierarchy_size = desc.hierarchy_size();
  write(seg_data, hierarchy_size);

  for (int i = 0; i < hierarchy_size; ++i) {
    int num_regions = desc.hierarchy(i).region_size();
    write(seg_data, num_regions);

    for (int j = 0; j < num_regions; ++j) {
      const SegmentationDesc::CompoundRegion& r = desc.hierarchy(i).region(j);

      // Id.
      int id = r.id();
      write(seg_data, id);

      // Size.
      int size = r.size();
      write(seg_data, size);

      // Parent id.
      int parent_id = r.parent_id();
      write(seg_data, parent_id);

      // Children.
      int num_children = r.child_id_size();
      write(seg_data, num_children);

      for (int c = 0; c < num_children; ++c) {
        int child_id = r.child_id(c);
        write(seg_data, child_id);
      }

      // Start and end frame.
      int start_frame = r.start_frame();
      write(seg_data, start_frame);
      int end_frame = r.end_frame();
      write(seg_data, end_frame);
    }
  }

  seg_data.flush();
  *binary_rep = std::string(seg_data.str());
}

}  // namespace segmentation.
<|endoftext|>