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

74
static constexpr const char* AVLOGLEVEL = "AVLOGLEVEL";
75

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

    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);
90 91 92
#else
    av_log_set_level(0);
#endif
93 94
}

Philippe Gorley's avatar
Philippe Gorley committed
95 96 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
#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

134 135
static void
init_once()
136
{
137
    av_register_all();
138
    avdevice_register_all();
139
    avformat_network_init();
140

141
    av_lockmgr_register(avcodecManageMutex);
142

Vittorio Giovara's avatar
Vittorio Giovara committed
143
    if (getDebugMode())
144
        setAvLogLevel();
Philippe Gorley's avatar
Philippe Gorley committed
145 146 147 148 149

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

152
static std::once_flag already_called;
153

154
void ring_avcodec_init()
155
{
156
    std::call_once(already_called, init_once);
157 158
}

159

160 161 162
int libav_pixel_format(int fmt)
{
    switch (fmt) {
163 164 165 166 167
        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;
168 169 170 171
    }
    return fmt;
}

172
int ring_pixel_format(int fmt)
173 174
{
    switch (fmt) {
175
        case AV_PIX_FMT_YUYV422: return video::VIDEO_PIXFMT_YUYV422;
176 177 178 179
    }
    return fmt;
}

180
void ring_url_split(const char *url,
181 182 183 184 185 186 187
                   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);
}

188 189 190 191 192 193 194 195 196 197 198 199 200 201
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;
}

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