libav_utils.cpp 5.96 KB
Newer Older
1
/*
Guillaume Roguez's avatar
Guillaume Roguez committed
2
 *  Copyright (C) 2004-2018 Savoir-faire Linux Inc.
Guillaume Roguez's avatar
Guillaume Roguez committed
3
 *
4
 *  Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com>
5
 *  Author: Luca Barbato <lu_zero@gentoo.org>
6
 *  Author: Guillaume Roguez <Guillaume.Roguez@savoirfairelinux.com>
7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
20
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
21 22
 */

23 24
#include "libav_deps.h" // MUST BE INCLUDED FIRST

Alexandre Lision's avatar
Alexandre Lision committed
25
#include "config.h"
26
#include "video/video_base.h"
27 28
#include "logger.h"

29
#include <vector>
30 31 32
#include <algorithm>
#include <string>
#include <iostream>
33 34 35
#include <thread>
#include <mutex>
#include <exception>
36
#include <ciso646> // fix windows compiler bug
37

Guillaume Roguez's avatar
Guillaume Roguez committed
38 39
namespace ring { namespace libav_utils {

40
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
41
// protect libav/ffmpeg access
42 43
static int
avcodecManageMutex(void **data, enum AVLockOp op)
44
{
45
    auto mutex = reinterpret_cast<std::mutex**>(data);
46 47
    int ret = 0;
    switch (op) {
Tristan Matthews's avatar
Tristan Matthews committed
48
        case AV_LOCK_CREATE:
49 50 51
            try {
                *mutex = new std::mutex;
            } catch (const std::bad_alloc& e) {
52
                return AVERROR(ENOMEM);
53
            }
54
            break;
Tristan Matthews's avatar
Tristan Matthews committed
55
        case AV_LOCK_OBTAIN:
56
            (*mutex)->lock();
Tristan Matthews's avatar
Tristan Matthews committed
57 58
            break;
        case AV_LOCK_RELEASE:
59
            (*mutex)->unlock();
Tristan Matthews's avatar
Tristan Matthews committed
60
            break;
61
        case AV_LOCK_DESTROY:
62 63
            delete *mutex;
            *mutex = nullptr;
64 65
            break;
        default:
66
#ifdef AVERROR_BUG
67
            return AVERROR_BUG;
68 69
#else
            break;
70
#endif
71
    }
72
    return AVERROR(ret);
73
}
74
#endif
75

76
static constexpr const char* AVLOGLEVEL = "AVLOGLEVEL";
77

78 79 80
static void
setAvLogLevel()
{
81
#ifndef RING_UWP
82
    char* envvar = getenv(AVLOGLEVEL);
83
    signed level = AV_LOG_WARNING;
84 85 86 87 88 89 90 91

    if (envvar != nullptr) {
        if (not (std::istringstream(envvar) >> level))
            level = AV_LOG_ERROR;

        level = std::max(AV_LOG_QUIET, std::min(level, AV_LOG_DEBUG));
    }
    av_log_set_level(level);
92 93 94
#else
    av_log_set_level(0);
#endif
95 96
}

Philippe Gorley's avatar
Philippe Gorley committed
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
#ifdef __ANDROID__
static void
androidAvLogCb(void* ptr, int level, const char* fmt, va_list vl)
{
    if (level > av_log_get_level())
        return;

    char line[1024];
    int print_prefix = 1;
    int android_level;
    va_list vl2;
    va_copy(vl2, vl);
    av_log_format_line(ptr, level, fmt, vl2, line, sizeof(line), &print_prefix);
    va_end(vl2);

    // replace unprintable characters with '?'
    int idx = 0;
    while (line[idx]) {
        if (line[idx] < 0x08 || (line[idx] > 0x0D && line[idx] < 0x20))
            line[idx] = '?';
        ++idx;
    }

    switch(level) {
        case AV_LOG_QUIET:   android_level = ANDROID_LOG_SILENT;  break;
        case AV_LOG_PANIC:   android_level = ANDROID_LOG_FATAL;   break;
        case AV_LOG_FATAL:   android_level = ANDROID_LOG_FATAL;   break;
        case AV_LOG_ERROR:   android_level = ANDROID_LOG_ERROR;   break;
        case AV_LOG_WARNING: android_level = ANDROID_LOG_WARN;    break;
        case AV_LOG_INFO:    android_level = ANDROID_LOG_INFO;    break;
        case AV_LOG_VERBOSE: android_level = ANDROID_LOG_INFO;    break;
        case AV_LOG_DEBUG:   android_level = ANDROID_LOG_DEBUG;   break;
        case AV_LOG_TRACE:   android_level = ANDROID_LOG_VERBOSE; break;
        default:             android_level = ANDROID_LOG_DEFAULT; break;
    }
    __android_log_print(android_level, "FFmpeg", line);
}
#endif

136 137
static void
init_once()
138
{
139
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
140
    av_register_all();
141
#endif
142
    avdevice_register_all();
143
    avformat_network_init();
144 145 146
#if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7, 13, 100)
    avfilter_register_all();
#endif
147

148
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
149
    av_lockmgr_register(avcodecManageMutex);
150
#endif
151

Vittorio Giovara's avatar
Vittorio Giovara committed
152
    if (getDebugMode())
153
        setAvLogLevel();
Philippe Gorley's avatar
Philippe Gorley committed
154 155 156 157 158

#ifdef __ANDROID__
    // android doesn't like stdout and stderr :(
    av_log_set_callback(androidAvLogCb);
#endif
159 160
}

161
static std::once_flag already_called;
162

163
void ring_avcodec_init()
164
{
165
    std::call_once(already_called, init_once);
166 167
}

168

169 170 171
int libav_pixel_format(int fmt)
{
    switch (fmt) {
172 173 174 175 176
        case video::VIDEO_PIXFMT_BGRA: return AV_PIX_FMT_BGRA;
        case video::VIDEO_PIXFMT_RGBA: return AV_PIX_FMT_RGBA;
        case video::VIDEO_PIXFMT_YUYV422: return AV_PIX_FMT_YUYV422;
        case video::VIDEO_PIXFMT_YUV420P: return AV_PIX_FMT_YUV420P;
        case video::VIDEO_PIXFMT_NV21: return AV_PIX_FMT_NV21;
177 178 179 180
    }
    return fmt;
}

181
int ring_pixel_format(int fmt)
182 183
{
    switch (fmt) {
184
        case AV_PIX_FMT_YUYV422: return video::VIDEO_PIXFMT_YUYV422;
185 186 187 188
    }
    return fmt;
}

189
void ring_url_split(const char *url,
190 191 192 193 194 195 196
                   char *hostname, size_t hostname_size, int *port,
                   char *path, size_t path_size)
{
    av_url_split(NULL, 0, NULL, 0, hostname, hostname_size, port,
                 path, path_size, url);
}

197 198 199 200 201 202 203 204 205 206 207 208 209 210
bool
is_yuv_planar(const AVPixFmtDescriptor& desc)
{
    if (not (desc.flags & AV_PIX_FMT_FLAG_PLANAR) or desc.flags & AV_PIX_FMT_FLAG_RGB)
        return false;

    /* handle formats that do not use all planes */
    unsigned used_bit_mask = (1u << desc.nb_components) - 1;
    for (unsigned i = 0; i < desc.nb_components; ++i)
        used_bit_mask &= ~(1u << desc.comp[i].plane);

    return not used_bit_mask;
}

211 212 213 214 215 216 217 218
std::string
getError(int err)
{
    std::string ret(AV_ERROR_MAX_STRING_SIZE, '\0');
    av_strerror(err, (char*)ret.data(), ret.size());
    return ret;
}

Guillaume Roguez's avatar
Guillaume Roguez committed
219
}} // namespace ring::libav_utils