libav_utils.cpp 7.08 KB
Newer Older
1
/*
Sébastien Blin's avatar
Sébastien Blin committed
2
 *  Copyright (C) 2004-2019 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
 *  Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com>
8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 *  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
21
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
22 23
 */

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

26
#ifdef HAVE_CONFIG_H
Alexandre Lision's avatar
Alexandre Lision committed
27
#include "config.h"
28
#endif
29
#include "video/video_base.h"
30 31
#include "logger.h"

32
#include <vector>
33 34 35
#include <algorithm>
#include <string>
#include <iostream>
36 37 38
#include <thread>
#include <mutex>
#include <exception>
39
#include <ciso646> // fix windows compiler bug
40

41 42 43 44 45 46 47 48 49 50 51 52 53 54
extern "C" {
#if LIBAVUTIL_VERSION_MAJOR < 56
AVFrameSideData*
av_frame_new_side_data_from_buf(AVFrame* frame, enum AVFrameSideDataType type, AVBufferRef* buf)
{
    auto side_data = av_frame_new_side_data(frame, type, 0);
    av_buffer_unref(&side_data->buf);
    side_data->buf = buf;
    side_data->data = side_data->buf->data;
    side_data->size = side_data->buf->size;
}
#endif
}

Guillaume Roguez's avatar
Guillaume Roguez committed
55 56
namespace ring { namespace libav_utils {

57
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
58
// protect libav/ffmpeg access
59 60
static int
avcodecManageMutex(void **data, enum AVLockOp op)
61
{
62
    auto mutex = reinterpret_cast<std::mutex**>(data);
63 64
    int ret = 0;
    switch (op) {
Tristan Matthews's avatar
Tristan Matthews committed
65
        case AV_LOCK_CREATE:
66 67 68
            try {
                *mutex = new std::mutex;
            } catch (const std::bad_alloc& e) {
69
                return AVERROR(ENOMEM);
70
            }
71
            break;
Tristan Matthews's avatar
Tristan Matthews committed
72
        case AV_LOCK_OBTAIN:
73
            (*mutex)->lock();
Tristan Matthews's avatar
Tristan Matthews committed
74 75
            break;
        case AV_LOCK_RELEASE:
76
            (*mutex)->unlock();
Tristan Matthews's avatar
Tristan Matthews committed
77
            break;
78
        case AV_LOCK_DESTROY:
79 80
            delete *mutex;
            *mutex = nullptr;
81 82
            break;
        default:
83
#ifdef AVERROR_BUG
84
            return AVERROR_BUG;
85 86
#else
            break;
87
#endif
88
    }
89
    return AVERROR(ret);
90
}
91
#endif
92

93
static constexpr const char* AVLOGLEVEL = "AVLOGLEVEL";
94

95 96 97
static void
setAvLogLevel()
{
98
#ifndef RING_UWP
99
    char* envvar = getenv(AVLOGLEVEL);
100
    signed level = AV_LOG_WARNING;
101 102 103 104 105 106 107 108

    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);
109 110 111
#else
    av_log_set_level(0);
#endif
112 113
}

Philippe Gorley's avatar
Philippe Gorley committed
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
#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;
    }
Philippe Gorley's avatar
Philippe Gorley committed
149
    __android_log_print(android_level, "FFmpeg", "%s", line);
Philippe Gorley's avatar
Philippe Gorley committed
150 151 152
}
#endif

153 154
static void
init_once()
155
{
156
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
157
    av_register_all();
158
#endif
159
    avdevice_register_all();
160
    avformat_network_init();
161 162 163
#if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7, 13, 100)
    avfilter_register_all();
#endif
164

165
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
166
    av_lockmgr_register(avcodecManageMutex);
167
#endif
168

Vittorio Giovara's avatar
Vittorio Giovara committed
169
    if (getDebugMode())
170
        setAvLogLevel();
Philippe Gorley's avatar
Philippe Gorley committed
171 172 173 174 175

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

178
static std::once_flag already_called;
179

180
void ring_avcodec_init()
181
{
182
    std::call_once(already_called, init_once);
183 184
}

185
void ring_url_split(const char *url,
186 187 188 189 190 191 192
                   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);
}

193 194 195 196 197 198 199 200 201 202 203 204 205 206
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;
}

207 208 209 210 211 212 213 214
std::string
getError(int err)
{
    std::string ret(AV_ERROR_MAX_STRING_SIZE, '\0');
    av_strerror(err, (char*)ret.data(), ret.size());
    return ret;
}

215 216 217 218 219 220 221 222 223 224
const char*
getDictValue(const AVDictionary* d, const std::string& key, int flags)
{
    auto kv = av_dict_get(d, key.c_str(), nullptr, flags);
    if (kv)
        return kv->value;
    else
        return "";
}

225 226 227 228 229 230
void
setDictValue(AVDictionary** d, const std::string& key, const std::string& value, int flags)
{
    av_dict_set(d, key.c_str(), value.c_str(), flags);
}

231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
void
fillWithBlack(AVFrame* frame)
{
    const AVPixelFormat format = static_cast<AVPixelFormat>(frame->format);
    const int planes = av_pix_fmt_count_planes(format);
    // workaround for casting pointers to different sizes
    // on 64 bit machines: sizeof(ptrdiff_t) != sizeof(int)
    ptrdiff_t linesizes[4];
    for (int i = 0; i < planes; ++i)
        linesizes[i] = frame->linesize[i];
    int ret = av_image_fill_black(frame->data, linesizes, format,
        frame->color_range, frame->width, frame->height);
    if (ret < 0) {
        RING_ERR() << "Failed to blacken frame";
    }
}

248 249 250 251 252 253 254 255
void
fillWithSilence(AVFrame* frame)
{
    int ret = av_samples_set_silence(frame->extended_data, 0, frame->nb_samples, frame->channels, (AVSampleFormat)frame->format);
    if (ret < 0)
        RING_ERR() << "Failed to fill frame with silence";
}

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