<commit_before>/*******************************************************************************
 *
 * MIT License
 *
 * Copyright (c) 2017 Advanced Micro Devices, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 *******************************************************************************/
#include <cassert>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <memory>
#include <miopen/config.h>
#include <miopen/env.hpp>
#include <miopen/errors.hpp>
#include <miopen/gcn_asm_utils.hpp>
#include <sstream>

#ifdef __linux__
#include <paths.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#endif // __linux__

MIOPEN_DECLARE_ENV_VAR(MIOPEN_EXPERIMENTAL_GCN_ASM_PATH)

struct tmp_dir_env
{
    static const char* value() { return "TMPDIR"; }
};

#ifdef __linux__
class TempFile
{
    public:
    TempFile(const std::string& path_template)
        : _path(GetTempDirectoryPath() + "/" + path_template + "-XXXXXX")
    {
        _fd = mkstemp(&_path[0]);
        if(_fd == -1)
        {
            MIOPEN_THROW("Error: TempFile: mkstemp()");
        }
    }

    ~TempFile()
    {
        const int remove_rc = std::remove(_path.c_str());
        const int close_rc  = close(_fd);
        if(remove_rc != 0 || close_rc != 0)
        {
#ifndef NDEBUG // Be quiet in release versions.
            std::fprintf(stderr,
                         "Error: TempFile: On removal of '%s', remove_rc = %d, close_rc = %d.\n",
                         _path.c_str(),
                         remove_rc,
                         close_rc);
#endif
        }
    }

    inline operator const std::string&() { return _path; }

    private:
    std::string _path;
    int _fd;

    static const std::string GetTempDirectoryPath()
    {
        const auto path = miopen::GetStringEnv(tmp_dir_env{});
        if(path != nullptr)
        {
            return path;
        }
#if defined(P_tmpdir)
        return P_tmpdir; // a string literal, if defined.
#elif defined(_PATH_TMP)
        return _PATH_TMP; // a string literal, if defined.
#else
        return "/tmp";
#endif
    }
};
#endif

static std::string CleanupPath(const char* p);

// Redirrecting both input and output is not supported.
static int ExecuteGcnAssembler(const std::string& p,
                               std::vector<std::string>& args,
                               std::istream* in,
                               std::ostream* out);

std::string GetGcnAssemblerPathImpl()
{
    const auto asm_path_env_p = miopen::GetStringEnv(MIOPEN_EXPERIMENTAL_GCN_ASM_PATH{});
    if(asm_path_env_p)
    {
        return CleanupPath(asm_path_env_p);
    }
#ifdef MIOPEN_AMDGCN_ASSEMBLER // string literal generated by CMake
    return CleanupPath(MIOPEN_AMDGCN_ASSEMBLER);
#else
    return "";
#endif
}

std::string GetGcnAssemblerPath()
{
    static const auto result = GetGcnAssemblerPathImpl();
    return result;
}

bool ValidateGcnAssemblerImpl()
{
#ifdef __linux__
    const auto path = GetGcnAssemblerPath();
    if(path.empty())
    {
        return false;
    }
    if(!std::ifstream(path).good())
    {
        return false;
    }

    std::vector<std::string> args({"--version"});
    std::stringstream clang_stdout;
    std::string clang_result_line;
    auto clang_rc = ExecuteGcnAssembler(path, args, nullptr, &clang_stdout);

    if(clang_rc != 0)
    {
        return false;
    }

    std::getline(clang_stdout, clang_result_line);
    if(clang_result_line.find("clang") != std::string::npos)
    {
        while(!clang_stdout.eof())
        {
            std::getline(clang_stdout, clang_result_line);
            if(clang_result_line.find("Target: ") != std::string::npos)
            {
                return clang_result_line.find("amdgcn") != std::string::npos;
            }
        }
    }
#endif // __linux__
    return false;
}

bool ValidateGcnAssembler()
{
    static bool result = ValidateGcnAssemblerImpl();
    return result;
}

int ExecuteGcnAssembler(const std::string& p,
    std::istream* in,
    std::ostream* out)
{
#ifdef __linux__
    const auto redirect_stdin = (in != nullptr);
    const auto redirect_stdout = (out != nullptr);

    assert(!(redirect_stdin && redirect_stdout));

    const auto file_mode = redirect_stdout ? "r" : "w";
    std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(p.c_str(), file_mode), pclose);

    if (!pipe)
        MIOPEN_THROW("Error: X-AMDGCN-ASM: popen()");

    if (redirect_stdin || redirect_stdout)
    {
        std::array<char, 1024> buffer;

        if (redirect_stdout)
        {
            while (!feof(pipe.get()))
                if (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
                    *out << buffer.data();
        }
        else
        {
            while (!in->eof())
            {
                in->read(buffer.data(), buffer.size());
                if (fputs(buffer.data(), pipe.get()) == EOF)
                    MIOPEN_THROW("Error: X-AMDGCN-ASM: fputs()");
            }
        }
    }

    return pclose(pipe.release());
#else
    (void)p;
    (void)args;
    (void)in;
    (void)out;
    return -1;
#endif // __linux__
}

int ExecuteGcnAssembler(const std::string& p,
    std::vector<std::string>& args,
    std::istream* in,
    std::ostream* out)
{
#ifdef __linux__
    std::ostringstream cmd;

    cmd << '"' << p << '"';

    for (const auto& arg : args)
        cmd << " \"" << arg << "\"";

    return ExecuteGcnAssembler(cmd.str(), in, out);
#endif
}

int ExecuteGcnAssembler(std::vector<std::string>& args,
                        std::istream* clang_stdin_content,
                        std::ostream* clang_stdout_content)
{
    auto path = GetGcnAssemblerPath();
    return ExecuteGcnAssembler(path, args, clang_stdin_content, clang_stdout_content);
}

static std::string CleanupPath(const char* p)
{
    std::string path(p);
    static const char bad[] = "!#$*;<>?@\\^`{|}";
    for(char* c = &path[0]; c < (&path[0] + path.length()); ++c)
    {
        if(std::iscntrl(*c))
        {
            *c = '_';
            continue;
        }
        for(const char* b = &bad[0]; b < (&bad[0] + sizeof(bad) - 1); ++b)
        {
            if(*b == *c)
            {
                *c = '_';
                break;
            }
        }
    }
    return path;
}

/*
 * Temporary function which emulates online assembly feature of OpenCL-on-ROCm being developed.
 * Not intended to be used in production code, so error handling is very straghtforward,
 * just catch whatever possible and throw an exception.
 */
void AmdgcnAssemble(std::string& source, const std::string& params)
{
#ifdef __linux__
    TempFile outfile("amdgcn-asm-out-XXXXXX");

    std::vector<std::string> args({
        "-x", "assembler", "-target", "amdgcn--amdhsa",
    });

    {
        std::istringstream iss(params);
        std::string param;
        while(iss >> param)
        {
            args.push_back(param);
        };
    }
    args.push_back("-");
    args.push_back("-o");
    args.push_back(outfile);

    std::istringstream clang_stdin(source);
    const auto clang_rc = ExecuteGcnAssembler(args, &clang_stdin, nullptr);
    if(clang_rc != 0)
        MIOPEN_THROW("Assembly error(" + std::to_string(clang_rc) + ")");

    std::ifstream file(outfile, std::ios::binary | std::ios::ate);
    bool outfile_read_failed = false;
    do
    {
        const auto size = file.tellg();
        if(size == -1)
        {
            outfile_read_failed = true;
            break;
        }
        source.resize(size, '\0');
        file.seekg(std::ios::beg);
        if(file.fail())
        {
            outfile_read_failed = true;
            break;
        }
        if(file.rdbuf()->sgetn(&source[0], size) != size)
        {
            outfile_read_failed = true;
            break;
        }
    } while(false);
    file.close();
    if(outfile_read_failed)
    {
        MIOPEN_THROW("Error: X-AMDGCN-ASM: outfile_read_failed");
    }
#else
    (void)source; // -warning
    (void)params; // -warning
    MIOPEN_THROW("Error: X-AMDGCN-ASM: online assembly under Windows is not supported");
#endif //__linux__
}

template <>
void GenerateClangDefsym<const std::string&>(std::ostream& stream,
                                             const std::string& name,
                                             const std::string& value)
{
    stream << " -Wa,-defsym," << name << "=" << value;
}

std::string MakeLutKey(int w, int h, int c, int n, int k, int u, int v, int dir, int CUs)
{
    std::ostringstream ss;
    ss << w << ";" << h << ";" << c << ";" << n << ";" << k << ";" << u << ";" << v << ";" << dir
       << ";" << CUs;
    return ss.str();
}

std::string MakeLutKey(int w, int h, int c, int n, int k, int dir, int CUs)
{
    return MakeLutKey(w, h, c, n, k, 1, 1, dir, CUs);
}
<commit_msg>Formatting<commit_after>/*******************************************************************************
 *
 * MIT License
 *
 * Copyright (c) 2017 Advanced Micro Devices, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 *******************************************************************************/
#include <cassert>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <memory>
#include <miopen/config.h>
#include <miopen/env.hpp>
#include <miopen/errors.hpp>
#include <miopen/gcn_asm_utils.hpp>
#include <sstream>

#ifdef __linux__
#include <paths.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#endif // __linux__

MIOPEN_DECLARE_ENV_VAR(MIOPEN_EXPERIMENTAL_GCN_ASM_PATH)

struct tmp_dir_env
{
    static const char* value() { return "TMPDIR"; }
};

#ifdef __linux__
class TempFile
{
    public:
    TempFile(const std::string& path_template)
        : _path(GetTempDirectoryPath() + "/" + path_template + "-XXXXXX")
    {
        _fd = mkstemp(&_path[0]);
        if(_fd == -1)
        {
            MIOPEN_THROW("Error: TempFile: mkstemp()");
        }
    }

    ~TempFile()
    {
        const int remove_rc = std::remove(_path.c_str());
        const int close_rc  = close(_fd);
        if(remove_rc != 0 || close_rc != 0)
        {
#ifndef NDEBUG // Be quiet in release versions.
            std::fprintf(stderr,
                         "Error: TempFile: On removal of '%s', remove_rc = %d, close_rc = %d.\n",
                         _path.c_str(),
                         remove_rc,
                         close_rc);
#endif
        }
    }

    inline operator const std::string&() { return _path; }

    private:
    std::string _path;
    int _fd;

    static const std::string GetTempDirectoryPath()
    {
        const auto path = miopen::GetStringEnv(tmp_dir_env{});
        if(path != nullptr)
        {
            return path;
        }
#if defined(P_tmpdir)
        return P_tmpdir; // a string literal, if defined.
#elif defined(_PATH_TMP)
        return _PATH_TMP; // a string literal, if defined.
#else
        return "/tmp";
#endif
    }
};
#endif

static std::string CleanupPath(const char* p);

// Redirrecting both input and output is not supported.
static int ExecuteGcnAssembler(const std::string& p,
                               std::vector<std::string>& args,
                               std::istream* in,
                               std::ostream* out);

std::string GetGcnAssemblerPathImpl()
{
    const auto asm_path_env_p = miopen::GetStringEnv(MIOPEN_EXPERIMENTAL_GCN_ASM_PATH{});
    if(asm_path_env_p)
    {
        return CleanupPath(asm_path_env_p);
    }
#ifdef MIOPEN_AMDGCN_ASSEMBLER // string literal generated by CMake
    return CleanupPath(MIOPEN_AMDGCN_ASSEMBLER);
#else
    return "";
#endif
}

std::string GetGcnAssemblerPath()
{
    static const auto result = GetGcnAssemblerPathImpl();
    return result;
}

bool ValidateGcnAssemblerImpl()
{
#ifdef __linux__
    const auto path = GetGcnAssemblerPath();
    if(path.empty())
    {
        return false;
    }
    if(!std::ifstream(path).good())
    {
        return false;
    }

    std::vector<std::string> args({"--version"});
    std::stringstream clang_stdout;
    std::string clang_result_line;
    auto clang_rc = ExecuteGcnAssembler(path, args, nullptr, &clang_stdout);

    if(clang_rc != 0)
    {
        return false;
    }

    std::getline(clang_stdout, clang_result_line);
    if(clang_result_line.find("clang") != std::string::npos)
    {
        while(!clang_stdout.eof())
        {
            std::getline(clang_stdout, clang_result_line);
            if(clang_result_line.find("Target: ") != std::string::npos)
            {
                return clang_result_line.find("amdgcn") != std::string::npos;
            }
        }
    }
#endif // __linux__
    return false;
}

bool ValidateGcnAssembler()
{
    static bool result = ValidateGcnAssemblerImpl();
    return result;
}

int ExecuteGcnAssembler(const std::string& p, std::istream* in, std::ostream* out)
{
#ifdef __linux__
    const auto redirect_stdin  = (in != nullptr);
    const auto redirect_stdout = (out != nullptr);

    assert(!(redirect_stdin && redirect_stdout));

    const auto file_mode = redirect_stdout ? "r" : "w";
    std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(p.c_str(), file_mode), pclose);

    if(!pipe)
        MIOPEN_THROW("Error: X-AMDGCN-ASM: popen()");

    if(redirect_stdin || redirect_stdout)
    {
        std::array<char, 1024> buffer;

        if(redirect_stdout)
        {
            while(!feof(pipe.get()))
                if(fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
                    *out << buffer.data();
        }
        else
        {
            while(!in->eof())
            {
                in->read(buffer.data(), buffer.size());
                if(fputs(buffer.data(), pipe.get()) == EOF)
                    MIOPEN_THROW("Error: X-AMDGCN-ASM: fputs()");
            }
        }
    }

    return pclose(pipe.release());
#else
    (void)p;
    (void)args;
    (void)in;
    (void)out;
    return -1;
#endif // __linux__
}

int ExecuteGcnAssembler(const std::string& p,
                        std::vector<std::string>& args,
                        std::istream* in,
                        std::ostream* out)
{
#ifdef __linux__
    std::ostringstream cmd;

    cmd << '"' << p << '"';

    for(const auto& arg : args)
        cmd << " \"" << arg << "\"";

    return ExecuteGcnAssembler(cmd.str(), in, out);
#endif
}

int ExecuteGcnAssembler(std::vector<std::string>& args,
                        std::istream* clang_stdin_content,
                        std::ostream* clang_stdout_content)
{
    auto path = GetGcnAssemblerPath();
    return ExecuteGcnAssembler(path, args, clang_stdin_content, clang_stdout_content);
}

static std::string CleanupPath(const char* p)
{
    std::string path(p);
    static const char bad[] = "!#$*;<>?@\\^`{|}";
    for(char* c = &path[0]; c < (&path[0] + path.length()); ++c)
    {
        if(std::iscntrl(*c))
        {
            *c = '_';
            continue;
        }
        for(const char* b = &bad[0]; b < (&bad[0] + sizeof(bad) - 1); ++b)
        {
            if(*b == *c)
            {
                *c = '_';
                break;
            }
        }
    }
    return path;
}

/*
 * Temporary function which emulates online assembly feature of OpenCL-on-ROCm being developed.
 * Not intended to be used in production code, so error handling is very straghtforward,
 * just catch whatever possible and throw an exception.
 */
void AmdgcnAssemble(std::string& source, const std::string& params)
{
#ifdef __linux__
    TempFile outfile("amdgcn-asm-out-XXXXXX");

    std::vector<std::string> args({
        "-x", "assembler", "-target", "amdgcn--amdhsa",
    });

    {
        std::istringstream iss(params);
        std::string param;
        while(iss >> param)
        {
            args.push_back(param);
        };
    }
    args.push_back("-");
    args.push_back("-o");
    args.push_back(outfile);

    std::istringstream clang_stdin(source);
    const auto clang_rc = ExecuteGcnAssembler(args, &clang_stdin, nullptr);
    if(clang_rc != 0)
        MIOPEN_THROW("Assembly error(" + std::to_string(clang_rc) + ")");

    std::ifstream file(outfile, std::ios::binary | std::ios::ate);
    bool outfile_read_failed = false;
    do
    {
        const auto size = file.tellg();
        if(size == -1)
        {
            outfile_read_failed = true;
            break;
        }
        source.resize(size, '\0');
        file.seekg(std::ios::beg);
        if(file.fail())
        {
            outfile_read_failed = true;
            break;
        }
        if(file.rdbuf()->sgetn(&source[0], size) != size)
        {
            outfile_read_failed = true;
            break;
        }
    } while(false);
    file.close();
    if(outfile_read_failed)
    {
        MIOPEN_THROW("Error: X-AMDGCN-ASM: outfile_read_failed");
    }
#else
    (void)source; // -warning
    (void)params; // -warning
    MIOPEN_THROW("Error: X-AMDGCN-ASM: online assembly under Windows is not supported");
#endif //__linux__
}

template <>
void GenerateClangDefsym<const std::string&>(std::ostream& stream,
                                             const std::string& name,
                                             const std::string& value)
{
    stream << " -Wa,-defsym," << name << "=" << value;
}

std::string MakeLutKey(int w, int h, int c, int n, int k, int u, int v, int dir, int CUs)
{
    std::ostringstream ss;
    ss << w << ";" << h << ";" << c << ";" << n << ";" << k << ";" << u << ";" << v << ";" << dir
       << ";" << CUs;
    return ss.str();
}

std::string MakeLutKey(int w, int h, int c, int n, int k, int dir, int CUs)
{
    return MakeLutKey(w, h, c, n, k, 1, 1, dir, CUs);
}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "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 Nokia Corporation and its Subsidiary(-ies) 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."
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtCore>
#include <qtapsensor.h>

QTM_USE_NAMESPACE

class TapSensorFilter : public QTapFilter
{
public:
    bool filter(QTapReading *reading)
    {
        int diff = ( reading->timestamp() - stamp );
        stamp = reading->timestamp();
        QString output;
        switch (reading->tapDirection()) {
            case QTapReading::X:         output = "X";         break;
            case QTapReading::Y:         output = "Y";         break;
            case QTapReading::Z:         output = "Z";         break;
            case QTapReading::X_Pos:     output = "X pos";     break;
            case QTapReading::Y_Pos:     output = "Y pos";     break;
            case QTapReading::Z_Pos:     output = "Z pos";     break;
            case QTapReading::X_Neg:     output = "X neg";     break;
            case QTapReading::Y_Neg:     output = "Y neg";     break;
            case QTapReading::Z_Neg:     output = "Z neg";     break;
            case QTapReading::Undefined: output = "Undefined"; break;
            default: output = "Invalid enum value";
        }
        QTextStream out(stdout);
        out << "Tap: ";
        if(reading->isDoubleTap())
            out << "Double ";
        else
            out << "Single ";
        out << QString(" (%1 ms since last, %2 Hz)").arg(diff / 1000, 5).arg( 1000000.0 / diff, 3, 'f', 1) << endl;
        return false; // don't store the reading in the sensor
    }
private:
    qtimestamp stamp;
};

int main(int argc, char **argv)
{
    QCoreApplication app(argc, argv);
    QStringList args = app.arguments();
    int rate_place = args.indexOf("-r");
    int rate_val = 0;
    if (rate_place != -1)
        rate_val = args.at(rate_place + 1).toInt();

    QTapSensor doublesensor;
    doublesensor.setProperty("returnDoubleTapEvents", true);
    if (rate_val > 0) {
        doublesensor.setDataRate(rate_val);
    }
    TapSensorFilter filter;
    doublesensor.addFilter(&filter);
    doublesensor.start();
    if (!doublesensor.isActive()) {
        qWarning("Tapsensor (double) didn't start!");
        return 1;
    }

    QTapSensor singlesensor;
    singlesensor.setProperty("returnDoubleTapEvents", false);
    if (rate_val > 0) {
        singlesensor.setDataRate(rate_val);
    }
    singlesensor.addFilter(&filter);
    singlesensor.start();
    if (!singlesensor.isActive()) {
        qWarning("Tapsensor (single) didn't start!");
        return 1;
    }

    return app.exec();
}
<commit_msg>direction printed<commit_after>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "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 Nokia Corporation and its Subsidiary(-ies) 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."
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtCore>
#include <qtapsensor.h>

QTM_USE_NAMESPACE

class TapSensorFilter : public QTapFilter
{
public:
    bool filter(QTapReading *reading)
    {
        int diff = ( reading->timestamp() - stamp );
        stamp = reading->timestamp();
        QString output;
        switch (reading->tapDirection()) {
            case QTapReading::X:         output = "X";         break;
            case QTapReading::Y:         output = "Y";         break;
            case QTapReading::Z:         output = "Z";         break;
            case QTapReading::X_Pos:     output = "X pos";     break;
            case QTapReading::Y_Pos:     output = "Y pos";     break;
            case QTapReading::Z_Pos:     output = "Z pos";     break;
            case QTapReading::X_Neg:     output = "X neg";     break;
            case QTapReading::Y_Neg:     output = "Y neg";     break;
            case QTapReading::Z_Neg:     output = "Z neg";     break;
            case QTapReading::Undefined: output = "Undefined"; break;
            default: output = "Invalid enum value";
        }
        QTextStream out(stdout);
        out << "Tap: ";
        if(reading->isDoubleTap())
            out << "Double ";
        else
            out << "Single ";
        out << output<< QString(" (%1 ms since last, %2 Hz)").arg(diff / 1000, 5).arg( 1000000.0 / diff, 3, 'f', 1) << endl;
        return false; // don't store the reading in the sensor
    }
private:
    qtimestamp stamp;
};

int main(int argc, char **argv)
{
    QCoreApplication app(argc, argv);
    QStringList args = app.arguments();
    int rate_place = args.indexOf("-r");
    int rate_val = 0;
    if (rate_place != -1)
        rate_val = args.at(rate_place + 1).toInt();

    TapSensorFilter filter;

    QTapSensor doublesensor;
    doublesensor.setProperty("returnDoubleTapEvents", true);
    if (rate_val > 0) {
        doublesensor.setDataRate(rate_val);
    }
    doublesensor.addFilter(&filter);
    doublesensor.start();
    if (!doublesensor.isActive()) {
        qWarning("Tapsensor (double) didn't start!");
        return 1;
    }

    QTapSensor singlesensor;
    singlesensor.setProperty("returnDoubleTapEvents", false);

    bool isDouble = singlesensor.property("returnDoubleTapEvents").toBool();

    if (rate_val > 0) {
        singlesensor.setDataRate(rate_val);
    }
    singlesensor.addFilter(&filter);
    singlesensor.start();

    isDouble = singlesensor.property("returnDoubleTapEvents").toBool();

    if (!singlesensor.isActive()) {
        qWarning("Tapsensor (single) didn't start!");
        return 1;
    }

    return app.exec();
}
<|endoftext|>


<commit_before>/*
 * This file is part of the CN24 semantic segmentation software,
 * copyright (C) 2015 Clemens-Alexander Brust (ikosa dot de at gmail dot com).
 *
 * For licensing information, see the LICENSE file included with this project.
 */
#include <sstream>
#include <cmath>
#include <chrono>

#include "Log.h"
#include "Net.h"
#include "StatLayer.h"
#include "CLHelper.h"

#include "Trainer.h"

namespace Conv {

	Trainer::Trainer(Conv::NetGraph& graph, TrainerSettings settings) :
		graph_(graph), settings_(settings) {
		LOGDEBUG << "Instance created";

		// We need a training layer to select training samples and some kind of
		// loss function to minimize
		if (graph_.GetTrainingNodes().size() == 0 || graph_.GetLossNodes().size() == 0) {
			FATAL("Net doesn't have training layer or loss function layer!");
		}

		// Ask the Net for parameters
		graph_.GetParameters(parameters_);

		LOGDEBUG << "Optimizing " << parameters_.size() << " sets of parameters.";

		unsigned int w = 0;

		for (unsigned int p = 0; p < parameters_.size(); p++) {
			w += parameters_[p]->data.elements();

      // Allocate Tensors for momentum
      Tensor* last_delta = new Tensor();
      Tensor* last_gradient = new Tensor();
      Tensor* accumulated_gradient = new Tensor();
      last_delta->Resize (parameters_[p]->data);
      last_delta->Clear();
      last_gradient->Resize (parameters_[p]->data);
      last_gradient->Clear();
      accumulated_gradient->Resize (parameters_[p]->data);
      accumulated_gradient->Clear();

      last_deltas_.push_back (last_delta);
      last_gradients_.push_back (last_gradient);
      accumulated_gradients_.push_back (accumulated_gradient);
    }

		// Outputs the number of weights
		LOGDEBUG << "Weights: " << w;

		first_training_layer_ = dynamic_cast<TrainingLayer*>(graph_.GetTrainingNodes()[0]->layer);
		sample_count_ = first_training_layer_->GetLabelWidth() * first_training_layer_->GetLabelHeight()
    * first_training_layer_->GetBatchSize();
}

void Trainer::Train (unsigned int epochs) {
  // net_.SetTestOnlyStatDisabled (false);
  graph_.SetIsTesting(false);

  for (unsigned int e = 0; e < epochs; e++)
    Epoch();

  // net_.SetTestOnlyStatDisabled (false);
}

void Trainer::Test() {
	datum aggregate_loss = 0.0;
	datum* loss_sums = new datum[graph_.GetLossNodes().size()];
	for (unsigned int n = 0; n < graph_.GetLossNodes().size(); n++)
		loss_sums[n] = 0;

  unsigned int iterations = (first_training_layer_->GetSamplesInTestingSet()
                             / first_training_layer_->GetBatchSize()) + 1;
  iterations = (unsigned int) ( ( (datum) iterations) *
                                settings_.testing_ratio);

	for (NetGraphNode* training_node : graph_.GetTrainingNodes())
		(dynamic_cast<TrainingLayer*>(training_node->layer))->SetTestingMode(true);

  graph_.SetIsTesting(true);

  LOGDEBUG << "Testing, iterations: " << iterations <<
           ", batch size: " << first_training_layer_->GetBatchSize();

  auto t_begin = std::chrono::system_clock::now();

  for (unsigned int i = 0; i < iterations; i++) {
    graph_.FeedForward();
    for (unsigned int n = 0; n < graph_.GetLossNodes().size(); n++) {
      LossFunctionLayer* lossfunction_layer = dynamic_cast<LossFunctionLayer*>(graph_.GetLossNodes()[n]->layer);
			const datum loss = lossfunction_layer->CalculateLossFunction();
			loss_sums[n] += loss;
			aggregate_loss += loss;
		}
	}

  auto t_end = std::chrono::system_clock::now();
  std::chrono::duration<double> t_diff = t_end - t_begin;
  LOGDEBUG << "Testing, sps: " <<
          (datum) (sample_count_ * iterations)
          / (datum) t_diff.count();

  LOGDEBUG << "Testing, tps: " <<
          1000000.0f * (datum) t_diff.count() /
          (datum) (sample_count_ * iterations) << " us";

	for (unsigned int n = 0; n < graph_.GetLossNodes().size(); n++) {
		LossFunctionLayer* lossfunction_layer = dynamic_cast<LossFunctionLayer*>(graph_.GetLossNodes()[n]->layer);
		LOGINFO << "Testing (Epoch " << epoch_ << ", node " << n << ") " << graph_.GetLossNodes()[n]->layer->GetLayerDescription() <<  " lps: " << loss_sums[n] / (datum)(iterations * sample_count_);
	}
	LOGINFO << "Testing (Epoch " << epoch_ << ") aggregate lps: " << aggregate_loss / (datum)(iterations * sample_count_);

	for (unsigned int n = 0; n < graph_.GetStatNodes().size(); n++) {
		StatLayer* stat_layer = dynamic_cast<StatLayer*>(graph_.GetStatNodes()[n]->layer);
    std::stringstream epochname;
    epochname << "Testing  - Epoch " << epoch_ << " -";
    stat_layer->Print (epochname.str(), false);
    stat_layer->Reset();
	}

	for (NetGraphNode* training_node : graph_.GetTrainingNodes())
		(dynamic_cast<TrainingLayer*>(training_node->layer))->SetTestingMode(false);

  graph_.SetIsTesting(false);

	delete[] loss_sums;
}

void Trainer::Epoch() {
	datum aggregate_loss = 0.0;
	datum* loss_sums = new datum[graph_.GetLossNodes().size()];
	for (unsigned int n = 0; n < graph_.GetLossNodes().size(); n++)
		loss_sums[n] = 0;

  unsigned int iterations =
    settings_.iterations == 0 ?
    first_training_layer_->GetSamplesInTrainingSet() :
    settings_.iterations;
  iterations = (unsigned int) ( ( (datum) iterations) *
                                settings_.epoch_training_ratio);

  unsigned int fiftieth = 0;
  unsigned int tenth = 0;

	for (NetGraphNode* training_node : graph_.GetTrainingNodes())
		(dynamic_cast<TrainingLayer*>(training_node->layer))->SetTestingMode(false);

  LOGDEBUG << "Epoch: " << epoch_ << ", it: " << iterations <<
           ", bsize: " << first_training_layer_->GetBatchSize() * settings_.sbatchsize << ", lr0: " <<
           CalculateLR (epoch_ * iterations) << std::endl;

  auto t_begin = std::chrono::system_clock::now();

  for (unsigned int i = 0; i < iterations; i++) {
    if ( (50 * i / iterations) > fiftieth) {
      fiftieth = 50 * i / iterations;
      std::cout << "." << std::flush;
    }

    if ( (10 * i / iterations) > tenth) {
      tenth = 10 * i / iterations;
      std::cout << tenth << "0%" << std::flush;
    }

    // Reset gradients
    for (unsigned int np = 0; np < accumulated_gradients_.size(); np++)
      accumulated_gradients_[np]->Clear();

    for (unsigned int b = 0; b < settings_.sbatchsize; b++) {
      graph_.FeedForward();

      // Save errors
			for (unsigned int n = 0; n < graph_.GetLossNodes().size(); n++) {
				LossFunctionLayer* lossfunction_layer = dynamic_cast<LossFunctionLayer*>(graph_.GetLossNodes()[n]->layer);
				const datum loss = lossfunction_layer->CalculateLossFunction();
				loss_sums[n] += loss;
				aggregate_loss += loss;
			}

      // Correct errors
      graph_.BackPropagate();

      unsigned int np = 0;

      // Accumulate gradients
      for (unsigned int l = 0; l < graph_.GetNodes().size(); l++) {
				Layer* const layer = graph_.GetNodes()[l]->layer;
        for (unsigned int p = 0; p < layer->parameters().size(); p++) {
          Tensor& gradients = layer->parameters() [p]->delta;
#ifdef BUILD_OPENCL
          gradients.MoveToCPU();
#endif

          for (unsigned int e = 0; e < gradients.elements(); e++) {
            (* (accumulated_gradients_[np])) [e] += gradients[e];
          }

          np++;
        }
      }
    }

    // Calculate annealed learning rate
    const datum lr =
      CalculateLR (epoch_ * iterations + i);

    // Apply gradients with new learning rate
    ApplyGradients (lr);
  }

  auto t_end = std::chrono::system_clock::now();
  std::chrono::duration<double> t_diff = t_end - t_begin;
  LOGDEBUG << "Training, sps: " <<
          (datum) (sample_count_ * settings_.sbatchsize
                   * first_training_layer_->GetLossSamplingProbability() * iterations)
          / (datum) t_diff.count();

  LOGDEBUG << "Training, tps: " <<
          1000000.0f * (datum) t_diff.count() /
          (datum) (sample_count_ * settings_.sbatchsize
                   * first_training_layer_->GetLossSamplingProbability() * iterations) << " us";

#ifdef BUILD_OPENCL
  LOGDEBUG << "Training, GB/s   up: " << ((datum)CLHelper::bytes_up)/(1073741824.0 * (datum)t_diff.count());
  LOGDEBUG << "Training, GB/s down: " << ((datum)CLHelper::bytes_down)/(1073741824.0 * (datum)t_diff.count());
  CLHelper::bytes_up = 0;
  CLHelper::bytes_down = 0;
#endif

  // Display training epoch_error
	for (unsigned int n = 0; n < graph_.GetLossNodes().size(); n++) {
		LossFunctionLayer* lossfunction_layer = dynamic_cast<LossFunctionLayer*>(graph_.GetLossNodes()[n]->layer);
		LOGINFO << "Training (Epoch " << epoch_ << ", node " << n << ") " << graph_.GetLossNodes()[n]->layer->GetLayerDescription() <<  " lps: " << loss_sums[n] / (datum)(iterations * sample_count_ * settings_.sbatchsize * first_training_layer_->GetLossSamplingProbability());
	}
	LOGINFO << "Training (Epoch " << epoch_ << ") aggregate lps: " << aggregate_loss / (datum)(iterations * sample_count_ * settings_.sbatchsize * first_training_layer_->GetLossSamplingProbability());

	for (unsigned int n = 0; n < graph_.GetStatNodes().size(); n++) {
		StatLayer* stat_layer = dynamic_cast<StatLayer*>(graph_.GetStatNodes()[n]->layer);
    std::stringstream epochname;
    epochname << "Training  - Epoch " << epoch_ << " -";
    stat_layer->Print (epochname.str(), true);
    stat_layer->Reset();
	}

  delete[] loss_sums;
  epoch_++;
}

void Trainer::ApplyGradients (datum lr) {
  unsigned int dp = 0;

	for (unsigned int l = 0; l < graph_.GetNodes().size(); l++) {
		Layer* const layer = graph_.GetNodes()[l]->layer;
    datum layer_lr;
    switch (settings_.optimization_method) {
      case GRADIENT_DESCENT:
        layer_lr = layer->local_lr_;
        break;
      case QUICKPROP:
        layer_lr = layer->local_lr_;
        break;
    }

    for (unsigned int p = 0; p < layer->parameters().size(); p++) {
      CombinedTensor* const param = layer->parameters_[p];
#ifdef BUILD_OPENCL
      param->data.MoveToCPU();
#endif

      for (unsigned int w = 0; w < param->data.elements(); w++) {
        const datum weight = param->data (w);
        const datum l1_gradient = (weight > 0) - (weight < 0);
        const datum l2_gradient = weight;
        const datum w_gradient = (*accumulated_gradients_[dp]) [w];

        /*
         * http://www.iro.umontreal.ca/~pift6266/H10/notes/gradient.html
         *
         * This site says that one should average the gradient over
         * the minibatch
         */
        datum delta =

          // Average of gradient over minibatch
          layer_lr * (w_gradient / (datum) (sample_count_ * settings_.sbatchsize)) +
          // Regularization
          layer_lr * (settings_.l2_weight * l2_gradient + settings_.l1_weight * l1_gradient);

        // This is needed for both methods
        const datum last_step = (*last_deltas_[dp]) (w);

        switch (settings_.optimization_method) {
          case GRADIENT_DESCENT:
          {
            const datum step = lr * delta + settings_.momentum * last_step;
            param->data[w] -= step;

            // Backup delta
            (*last_deltas_[dp]) [w] = step;
          }
            break;
          case QUICKPROP:
          {
            const datum last_gradient = (*last_gradients_[dp]) (w);
            const datum s = settings_.mu / (1.0 + settings_.mu);

            datum step = 0;
            if(last_step > 0.001) {
              if(delta > 0.0) {
                step += lr * settings_.eta * delta;
              }

              if(delta > (s * last_gradient)) {
                step += settings_.mu * last_step;
              } else {
                step += last_step * delta / (last_gradient - delta);
              }

            } else if(last_step < -0.001) {
              if(delta < 0.0) {
                step += lr * settings_.eta * delta;
              }

              if(delta < (s * last_gradient)) {
                step += settings_.mu * last_step;
              } else {
                step += last_step * delta / (last_gradient - delta);
              }
            } else {
              step += lr * settings_.eta * delta;
            }

            if(step > 1000 || step < -1000) {
              if(step>1000)
                step=1000;
              else
                step=-1000;
            }

            param->data[w] -= step;

            // Backup steps and gradient
            (*last_deltas_[dp]) [w] = step;
            (*last_gradients_[dp]) [w] = delta;
          }
            break;
        }
      }

      dp++;
    }
  }
}

std::ostream& operator<< (std::ostream & output,
                          const TrainerSettings settings) {
  output << "LR: " << settings.learning_rate << ", ";
  output << "GM: " << settings.gamma << ", ";
  output << "EX: " << settings.exponent << ", ";
  output << "SB: " << settings.sbatchsize << ", ";
  output << "PB: " << settings.pbatchsize << ", ";
  output << "L1: " << settings.l1_weight << ", ";
  output << "L2: " << settings.l2_weight << ", ";
  output << "MM: " << settings.momentum << ", ";
  output << "MU: " << settings.mu << ", ";
  output << "ET: " << settings.eta << ", ";
  switch (settings.optimization_method) {
    case GRADIENT_DESCENT:
      output << "GD";
      break;
    case QUICKPROP:
      output << "QP";
      break;
  }
  return output;
}

}
<commit_msg>Trainer: Report epochs at INF level<commit_after>/*
 * This file is part of the CN24 semantic segmentation software,
 * copyright (C) 2015 Clemens-Alexander Brust (ikosa dot de at gmail dot com).
 *
 * For licensing information, see the LICENSE file included with this project.
 */
#include <sstream>
#include <cmath>
#include <chrono>

#include "Log.h"
#include "Net.h"
#include "StatLayer.h"
#include "CLHelper.h"

#include "Trainer.h"

namespace Conv {

	Trainer::Trainer(Conv::NetGraph& graph, TrainerSettings settings) :
		graph_(graph), settings_(settings) {
		LOGDEBUG << "Instance created";

		// We need a training layer to select training samples and some kind of
		// loss function to minimize
		if (graph_.GetTrainingNodes().size() == 0 || graph_.GetLossNodes().size() == 0) {
			FATAL("Net doesn't have training layer or loss function layer!");
		}

		// Ask the Net for parameters
		graph_.GetParameters(parameters_);

		LOGDEBUG << "Optimizing " << parameters_.size() << " sets of parameters.";

		unsigned int w = 0;

		for (unsigned int p = 0; p < parameters_.size(); p++) {
			w += parameters_[p]->data.elements();

      // Allocate Tensors for momentum
      Tensor* last_delta = new Tensor();
      Tensor* last_gradient = new Tensor();
      Tensor* accumulated_gradient = new Tensor();
      last_delta->Resize (parameters_[p]->data);
      last_delta->Clear();
      last_gradient->Resize (parameters_[p]->data);
      last_gradient->Clear();
      accumulated_gradient->Resize (parameters_[p]->data);
      accumulated_gradient->Clear();

      last_deltas_.push_back (last_delta);
      last_gradients_.push_back (last_gradient);
      accumulated_gradients_.push_back (accumulated_gradient);
    }

		// Outputs the number of weights
		LOGDEBUG << "Weights: " << w;

		first_training_layer_ = dynamic_cast<TrainingLayer*>(graph_.GetTrainingNodes()[0]->layer);
		sample_count_ = first_training_layer_->GetLabelWidth() * first_training_layer_->GetLabelHeight()
    * first_training_layer_->GetBatchSize();
}

void Trainer::Train (unsigned int epochs) {
  // net_.SetTestOnlyStatDisabled (false);
  graph_.SetIsTesting(false);

  for (unsigned int e = 0; e < epochs; e++)
    Epoch();

  // net_.SetTestOnlyStatDisabled (false);
}

void Trainer::Test() {
	datum aggregate_loss = 0.0;
	datum* loss_sums = new datum[graph_.GetLossNodes().size()];
	for (unsigned int n = 0; n < graph_.GetLossNodes().size(); n++)
		loss_sums[n] = 0;

  unsigned int iterations = (first_training_layer_->GetSamplesInTestingSet()
                             / first_training_layer_->GetBatchSize()) + 1;
  iterations = (unsigned int) ( ( (datum) iterations) *
                                settings_.testing_ratio);

	for (NetGraphNode* training_node : graph_.GetTrainingNodes())
		(dynamic_cast<TrainingLayer*>(training_node->layer))->SetTestingMode(true);

  graph_.SetIsTesting(true);

  LOGDEBUG << "Testing, iterations: " << iterations <<
           ", batch size: " << first_training_layer_->GetBatchSize();

  auto t_begin = std::chrono::system_clock::now();

  for (unsigned int i = 0; i < iterations; i++) {
    graph_.FeedForward();
    for (unsigned int n = 0; n < graph_.GetLossNodes().size(); n++) {
      LossFunctionLayer* lossfunction_layer = dynamic_cast<LossFunctionLayer*>(graph_.GetLossNodes()[n]->layer);
			const datum loss = lossfunction_layer->CalculateLossFunction();
			loss_sums[n] += loss;
			aggregate_loss += loss;
		}
	}

  auto t_end = std::chrono::system_clock::now();
  std::chrono::duration<double> t_diff = t_end - t_begin;
  LOGDEBUG << "Testing, sps: " <<
          (datum) (sample_count_ * iterations)
          / (datum) t_diff.count();

  LOGDEBUG << "Testing, tps: " <<
          1000000.0f * (datum) t_diff.count() /
          (datum) (sample_count_ * iterations) << " us";

	for (unsigned int n = 0; n < graph_.GetLossNodes().size(); n++) {
		LossFunctionLayer* lossfunction_layer = dynamic_cast<LossFunctionLayer*>(graph_.GetLossNodes()[n]->layer);
		LOGINFO << "Testing (Epoch " << epoch_ << ", node " << n << ") " << graph_.GetLossNodes()[n]->layer->GetLayerDescription() <<  " lps: " << loss_sums[n] / (datum)(iterations * sample_count_);
	}
	LOGINFO << "Testing (Epoch " << epoch_ << ") aggregate lps: " << aggregate_loss / (datum)(iterations * sample_count_);

	for (unsigned int n = 0; n < graph_.GetStatNodes().size(); n++) {
		StatLayer* stat_layer = dynamic_cast<StatLayer*>(graph_.GetStatNodes()[n]->layer);
    std::stringstream epochname;
    epochname << "Testing  - Epoch " << epoch_ << " -";
    stat_layer->Print (epochname.str(), false);
    stat_layer->Reset();
	}

	for (NetGraphNode* training_node : graph_.GetTrainingNodes())
		(dynamic_cast<TrainingLayer*>(training_node->layer))->SetTestingMode(false);

  graph_.SetIsTesting(false);

	delete[] loss_sums;
}

void Trainer::Epoch() {
	datum aggregate_loss = 0.0;
	datum* loss_sums = new datum[graph_.GetLossNodes().size()];
	for (unsigned int n = 0; n < graph_.GetLossNodes().size(); n++)
		loss_sums[n] = 0;

  unsigned int iterations =
    settings_.iterations == 0 ?
    first_training_layer_->GetSamplesInTrainingSet() :
    settings_.iterations;
  iterations = (unsigned int) ( ( (datum) iterations) *
                                settings_.epoch_training_ratio);

  unsigned int fiftieth = 0;
  unsigned int tenth = 0;

	for (NetGraphNode* training_node : graph_.GetTrainingNodes())
		(dynamic_cast<TrainingLayer*>(training_node->layer))->SetTestingMode(false);

  LOGINFO << "Epoch: " << epoch_ << ", it: " << iterations <<
           ", bsize: " << first_training_layer_->GetBatchSize() * settings_.sbatchsize << ", current lr: " <<
           CalculateLR (epoch_ * iterations) << std::endl;

  auto t_begin = std::chrono::system_clock::now();

  for (unsigned int i = 0; i < iterations; i++) {
    if ( (50 * i / iterations) > fiftieth) {
      fiftieth = 50 * i / iterations;
      std::cout << "." << std::flush;
    }

    if ( (10 * i / iterations) > tenth) {
      tenth = 10 * i / iterations;
      std::cout << tenth << "0%" << std::flush;
    }

    // Reset gradients
    for (unsigned int np = 0; np < accumulated_gradients_.size(); np++)
      accumulated_gradients_[np]->Clear();

    for (unsigned int b = 0; b < settings_.sbatchsize; b++) {
      graph_.FeedForward();

      // Save errors
			for (unsigned int n = 0; n < graph_.GetLossNodes().size(); n++) {
				LossFunctionLayer* lossfunction_layer = dynamic_cast<LossFunctionLayer*>(graph_.GetLossNodes()[n]->layer);
				const datum loss = lossfunction_layer->CalculateLossFunction();
				loss_sums[n] += loss;
				aggregate_loss += loss;
			}

      // Correct errors
      graph_.BackPropagate();

      unsigned int np = 0;

      // Accumulate gradients
      for (unsigned int l = 0; l < graph_.GetNodes().size(); l++) {
				Layer* const layer = graph_.GetNodes()[l]->layer;
        for (unsigned int p = 0; p < layer->parameters().size(); p++) {
          Tensor& gradients = layer->parameters() [p]->delta;
#ifdef BUILD_OPENCL
          gradients.MoveToCPU();
#endif

          for (unsigned int e = 0; e < gradients.elements(); e++) {
            (* (accumulated_gradients_[np])) [e] += gradients[e];
          }

          np++;
        }
      }
    }

    // Calculate annealed learning rate
    const datum lr =
      CalculateLR (epoch_ * iterations + i);

    // Apply gradients with new learning rate
    ApplyGradients (lr);
  }

  auto t_end = std::chrono::system_clock::now();
  std::chrono::duration<double> t_diff = t_end - t_begin;
  LOGDEBUG << "Training, sps: " <<
          (datum) (sample_count_ * settings_.sbatchsize
                   * first_training_layer_->GetLossSamplingProbability() * iterations)
          / (datum) t_diff.count();

  LOGDEBUG << "Training, tps: " <<
          1000000.0f * (datum) t_diff.count() /
          (datum) (sample_count_ * settings_.sbatchsize
                   * first_training_layer_->GetLossSamplingProbability() * iterations) << " us";

#ifdef BUILD_OPENCL
  LOGDEBUG << "Training, GB/s   up: " << ((datum)CLHelper::bytes_up)/(1073741824.0 * (datum)t_diff.count());
  LOGDEBUG << "Training, GB/s down: " << ((datum)CLHelper::bytes_down)/(1073741824.0 * (datum)t_diff.count());
  CLHelper::bytes_up = 0;
  CLHelper::bytes_down = 0;
#endif

  // Display training epoch_error
	for (unsigned int n = 0; n < graph_.GetLossNodes().size(); n++) {
		LossFunctionLayer* lossfunction_layer = dynamic_cast<LossFunctionLayer*>(graph_.GetLossNodes()[n]->layer);
		LOGINFO << "Training (Epoch " << epoch_ << ", node " << n << ") " << graph_.GetLossNodes()[n]->layer->GetLayerDescription() <<  " lps: " << loss_sums[n] / (datum)(iterations * sample_count_ * settings_.sbatchsize * first_training_layer_->GetLossSamplingProbability());
	}
	LOGINFO << "Training (Epoch " << epoch_ << ") aggregate lps: " << aggregate_loss / (datum)(iterations * sample_count_ * settings_.sbatchsize * first_training_layer_->GetLossSamplingProbability());

	for (unsigned int n = 0; n < graph_.GetStatNodes().size(); n++) {
		StatLayer* stat_layer = dynamic_cast<StatLayer*>(graph_.GetStatNodes()[n]->layer);
    std::stringstream epochname;
    epochname << "Training  - Epoch " << epoch_ << " -";
    stat_layer->Print (epochname.str(), true);
    stat_layer->Reset();
	}

  delete[] loss_sums;
  epoch_++;
}

void Trainer::ApplyGradients (datum lr) {
  unsigned int dp = 0;

	for (unsigned int l = 0; l < graph_.GetNodes().size(); l++) {
		Layer* const layer = graph_.GetNodes()[l]->layer;
    datum layer_lr;
    switch (settings_.optimization_method) {
      case GRADIENT_DESCENT:
        layer_lr = layer->local_lr_;
        break;
      case QUICKPROP:
        layer_lr = layer->local_lr_;
        break;
    }

    for (unsigned int p = 0; p < layer->parameters().size(); p++) {
      CombinedTensor* const param = layer->parameters_[p];
#ifdef BUILD_OPENCL
      param->data.MoveToCPU();
#endif

      for (unsigned int w = 0; w < param->data.elements(); w++) {
        const datum weight = param->data (w);
        const datum l1_gradient = (weight > 0) - (weight < 0);
        const datum l2_gradient = weight;
        const datum w_gradient = (*accumulated_gradients_[dp]) [w];

        /*
         * http://www.iro.umontreal.ca/~pift6266/H10/notes/gradient.html
         *
         * This site says that one should average the gradient over
         * the minibatch
         */
        datum delta =

          // Average of gradient over minibatch
          layer_lr * (w_gradient / (datum) (sample_count_ * settings_.sbatchsize)) +
          // Regularization
          layer_lr * (settings_.l2_weight * l2_gradient + settings_.l1_weight * l1_gradient);

        // This is needed for both methods
        const datum last_step = (*last_deltas_[dp]) (w);

        switch (settings_.optimization_method) {
          case GRADIENT_DESCENT:
          {
            const datum step = lr * delta + settings_.momentum * last_step;
            param->data[w] -= step;

            // Backup delta
            (*last_deltas_[dp]) [w] = step;
          }
            break;
          case QUICKPROP:
          {
            const datum last_gradient = (*last_gradients_[dp]) (w);
            const datum s = settings_.mu / (1.0 + settings_.mu);

            datum step = 0;
            if(last_step > 0.001) {
              if(delta > 0.0) {
                step += lr * settings_.eta * delta;
              }

              if(delta > (s * last_gradient)) {
                step += settings_.mu * last_step;
              } else {
                step += last_step * delta / (last_gradient - delta);
              }

            } else if(last_step < -0.001) {
              if(delta < 0.0) {
                step += lr * settings_.eta * delta;
              }

              if(delta < (s * last_gradient)) {
                step += settings_.mu * last_step;
              } else {
                step += last_step * delta / (last_gradient - delta);
              }
            } else {
              step += lr * settings_.eta * delta;
            }

            if(step > 1000 || step < -1000) {
              if(step>1000)
                step=1000;
              else
                step=-1000;
            }

            param->data[w] -= step;

            // Backup steps and gradient
            (*last_deltas_[dp]) [w] = step;
            (*last_gradients_[dp]) [w] = delta;
          }
            break;
        }
      }

      dp++;
    }
  }
}

std::ostream& operator<< (std::ostream & output,
                          const TrainerSettings settings) {
  output << "LR: " << settings.learning_rate << ", ";
  output << "GM: " << settings.gamma << ", ";
  output << "EX: " << settings.exponent << ", ";
  output << "SB: " << settings.sbatchsize << ", ";
  output << "PB: " << settings.pbatchsize << ", ";
  output << "L1: " << settings.l1_weight << ", ";
  output << "L2: " << settings.l2_weight << ", ";
  output << "MM: " << settings.momentum << ", ";
  output << "MU: " << settings.mu << ", ";
  output << "ET: " << settings.eta << ", ";
  switch (settings.optimization_method) {
    case GRADIENT_DESCENT:
      output << "GD";
      break;
    case QUICKPROP:
      output << "QP";
      break;
  }
  return output;
}

}
<|endoftext|>


<commit_before>#include "output_hls.h"
#include <mist/stream.h>
#include <unistd.h>

namespace Mist {
  ///\brief Builds an index file for HTTP Live streaming.
  ///\return The index file for HTTP Live Streaming.
  std::string OutHLS::liveIndex(){
    std::stringstream result;
    result << "#EXTM3U\r\n";
    int audioId = -1;
    std::string audioName;
    for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
      if (it->second.codec == "AAC"){
        audioId = it->first;
        audioName = it->second.getIdentifier();
        break;
      }
    }
    unsigned int vidTracks = 0;
    for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
      if (it->second.codec == "H264"){
        vidTracks++;
        int bWidth = it->second.bps * 2;
        if (bWidth < 5){
          bWidth = 5;
        }
        if (audioId != -1){
          bWidth += myMeta.tracks[audioId].bps * 2;
        }
        result << "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" << bWidth * 10 << "\r\n";
        result << it->first;
        if (audioId != -1){
          result << "_" << audioId;
        }
        result << "/index.m3u8\r\n";
      }
    }
    if (!vidTracks && audioId){
      result << "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" << myMeta.tracks[audioId].bps * 20 << "\r\n";
      result << audioId << "/index.m3u8\r\n";
    }
    DEBUG_MSG(DLVL_HIGH, "Sending this index: %s", result.str().c_str());
    return result.str();
  }

  std::string OutHLS::liveIndex(int tid){
    updateMeta();
    std::stringstream result;
    //parse single track
    int longestFragment = 0;
    if (!myMeta.tracks[tid].fragments.size()){
      DEBUG_MSG(DLVL_FAIL, "liveIndex called with track %d, which has no fragments!", tid);
      return "";
    }
    for (std::deque<DTSC::Fragment>::iterator it = myMeta.tracks[tid].fragments.begin(); (it + 1) != myMeta.tracks[tid].fragments.end(); it++){
      if (it->getDuration() > longestFragment){
        longestFragment = it->getDuration();
      }
    }
    if ((myMeta.tracks[tid].lastms - myMeta.tracks[tid].firstms) / myMeta.tracks[tid].fragments.size() > longestFragment){
      longestFragment = (myMeta.tracks[tid].lastms - myMeta.tracks[tid].firstms) / myMeta.tracks[tid].fragments.size();
    }
    result << "#EXTM3U\r\n#EXT-X-TARGETDURATION:" << (longestFragment / 1000) + 1 << "\r\n";

    std::deque<std::string> lines;
    for (std::deque<DTSC::Fragment>::iterator it = myMeta.tracks[tid].fragments.begin(); it != myMeta.tracks[tid].fragments.end(); it++){
      long long int starttime = myMeta.tracks[tid].getKey(it->getNumber()).getTime();
      std::stringstream line;
      long long duration = it->getDuration();
      if (duration <= 0){
        duration = myMeta.tracks[tid].lastms - starttime;
      }
      line << "#EXTINF:" << ((duration + 500) / 1000) << ", no desc\r\n" << starttime << "_" << duration + starttime << ".ts\r\n";
      lines.push_back(line.str());
    }

    //skip the first fragment if live and there are more than 2 fragments.
    unsigned int skippedLines = 0;
    if (myMeta.live){
      if (lines.size() > 2){
        lines.pop_front();
        skippedLines++;
      }
      //only print the last segment when VoD
      lines.pop_back();
    }

    result << "#EXT-X-MEDIA-SEQUENCE:" << myMeta.tracks[tid].missedFrags + skippedLines << "\r\n";

    while (lines.size()){
      result << lines.front();
      lines.pop_front();
    }
    if ( !myMeta.live){
      result << "#EXT-X-ENDLIST\r\n";
    }
    DEBUG_MSG(DLVL_HIGH, "Sending this index: %s", result.str().c_str());
    return result.str();
  } //liveIndex

  OutHLS::OutHLS(Socket::Connection & conn) : TSOutput(conn){
    realTime = 0;
  }

  OutHLS::~OutHLS() {}

  void OutHLS::init(Util::Config * cfg){
    HTTPOutput::init(cfg);
    capa["name"] = "HLS";
    capa["desc"] = "Enables HTTP protocol Apple-specific streaming (also known as HLS).";
    capa["url_rel"] = "/hls/$/index.m3u8";
    capa["url_prefix"] = "/hls/$/";
    capa["codecs"][0u][0u].append("H264");
    capa["codecs"][0u][1u].append("AAC");
    capa["codecs"][0u][1u].append("MP3");
    capa["methods"][0u]["handler"] = "http";
    capa["methods"][0u]["type"] = "html5/application/vnd.apple.mpegurl";
    capa["methods"][0u]["priority"] = 9ll;
  }

  int OutHLS::canSeekms(unsigned int ms){
    //no tracks? Frame too new by definition.
    if ( !myMeta.tracks.size()){
      return 1;
    }
    //loop trough all the tracks
    for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
      //return "too late" if one track is past this point
      if (ms < it->second.firstms){
        return -1;
      }
      //return "too early" if one track is not yet at this point
      if (ms > it->second.lastms){
        return 1;
      }
    }
    return 0;
  }

  void OutHLS::onHTTP(){
    if (H.url == "/crossdomain.xml"){
      H.Clean();
      H.SetHeader("Content-Type", "text/xml");
      H.SetHeader("Server", "MistServer/" PACKAGE_VERSION);
      H.setCORSHeaders();
      H.SetBody("<?xml version=\"1.0\"?><!DOCTYPE cross-domain-policy SYSTEM \"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd\"><cross-domain-policy><allow-access-from domain=\"*\" /><site-control permitted-cross-domain-policies=\"all\"/></cross-domain-policy>");
      H.SendResponse("200", "OK", myConn);
      H.Clean(); //clean for any possible next requests
      return;
    } //crossdomain.xml

    if (H.method == "OPTIONS"){
      H.Clean();
      H.SetHeader("Content-Type", "application/octet-stream");
      H.SetHeader("Cache-Control", "no-cache");
      H.setCORSHeaders();
      H.SetBody("");
      H.SendResponse("200", "OK", myConn);
      H.Clean();
      return;
    }

    if (H.url.find("hls") == std::string::npos){
      myConn.close();
      return;
    }

    appleCompat = (H.GetHeader("User-Agent").find("iPad") != std::string::npos) || (H.GetHeader("User-Agent").find("iPod") != std::string::npos)|| (H.GetHeader("User-Agent").find("iPhone") != std::string::npos);
    bool VLCworkaround = false;
    if (H.GetHeader("User-Agent").substr(0, 3) == "VLC"){
      std::string vlcver = H.GetHeader("User-Agent").substr(4);
      if (vlcver[0] == '0' || vlcver[0] == '1' || (vlcver[0] == '2' && vlcver[2] < '2')){
        DEBUG_MSG(DLVL_INFO, "Enabling VLC version < 2.2.0 bug workaround.");
        VLCworkaround = true;
      }
    }
    initialize();
    if (H.url.find(".m3u") == std::string::npos){
      std::string tmpStr = H.getUrl().substr(5+streamName.size());
      long long unsigned int from;
      if (sscanf(tmpStr.c_str(), "/%u_%u/%llu_%llu.ts", &vidTrack, &audTrack, &from, &until) != 4){
        if (sscanf(tmpStr.c_str(), "/%u/%llu_%llu.ts", &vidTrack, &from, &until) != 3){
          DEBUG_MSG(DLVL_MEDIUM, "Could not parse URL: %s", H.getUrl().c_str());
          H.Clean();
          H.SetBody("The HLS URL wasn't understood - what did you want, exactly?\n");
          myConn.SendNow(H.BuildResponse("404", "URL mismatch"));
          H.Clean(); //clean for any possible next requests
          return;
        }else{
          selectedTracks.clear();
          selectedTracks.insert(vidTrack);
        }
      }else{
        selectedTracks.clear();
        selectedTracks.insert(vidTrack);
        selectedTracks.insert(audTrack);
      }

      if (myMeta.live){
        unsigned int timeout = 0;
        int seekable;
        do {
          seekable = canSeekms(from);
          /// \todo Detection of out-of-range parts.
          if (seekable > 0){
            //time out after 21 seconds
            if (++timeout > 42){
              myConn.close();
              break;
            }
            Util::sleep(500);
            updateMeta();
          }
        }while (myConn && seekable > 0);
        if (seekable < 0){
          H.Clean();
          H.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
          myConn.SendNow(H.BuildResponse("412", "Fragment out of range"));
          H.Clean(); //clean for any possible next requests
          DEBUG_MSG(DLVL_WARN, "Fragment @ %llu too old", from);
          return;
        }
      }

      seek(from);
      ts_from = from;
      lastVid = from * 90;

      H.SetHeader("Content-Type", "video/mp2t");
      H.setCORSHeaders();
      H.StartResponse(H, myConn, VLCworkaround);

      unsigned int fragCounter = myMeta.tracks[vidTrack].missedFrags;
      for (std::deque<DTSC::Fragment>::iterator it = myMeta.tracks[vidTrack].fragments.begin(); it != myMeta.tracks[vidTrack].fragments.end(); it++){
        long long int starttime = myMeta.tracks[vidTrack].getKey(it->getNumber()).getTime();
        if (starttime <= from && starttime + it->getDuration() > from){
          EXTREME_MSG("setting continuity counter for PAT/PMT to %d",fragCounter);
          contCounters[0]=fragCounter;     //PAT continuity counter
          contCounters[4096]=fragCounter;  //PMT continuity counter
          break;
        }
        ++fragCounter;
      }
      packCounter = 0;
      parseData = true;
      wantRequest = false;
    }else{
      initialize();
      std::string request = H.url.substr(H.url.find("/", 5) + 1);
      H.Clean();
      if (H.url.find(".m3u8") != std::string::npos){
        H.SetHeader("Content-Type", "audio/x-mpegurl");
      }else{
        H.SetHeader("Content-Type", "audio/mpegurl");
      }
      H.SetHeader("Cache-Control", "no-cache");
      H.setCORSHeaders();
      std::string manifest;
      if (request.find("/") == std::string::npos){
        manifest = liveIndex();
      }else{
        int selectId = atoi(request.substr(0,request.find("/")).c_str());
        manifest = liveIndex(selectId);
      }
      H.SetBody(manifest);
      H.SendResponse("200", "OK", myConn);
    }
  }

  void OutHLS::sendTS(const char * tsData, unsigned int len){
    H.Chunkify(tsData, len, myConn);
  }
}
<commit_msg>Improved HLS bandwidth accuracy.<commit_after>#include "output_hls.h"
#include <mist/stream.h>
#include <unistd.h>

namespace Mist {
  ///\brief Builds an index file for HTTP Live streaming.
  ///\return The index file for HTTP Live Streaming.
  std::string OutHLS::liveIndex(){
    std::stringstream result;
    result << "#EXTM3U\r\n";
    int audioId = -1;
    std::string audioName;
    for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
      if (it->second.codec == "AAC"){
        audioId = it->first;
        audioName = it->second.getIdentifier();
        break;
      }
    }
    unsigned int vidTracks = 0;
    for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
      if (it->second.codec == "H264"){
        vidTracks++;
        int bWidth = it->second.bps;
        if (bWidth < 5){
          bWidth = 5;
        }
        if (audioId != -1){
          bWidth += myMeta.tracks[audioId].bps;
        }
        result << "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" << (bWidth * 8) << "\r\n";
        result << it->first;
        if (audioId != -1){
          result << "_" << audioId;
        }
        result << "/index.m3u8\r\n";
      }
    }
    if (!vidTracks && audioId){
      result << "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" << (myMeta.tracks[audioId].bps * 8) << "\r\n";
      result << audioId << "/index.m3u8\r\n";
    }
    DEBUG_MSG(DLVL_HIGH, "Sending this index: %s", result.str().c_str());
    return result.str();
  }

  std::string OutHLS::liveIndex(int tid){
    updateMeta();
    std::stringstream result;
    //parse single track
    int longestFragment = 0;
    if (!myMeta.tracks[tid].fragments.size()){
      DEBUG_MSG(DLVL_FAIL, "liveIndex called with track %d, which has no fragments!", tid);
      return "";
    }
    for (std::deque<DTSC::Fragment>::iterator it = myMeta.tracks[tid].fragments.begin(); (it + 1) != myMeta.tracks[tid].fragments.end(); it++){
      if (it->getDuration() > longestFragment){
        longestFragment = it->getDuration();
      }
    }
    if ((myMeta.tracks[tid].lastms - myMeta.tracks[tid].firstms) / myMeta.tracks[tid].fragments.size() > longestFragment){
      longestFragment = (myMeta.tracks[tid].lastms - myMeta.tracks[tid].firstms) / myMeta.tracks[tid].fragments.size();
    }
    result << "#EXTM3U\r\n#EXT-X-TARGETDURATION:" << (longestFragment / 1000) + 1 << "\r\n";

    std::deque<std::string> lines;
    for (std::deque<DTSC::Fragment>::iterator it = myMeta.tracks[tid].fragments.begin(); it != myMeta.tracks[tid].fragments.end(); it++){
      long long int starttime = myMeta.tracks[tid].getKey(it->getNumber()).getTime();
      std::stringstream line;
      long long duration = it->getDuration();
      if (duration <= 0){
        duration = myMeta.tracks[tid].lastms - starttime;
      }
      line << "#EXTINF:" << ((duration + 500) / 1000) << ", no desc\r\n" << starttime << "_" << duration + starttime << ".ts\r\n";
      lines.push_back(line.str());
    }

    //skip the first fragment if live and there are more than 2 fragments.
    unsigned int skippedLines = 0;
    if (myMeta.live){
      if (lines.size() > 2){
        lines.pop_front();
        skippedLines++;
      }
      //only print the last segment when VoD
      lines.pop_back();
    }

    result << "#EXT-X-MEDIA-SEQUENCE:" << myMeta.tracks[tid].missedFrags + skippedLines << "\r\n";

    while (lines.size()){
      result << lines.front();
      lines.pop_front();
    }
    if ( !myMeta.live){
      result << "#EXT-X-ENDLIST\r\n";
    }
    DEBUG_MSG(DLVL_HIGH, "Sending this index: %s", result.str().c_str());
    return result.str();
  } //liveIndex

  OutHLS::OutHLS(Socket::Connection & conn) : TSOutput(conn){
    realTime = 0;
  }

  OutHLS::~OutHLS() {}

  void OutHLS::init(Util::Config * cfg){
    HTTPOutput::init(cfg);
    capa["name"] = "HLS";
    capa["desc"] = "Enables HTTP protocol Apple-specific streaming (also known as HLS).";
    capa["url_rel"] = "/hls/$/index.m3u8";
    capa["url_prefix"] = "/hls/$/";
    capa["codecs"][0u][0u].append("H264");
    capa["codecs"][0u][1u].append("AAC");
    capa["codecs"][0u][1u].append("MP3");
    capa["methods"][0u]["handler"] = "http";
    capa["methods"][0u]["type"] = "html5/application/vnd.apple.mpegurl";
    capa["methods"][0u]["priority"] = 9ll;
  }

  int OutHLS::canSeekms(unsigned int ms){
    //no tracks? Frame too new by definition.
    if ( !myMeta.tracks.size()){
      return 1;
    }
    //loop trough all the tracks
    for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
      //return "too late" if one track is past this point
      if (ms < it->second.firstms){
        return -1;
      }
      //return "too early" if one track is not yet at this point
      if (ms > it->second.lastms){
        return 1;
      }
    }
    return 0;
  }

  void OutHLS::onHTTP(){
    if (H.url == "/crossdomain.xml"){
      H.Clean();
      H.SetHeader("Content-Type", "text/xml");
      H.SetHeader("Server", "MistServer/" PACKAGE_VERSION);
      H.setCORSHeaders();
      H.SetBody("<?xml version=\"1.0\"?><!DOCTYPE cross-domain-policy SYSTEM \"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd\"><cross-domain-policy><allow-access-from domain=\"*\" /><site-control permitted-cross-domain-policies=\"all\"/></cross-domain-policy>");
      H.SendResponse("200", "OK", myConn);
      H.Clean(); //clean for any possible next requests
      return;
    } //crossdomain.xml

    if (H.method == "OPTIONS"){
      H.Clean();
      H.SetHeader("Content-Type", "application/octet-stream");
      H.SetHeader("Cache-Control", "no-cache");
      H.setCORSHeaders();
      H.SetBody("");
      H.SendResponse("200", "OK", myConn);
      H.Clean();
      return;
    }

    if (H.url.find("hls") == std::string::npos){
      myConn.close();
      return;
    }

    appleCompat = (H.GetHeader("User-Agent").find("iPad") != std::string::npos) || (H.GetHeader("User-Agent").find("iPod") != std::string::npos)|| (H.GetHeader("User-Agent").find("iPhone") != std::string::npos);
    bool VLCworkaround = false;
    if (H.GetHeader("User-Agent").substr(0, 3) == "VLC"){
      std::string vlcver = H.GetHeader("User-Agent").substr(4);
      if (vlcver[0] == '0' || vlcver[0] == '1' || (vlcver[0] == '2' && vlcver[2] < '2')){
        DEBUG_MSG(DLVL_INFO, "Enabling VLC version < 2.2.0 bug workaround.");
        VLCworkaround = true;
      }
    }
    initialize();
    if (H.url.find(".m3u") == std::string::npos){
      std::string tmpStr = H.getUrl().substr(5+streamName.size());
      long long unsigned int from;
      if (sscanf(tmpStr.c_str(), "/%u_%u/%llu_%llu.ts", &vidTrack, &audTrack, &from, &until) != 4){
        if (sscanf(tmpStr.c_str(), "/%u/%llu_%llu.ts", &vidTrack, &from, &until) != 3){
          DEBUG_MSG(DLVL_MEDIUM, "Could not parse URL: %s", H.getUrl().c_str());
          H.Clean();
          H.SetBody("The HLS URL wasn't understood - what did you want, exactly?\n");
          myConn.SendNow(H.BuildResponse("404", "URL mismatch"));
          H.Clean(); //clean for any possible next requests
          return;
        }else{
          selectedTracks.clear();
          selectedTracks.insert(vidTrack);
        }
      }else{
        selectedTracks.clear();
        selectedTracks.insert(vidTrack);
        selectedTracks.insert(audTrack);
      }

      if (myMeta.live){
        unsigned int timeout = 0;
        int seekable;
        do {
          seekable = canSeekms(from);
          /// \todo Detection of out-of-range parts.
          if (seekable > 0){
            //time out after 21 seconds
            if (++timeout > 42){
              myConn.close();
              break;
            }
            Util::sleep(500);
            updateMeta();
          }
        }while (myConn && seekable > 0);
        if (seekable < 0){
          H.Clean();
          H.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
          myConn.SendNow(H.BuildResponse("412", "Fragment out of range"));
          H.Clean(); //clean for any possible next requests
          DEBUG_MSG(DLVL_WARN, "Fragment @ %llu too old", from);
          return;
        }
      }

      seek(from);
      ts_from = from;
      lastVid = from * 90;

      H.SetHeader("Content-Type", "video/mp2t");
      H.setCORSHeaders();
      H.StartResponse(H, myConn, VLCworkaround);

      unsigned int fragCounter = myMeta.tracks[vidTrack].missedFrags;
      for (std::deque<DTSC::Fragment>::iterator it = myMeta.tracks[vidTrack].fragments.begin(); it != myMeta.tracks[vidTrack].fragments.end(); it++){
        long long int starttime = myMeta.tracks[vidTrack].getKey(it->getNumber()).getTime();
        if (starttime <= from && starttime + it->getDuration() > from){
          EXTREME_MSG("setting continuity counter for PAT/PMT to %d",fragCounter);
          contCounters[0]=fragCounter;     //PAT continuity counter
          contCounters[4096]=fragCounter;  //PMT continuity counter
          break;
        }
        ++fragCounter;
      }
      packCounter = 0;
      parseData = true;
      wantRequest = false;
    }else{
      initialize();
      std::string request = H.url.substr(H.url.find("/", 5) + 1);
      H.Clean();
      if (H.url.find(".m3u8") != std::string::npos){
        H.SetHeader("Content-Type", "audio/x-mpegurl");
      }else{
        H.SetHeader("Content-Type", "audio/mpegurl");
      }
      H.SetHeader("Cache-Control", "no-cache");
      H.setCORSHeaders();
      std::string manifest;
      if (request.find("/") == std::string::npos){
        manifest = liveIndex();
      }else{
        int selectId = atoi(request.substr(0,request.find("/")).c_str());
        manifest = liveIndex(selectId);
      }
      H.SetBody(manifest);
      H.SendResponse("200", "OK", myConn);
    }
  }

  void OutHLS::sendTS(const char * tsData, unsigned int len){
    H.Chunkify(tsData, len, myConn);
  }
}
<|endoftext|>


<commit_before>#include "output_hls.h"
#include <mist/stream.h>
#include <unistd.h>

namespace Mist {
  ///\brief Builds an index file for HTTP Live streaming.
  ///\return The index file for HTTP Live Streaming.
  std::string OutHLS::liveIndex(){
    std::stringstream result;
    result << "#EXTM3U\r\n";
    int audioId = -1;
    std::string audioName;
    for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
      if (it->second.codec == "AAC"){
        audioId = it->first;
        audioName = it->second.getIdentifier();
        break;
      }
    }
    for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
      if (it->second.codec == "H264"){
        int bWidth = it->second.bps * 2;
        if (audioId != -1){
          bWidth += myMeta.tracks[audioId].bps * 2;
        }
        result << "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" << bWidth * 10 << "\r\n";
        result << it->first;
        if (audioId != -1){
          result << "_" << audioId;
        }
        result << "/index.m3u8\r\n";
      }
    }
    DEBUG_MSG(DLVL_HIGH, "Sending this index: %s", result.str().c_str());
    return result.str();
  }

  std::string OutHLS::liveIndex(int tid){
    updateMeta();
    std::stringstream result;
    //parse single track
    int longestFragment = 0;
    if (!myMeta.tracks[tid].fragments.size()){
      DEBUG_MSG(DLVL_FAIL, "liveIndex called with track %d, which has no fragments!", tid);
      return "";
    }
    for (std::deque<DTSC::Fragment>::iterator it = myMeta.tracks[tid].fragments.begin(); (it + 1) != myMeta.tracks[tid].fragments.end(); it++){
      if (it->getDuration() > longestFragment){
        longestFragment = it->getDuration();
      }
    }
    result << "#EXTM3U\r\n#EXT-X-TARGETDURATION:" << (longestFragment / 1000) + 1 << "\r\n";

    std::deque<std::string> lines;
    for (std::deque<DTSC::Fragment>::iterator it = myMeta.tracks[tid].fragments.begin(); it != myMeta.tracks[tid].fragments.end(); it++){
      long long int starttime = myMeta.tracks[tid].getKey(it->getNumber()).getTime();
      std::stringstream line;
      long long duration = it->getDuration();
      if (duration < 0){
        duration = myMeta.tracks[tid].lastms - starttime;
      }
      line << "#EXTINF:" << ((duration + 500) / 1000) << ", no desc\r\n" << starttime << "_" << duration + starttime << ".ts\r\n";
      lines.push_back(line.str());
    }

    //skip the first fragment if live and there are more than 2 fragments.
    unsigned int skippedLines = 0;
    if (myMeta.live){
      if (lines.size() > 2){
        lines.pop_front();
        skippedLines++;
      }
      //only print the last segment when VoD
      lines.pop_back();
    }

    result << "#EXT-X-MEDIA-SEQUENCE:" << myMeta.tracks[tid].missedFrags + skippedLines << "\r\n";

    while (lines.size()){
      result << lines.front();
      lines.pop_front();
    }
    if ( !myMeta.live){
      result << "#EXT-X-ENDLIST\r\n";
    }
#if DEBUG >= 8
    std::cerr << "Sending this index:" << std::endl << result.str() << std::endl;
#endif
    return result.str();
  } //liveIndex

  OutHLS::OutHLS(Socket::Connection & conn) : HTTPOutput(conn) {
    haveAvcc = false;
    realTime = 0;
    myConn.setBlocking(true);
  }

  OutHLS::~OutHLS() {}

  void OutHLS::init(Util::Config * cfg){
    HTTPOutput::init(cfg);
    capa["name"] = "HLS";
    capa["desc"] = "Enables HTTP protocol Apple-specific streaming (also known as HLS).";
    capa["url_rel"] = "/hls/$/index.m3u8";
    capa["url_prefix"] = "/hls/$/";
    capa["codecs"][0u][0u].append("H264");
    capa["codecs"][0u][1u].append("AAC");
    capa["codecs"][0u][1u].append("MP3");
    capa["methods"][0u]["handler"] = "http";
    capa["methods"][0u]["type"] = "html5/application/vnd.apple.mpegurl";
    capa["methods"][0u]["priority"] = 9ll;
  }

  void OutHLS::fillPacket(bool & first, const char * data, size_t dataLen, char & ContCounter){
    static std::map<int, int> contCounter;
    static unsigned int lastPCR = 0;
    if (!PackData.BytesFree()){
      if (PacketNumber % 42 == 0){
        TS::Packet tmpPack;
        tmpPack.FromPointer(TS::PAT);
        tmpPack.ContinuityCounter(++contCounter[tmpPack.PID()]);
        H.Chunkify(tmpPack.ToString(), 188, myConn);
        tmpPack.FromPointer(TS::createPMT(selectedTracks, myMeta).c_str());
        tmpPack.ContinuityCounter(++contCounter[tmpPack.PID()]);
        H.Chunkify(tmpPack.ToString(), 188, myConn);
        PacketNumber += 2;
      }
      H.Chunkify(PackData.ToString(), 188, myConn);
      PacketNumber ++;
      PackData.Clear();
    }

    if (!dataLen){return;}

    if (PackData.BytesFree() == 184){
      PackData.PID(0x100 - 1 + currentPacket.getTrackId());
      PackData.ContinuityCounter(ContCounter++);
      if (first){
        PackData.UnitStart(1);
        if (currentPacket.getInt("keyframe") || currentPacket.getTime() / 70 != lastPCR / 70){
          PackData.RandomAccess(1);
          PackData.PCR(currentPacket.getTime() * 27000);
          lastPCR = currentPacket.getTime();
        }
        first = false;
      }

    }

    int tmp = PackData.FillFree(data, dataLen);
    if (tmp != dataLen){
      fillPacket(first, data+tmp, dataLen-tmp, ContCounter);
    }
  }

  void OutHLS::sendNext(){
    bool first = true;
    char * dataPointer = 0;
    unsigned int dataLen = 0;
    currentPacket.getString("data", dataPointer, dataLen); //data

    if (currentPacket.getTime() >= until){
      stop();
      wantRequest = true;
      parseData = false;
      H.Chunkify("", 0, myConn);
      H.Clean();
      return;
    }

    std::string bs;
    //prepare bufferstring
    if (myMeta.tracks[currentPacket.getTrackId()].type == "video"){
      bs = TS::Packet::getPESVideoLeadIn(0ul, currentPacket.getTime() * 90, currentPacket.getInt("offset") * 90);
      fillPacket(first, bs.data(), bs.size(), VideoCounter);
      if (myMeta.tracks[currentPacket.getTrackId()].codec == "H264"){
        //End of previous nal unit, somehow needed for h264
        fillPacket(first, "\000\000\000\001\011\360", 6, VideoCounter);
      }

      if (currentPacket.getInt("keyframe")){
        if (!haveAvcc){
          avccbox.setPayload(myMeta.tracks[currentPacket.getTrackId()].init);
          haveAvcc = true;
        }
        bs = avccbox.asAnnexB();
        fillPacket(first, bs.data(), bs.size(), VideoCounter);
      }

      unsigned int i = 0;
      while (i + 4 < (unsigned int)dataLen){
        unsigned int ThisNaluSize = (dataPointer[i] << 24) + (dataPointer[i+1] << 16) + (dataPointer[i+2] << 8) + dataPointer[i+3];
        if (ThisNaluSize + i + 4 > (unsigned int)dataLen){
          DEBUG_MSG(DLVL_WARN, "Too big NALU detected (%u > %d) - skipping!", ThisNaluSize + i + 4, dataLen);
          break;
        }
        fillPacket(first, "\000\000\000\001",4, VideoCounter);
        fillPacket(first, dataPointer+i+4,ThisNaluSize, VideoCounter);
        i += ThisNaluSize+4;
      }
      if (PackData.BytesFree() < 184){
        PackData.AddStuffing();
        fillPacket(first, 0, 0, VideoCounter);
      }
    }else if (myMeta.tracks[currentPacket.getTrackId()].type == "audio"){
      long long unsigned int tempTime;
      if (AppleCompat){
        tempTime = lastVid;
      }else{
        tempTime = currentPacket.getTime() * 90;
      }
      long unsigned int tempLen = dataLen;
      if ( myMeta.tracks[currentPacket.getTrackId()].codec == "AAC"){
        tempLen += 7;
      }
      bs = TS::Packet::getPESAudioLeadIn(tempLen, tempTime);
      fillPacket(first, bs.data(), bs.size(), AudioCounter);
      if (myMeta.tracks[currentPacket.getTrackId()].codec == "AAC"){
        bs = TS::GetAudioHeader(dataLen, myMeta.tracks[currentPacket.getTrackId()].init);
        fillPacket(first, bs.data(), bs.size(), AudioCounter);
      }
      fillPacket(first, dataPointer,dataLen, AudioCounter);
      if (PackData.BytesFree() < 184){
        PackData.AddStuffing();
        fillPacket(first, 0, 0, AudioCounter);
      }
    }
  }

  int OutHLS::canSeekms(unsigned int ms){
    //no tracks? Frame too new by definition.
    if ( !myMeta.tracks.size()){
      return 1;
    }
    //loop trough all the tracks
    for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
      //return "too late" if one track is past this point
      if (ms < it->second.firstms){
        return -1;
      }
      //return "too early" if one track is not yet at this point
      if (ms > it->second.lastms){
        return 1;
      }
    }
    return 0;
  }

  void OutHLS::onHTTP(){
    AppleCompat = (H.GetHeader("User-Agent").find("Apple") != std::string::npos);
    initialize();
    if (H.url.find(".m3u") == std::string::npos){
      std::string tmpStr = H.getUrl().substr(5+streamName.size());
      long long unsigned int from;
      if (sscanf(tmpStr.c_str(), "/%u_%u/%llu_%llu.ts", &vidTrack, &audTrack, &from, &until) != 4){
        if (sscanf(tmpStr.c_str(), "/%u/%llu_%llu.ts", &vidTrack, &from, &until) != 3){
          DEBUG_MSG(DLVL_MEDIUM, "Could not parse URL: %s", H.getUrl().c_str());
          H.Clean();
          H.SetBody("The HLS URL wasn't understood - what did you want, exactly?\n");
          myConn.SendNow(H.BuildResponse("404", "URL mismatch"));
          H.Clean(); //clean for any possible next requests
          return;
        }else{
          selectedTracks.clear();
          selectedTracks.insert(vidTrack);
        }
      }else{
        selectedTracks.clear();
        selectedTracks.insert(vidTrack);
        selectedTracks.insert(audTrack);
      }

      if (myMeta.live){
        unsigned int timeout = 0;
        int seekable;
        do {
          seekable = canSeekms(from);
          /// \todo Detection of out-of-range parts.
          if (seekable > 0){
            //time out after 21 seconds
            if (++timeout > 42){
              myConn.close();
              break;
            }
            Util::sleep(500);
            updateMeta();
          }
        }while (myConn && seekable > 0);
        if (seekable < 0){
          H.Clean();
          H.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
          myConn.SendNow(H.BuildResponse("412", "Fragment out of range"));
          H.Clean(); //clean for any possible next requests
          DEBUG_MSG(DLVL_WARN, "Fragment @ %llu too old", from);
          return;
        }
      }

      seek(from);
      lastVid = from * 90;

      H.SetHeader("Content-Type", "video/mp2t");
      H.StartResponse(H, myConn);
      PacketNumber = 0;
      parseData = true;
      wantRequest = false;
    }else{
      initialize();
      std::string request = H.url.substr(H.url.find("/", 5) + 1);
      H.Clean();
      if (H.url.find(".m3u8") != std::string::npos){
        H.SetHeader("Content-Type", "audio/x-mpegurl");
      }else{
        H.SetHeader("Content-Type", "audio/mpegurl");
      }
      H.SetHeader("Cache-Control", "no-cache");
      std::string manifest;
      if (request.find("/") == std::string::npos){
        manifest = liveIndex();
      }else{
        int selectId = atoi(request.substr(0,request.find("/")).c_str());
        manifest = liveIndex(selectId);
      }
      H.SetBody(manifest);
      H.SendResponse("200", "OK", myConn);
    }
  }
}
<commit_msg>Revert sending timestamp more often for HLS - actually makes things worse.<commit_after>#include "output_hls.h"
#include <mist/stream.h>
#include <unistd.h>

namespace Mist {
  ///\brief Builds an index file for HTTP Live streaming.
  ///\return The index file for HTTP Live Streaming.
  std::string OutHLS::liveIndex(){
    std::stringstream result;
    result << "#EXTM3U\r\n";
    int audioId = -1;
    std::string audioName;
    for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
      if (it->second.codec == "AAC"){
        audioId = it->first;
        audioName = it->second.getIdentifier();
        break;
      }
    }
    for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
      if (it->second.codec == "H264"){
        int bWidth = it->second.bps * 2;
        if (audioId != -1){
          bWidth += myMeta.tracks[audioId].bps * 2;
        }
        result << "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" << bWidth * 10 << "\r\n";
        result << it->first;
        if (audioId != -1){
          result << "_" << audioId;
        }
        result << "/index.m3u8\r\n";
      }
    }
    DEBUG_MSG(DLVL_HIGH, "Sending this index: %s", result.str().c_str());
    return result.str();
  }

  std::string OutHLS::liveIndex(int tid){
    updateMeta();
    std::stringstream result;
    //parse single track
    int longestFragment = 0;
    if (!myMeta.tracks[tid].fragments.size()){
      DEBUG_MSG(DLVL_FAIL, "liveIndex called with track %d, which has no fragments!", tid);
      return "";
    }
    for (std::deque<DTSC::Fragment>::iterator it = myMeta.tracks[tid].fragments.begin(); (it + 1) != myMeta.tracks[tid].fragments.end(); it++){
      if (it->getDuration() > longestFragment){
        longestFragment = it->getDuration();
      }
    }
    result << "#EXTM3U\r\n#EXT-X-TARGETDURATION:" << (longestFragment / 1000) + 1 << "\r\n";

    std::deque<std::string> lines;
    for (std::deque<DTSC::Fragment>::iterator it = myMeta.tracks[tid].fragments.begin(); it != myMeta.tracks[tid].fragments.end(); it++){
      long long int starttime = myMeta.tracks[tid].getKey(it->getNumber()).getTime();
      std::stringstream line;
      long long duration = it->getDuration();
      if (duration < 0){
        duration = myMeta.tracks[tid].lastms - starttime;
      }
      line << "#EXTINF:" << ((duration + 500) / 1000) << ", no desc\r\n" << starttime << "_" << duration + starttime << ".ts\r\n";
      lines.push_back(line.str());
    }

    //skip the first fragment if live and there are more than 2 fragments.
    unsigned int skippedLines = 0;
    if (myMeta.live){
      if (lines.size() > 2){
        lines.pop_front();
        skippedLines++;
      }
      //only print the last segment when VoD
      lines.pop_back();
    }

    result << "#EXT-X-MEDIA-SEQUENCE:" << myMeta.tracks[tid].missedFrags + skippedLines << "\r\n";

    while (lines.size()){
      result << lines.front();
      lines.pop_front();
    }
    if ( !myMeta.live){
      result << "#EXT-X-ENDLIST\r\n";
    }
#if DEBUG >= 8
    std::cerr << "Sending this index:" << std::endl << result.str() << std::endl;
#endif
    return result.str();
  } //liveIndex

  OutHLS::OutHLS(Socket::Connection & conn) : HTTPOutput(conn) {
    haveAvcc = false;
    realTime = 0;
    myConn.setBlocking(true);
  }

  OutHLS::~OutHLS() {}

  void OutHLS::init(Util::Config * cfg){
    HTTPOutput::init(cfg);
    capa["name"] = "HLS";
    capa["desc"] = "Enables HTTP protocol Apple-specific streaming (also known as HLS).";
    capa["url_rel"] = "/hls/$/index.m3u8";
    capa["url_prefix"] = "/hls/$/";
    capa["codecs"][0u][0u].append("H264");
    capa["codecs"][0u][1u].append("AAC");
    capa["codecs"][0u][1u].append("MP3");
    capa["methods"][0u]["handler"] = "http";
    capa["methods"][0u]["type"] = "html5/application/vnd.apple.mpegurl";
    capa["methods"][0u]["priority"] = 9ll;
  }

  void OutHLS::fillPacket(bool & first, const char * data, size_t dataLen, char & ContCounter){
    static std::map<int, int> contCounter;
    if (!PackData.BytesFree()){
      if (PacketNumber % 42 == 0){
        TS::Packet tmpPack;
        tmpPack.FromPointer(TS::PAT);
        tmpPack.ContinuityCounter(++contCounter[tmpPack.PID()]);
        H.Chunkify(tmpPack.ToString(), 188, myConn);
        tmpPack.FromPointer(TS::createPMT(selectedTracks, myMeta).c_str());
        tmpPack.ContinuityCounter(++contCounter[tmpPack.PID()]);
        H.Chunkify(tmpPack.ToString(), 188, myConn);
        PacketNumber += 2;
      }
      H.Chunkify(PackData.ToString(), 188, myConn);
      PacketNumber ++;
      PackData.Clear();
    }

    if (!dataLen){return;}

    if (PackData.BytesFree() == 184){
      PackData.PID(0x100 - 1 + currentPacket.getTrackId());
      PackData.ContinuityCounter(ContCounter++);
      if (first){
        PackData.UnitStart(1);
        if (currentPacket.getInt("keyframe")){
          PackData.RandomAccess(1);
          PackData.PCR(currentPacket.getTime() * 27000);
        }
        first = false;
      }

    }

    int tmp = PackData.FillFree(data, dataLen);
    if (tmp != dataLen){
      fillPacket(first, data+tmp, dataLen-tmp, ContCounter);
    }
  }

  void OutHLS::sendNext(){
    bool first = true;
    char * dataPointer = 0;
    unsigned int dataLen = 0;
    currentPacket.getString("data", dataPointer, dataLen); //data

    if (currentPacket.getTime() >= until){
      stop();
      wantRequest = true;
      parseData = false;
      H.Chunkify("", 0, myConn);
      H.Clean();
      return;
    }

    std::string bs;
    //prepare bufferstring
    if (myMeta.tracks[currentPacket.getTrackId()].type == "video"){
      bs = TS::Packet::getPESVideoLeadIn(0ul, currentPacket.getTime() * 90, currentPacket.getInt("offset") * 90);
      fillPacket(first, bs.data(), bs.size(), VideoCounter);
      if (myMeta.tracks[currentPacket.getTrackId()].codec == "H264"){
        //End of previous nal unit, somehow needed for h264
        fillPacket(first, "\000\000\000\001\011\360", 6, VideoCounter);
      }

      if (currentPacket.getInt("keyframe")){
        if (!haveAvcc){
          avccbox.setPayload(myMeta.tracks[currentPacket.getTrackId()].init);
          haveAvcc = true;
        }
        bs = avccbox.asAnnexB();
        fillPacket(first, bs.data(), bs.size(), VideoCounter);
      }

      unsigned int i = 0;
      while (i + 4 < (unsigned int)dataLen){
        unsigned int ThisNaluSize = (dataPointer[i] << 24) + (dataPointer[i+1] << 16) + (dataPointer[i+2] << 8) + dataPointer[i+3];
        if (ThisNaluSize + i + 4 > (unsigned int)dataLen){
          DEBUG_MSG(DLVL_WARN, "Too big NALU detected (%u > %d) - skipping!", ThisNaluSize + i + 4, dataLen);
          break;
        }
        fillPacket(first, "\000\000\000\001",4, VideoCounter);
        fillPacket(first, dataPointer+i+4,ThisNaluSize, VideoCounter);
        i += ThisNaluSize+4;
      }
      if (PackData.BytesFree() < 184){
        PackData.AddStuffing();
        fillPacket(first, 0, 0, VideoCounter);
      }
    }else if (myMeta.tracks[currentPacket.getTrackId()].type == "audio"){
      long long unsigned int tempTime;
      if (AppleCompat){
        tempTime = lastVid;
      }else{
        tempTime = currentPacket.getTime() * 90;
      }
      long unsigned int tempLen = dataLen;
      if ( myMeta.tracks[currentPacket.getTrackId()].codec == "AAC"){
        tempLen += 7;
      }
      bs = TS::Packet::getPESAudioLeadIn(tempLen, tempTime);
      fillPacket(first, bs.data(), bs.size(), AudioCounter);
      if (myMeta.tracks[currentPacket.getTrackId()].codec == "AAC"){
        bs = TS::GetAudioHeader(dataLen, myMeta.tracks[currentPacket.getTrackId()].init);
        fillPacket(first, bs.data(), bs.size(), AudioCounter);
      }
      fillPacket(first, dataPointer,dataLen, AudioCounter);
      if (PackData.BytesFree() < 184){
        PackData.AddStuffing();
        fillPacket(first, 0, 0, AudioCounter);
      }
    }
  }

  int OutHLS::canSeekms(unsigned int ms){
    //no tracks? Frame too new by definition.
    if ( !myMeta.tracks.size()){
      return 1;
    }
    //loop trough all the tracks
    for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
      //return "too late" if one track is past this point
      if (ms < it->second.firstms){
        return -1;
      }
      //return "too early" if one track is not yet at this point
      if (ms > it->second.lastms){
        return 1;
      }
    }
    return 0;
  }

  void OutHLS::onHTTP(){
    AppleCompat = (H.GetHeader("User-Agent").find("Apple") != std::string::npos);
    initialize();
    if (H.url.find(".m3u") == std::string::npos){
      std::string tmpStr = H.getUrl().substr(5+streamName.size());
      long long unsigned int from;
      if (sscanf(tmpStr.c_str(), "/%u_%u/%llu_%llu.ts", &vidTrack, &audTrack, &from, &until) != 4){
        if (sscanf(tmpStr.c_str(), "/%u/%llu_%llu.ts", &vidTrack, &from, &until) != 3){
          DEBUG_MSG(DLVL_MEDIUM, "Could not parse URL: %s", H.getUrl().c_str());
          H.Clean();
          H.SetBody("The HLS URL wasn't understood - what did you want, exactly?\n");
          myConn.SendNow(H.BuildResponse("404", "URL mismatch"));
          H.Clean(); //clean for any possible next requests
          return;
        }else{
          selectedTracks.clear();
          selectedTracks.insert(vidTrack);
        }
      }else{
        selectedTracks.clear();
        selectedTracks.insert(vidTrack);
        selectedTracks.insert(audTrack);
      }

      if (myMeta.live){
        unsigned int timeout = 0;
        int seekable;
        do {
          seekable = canSeekms(from);
          /// \todo Detection of out-of-range parts.
          if (seekable > 0){
            //time out after 21 seconds
            if (++timeout > 42){
              myConn.close();
              break;
            }
            Util::sleep(500);
            updateMeta();
          }
        }while (myConn && seekable > 0);
        if (seekable < 0){
          H.Clean();
          H.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
          myConn.SendNow(H.BuildResponse("412", "Fragment out of range"));
          H.Clean(); //clean for any possible next requests
          DEBUG_MSG(DLVL_WARN, "Fragment @ %llu too old", from);
          return;
        }
      }

      seek(from);
      lastVid = from * 90;

      H.SetHeader("Content-Type", "video/mp2t");
      H.StartResponse(H, myConn);
      PacketNumber = 0;
      parseData = true;
      wantRequest = false;
    }else{
      initialize();
      std::string request = H.url.substr(H.url.find("/", 5) + 1);
      H.Clean();
      if (H.url.find(".m3u8") != std::string::npos){
        H.SetHeader("Content-Type", "audio/x-mpegurl");
      }else{
        H.SetHeader("Content-Type", "audio/mpegurl");
      }
      H.SetHeader("Cache-Control", "no-cache");
      std::string manifest;
      if (request.find("/") == std::string::npos){
        manifest = liveIndex();
      }else{
        int selectId = atoi(request.substr(0,request.find("/")).c_str());
        manifest = liveIndex(selectId);
      }
      H.SetBody(manifest);
      H.SendResponse("200", "OK", myConn);
    }
  }
}
<|endoftext|>