LCOV - code coverage report
Current view: top level - foo/src/media - libav_utils.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 43 62 69.4 %
Date: 2025-12-18 10:07:43 Functions: 9 11 81.8 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2004-2025 Savoir-faire Linux Inc.
       3             :  *
       4             :  *  This program is free software: you can redistribute it and/or modify
       5             :  *  it under the terms of the GNU General Public License as published by
       6             :  *  the Free Software Foundation, either version 3 of the License, or
       7             :  *  (at your option) any later version.
       8             :  *
       9             :  *  This program is distributed in the hope that it will be useful,
      10             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      12             :  *  GNU General Public License for more details.
      13             :  *
      14             :  *  You should have received a copy of the GNU General Public License
      15             :  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "libav_deps.h" // MUST BE INCLUDED FIRST
      19             : 
      20             : #ifdef HAVE_CONFIG_H
      21             : #include "config.h"
      22             : #endif
      23             : #include "video/video_base.h"
      24             : #include "logger.h"
      25             : 
      26             : #include <vector>
      27             : #include <algorithm>
      28             : #include <string>
      29             : #include <iostream>
      30             : #include <thread>
      31             : #include <mutex>
      32             : #include <exception>
      33             : #include <ciso646> // fix windows compiler bug
      34             : 
      35             : extern "C" {
      36             : #if LIBAVUTIL_VERSION_MAJOR < 56
      37             : AVFrameSideData*
      38             : av_frame_new_side_data_from_buf(AVFrame* frame, enum AVFrameSideDataType type, AVBufferRef* buf)
      39             : {
      40             :     auto side_data = av_frame_new_side_data(frame, type, 0);
      41             :     av_buffer_unref(&side_data->buf);
      42             :     side_data->buf = buf;
      43             :     side_data->data = side_data->buf->data;
      44             :     side_data->size = side_data->buf->size;
      45             :     return side_data;
      46             : }
      47             : #endif
      48             : }
      49             : 
      50             : namespace jami {
      51             : namespace libav_utils {
      52             : 
      53             : AVSampleFormat
      54         186 : choose_sample_fmt(const AVCodec* codec, const AVSampleFormat* preferred_formats, int preferred_formats_count)
      55             : {
      56         186 :     if (codec->sample_fmts)
      57         186 :         for (int i = 0; i < preferred_formats_count; ++i) {
      58         371 :             for (auto it = codec->sample_fmts; *it != -1; ++it) {
      59         371 :                 if (*it == preferred_formats[i])
      60         186 :                     return preferred_formats[i];
      61             :             }
      62             :         }
      63           0 :     return AV_SAMPLE_FMT_NONE;
      64             : }
      65             : 
      66             : AVSampleFormat
      67         186 : choose_sample_fmt_default(const AVCodec* codec, AVSampleFormat defaultFormat)
      68             : {
      69             :     // List of supported formats, current default first
      70         186 :     const AVSampleFormat preferred_formats[] = {defaultFormat,
      71             :                                                 AV_SAMPLE_FMT_FLTP,
      72             :                                                 AV_SAMPLE_FMT_FLT,
      73             :                                                 AV_SAMPLE_FMT_S16P,
      74             :                                                 AV_SAMPLE_FMT_S16,
      75             :                                                 AV_SAMPLE_FMT_DBLP,
      76             :                                                 AV_SAMPLE_FMT_DBL,
      77             :                                                 AV_SAMPLE_FMT_S32P,
      78         186 :                                                 AV_SAMPLE_FMT_S32};
      79         186 :     return choose_sample_fmt(codec, preferred_formats, sizeof(preferred_formats) / sizeof(preferred_formats[0]));
      80             : }
      81             : 
      82             : #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
      83             : // protect libav/ffmpeg access
      84             : static int
      85             : avcodecManageMutex(void** data, enum AVLockOp op)
      86             : {
      87             :     auto mutex = reinterpret_cast<std::mutex**>(data);
      88             :     int ret = 0;
      89             :     switch (op) {
      90             :     case AV_LOCK_CREATE:
      91             :         try {
      92             :             *mutex = new std::mutex;
      93             :         } catch (const std::bad_alloc& e) {
      94             :             return AVERROR(ENOMEM);
      95             :         }
      96             :         break;
      97             :     case AV_LOCK_OBTAIN:
      98             :         (*mutex)->lock();
      99             :         break;
     100             :     case AV_LOCK_RELEASE:
     101             :         (*mutex)->unlock();
     102             :         break;
     103             :     case AV_LOCK_DESTROY:
     104             :         delete *mutex;
     105             :         *mutex = nullptr;
     106             :         break;
     107             :     default:
     108             : #ifdef AVERROR_BUG
     109             :         return AVERROR_BUG;
     110             : #else
     111             :         break;
     112             : #endif
     113             :     }
     114             :     return AVERROR(ret);
     115             : }
     116             : #endif
     117             : 
     118             : static constexpr const char* AVLOGLEVEL = "AVLOGLEVEL";
     119             : 
     120             : static void
     121          38 : setAvLogLevel()
     122             : {
     123          38 :     char* envvar = getenv(AVLOGLEVEL);
     124          38 :     signed level = AV_LOG_WARNING;
     125             : 
     126          38 :     if (envvar != nullptr) {
     127           0 :         level = to_int<int>(envvar, AV_LOG_ERROR);
     128           0 :         level = std::max(AV_LOG_QUIET, std::min(level, AV_LOG_DEBUG));
     129             :     }
     130          38 :     av_log_set_level(level);
     131          38 : }
     132             : 
     133             : #ifdef __ANDROID__
     134             : static void
     135             : androidAvLogCb(void* ptr, int level, const char* fmt, va_list vl)
     136             : {
     137             :     if (level > av_log_get_level())
     138             :         return;
     139             : 
     140             :     char line[1024];
     141             :     int print_prefix = 1;
     142             :     int android_level;
     143             :     va_list vl2;
     144             :     va_copy(vl2, vl);
     145             :     av_log_format_line(ptr, level, fmt, vl2, line, sizeof(line), &print_prefix);
     146             :     va_end(vl2);
     147             : 
     148             :     // replace unprintable characters with '?'
     149             :     int idx = 0;
     150             :     while (line[idx]) {
     151             :         if (line[idx] < 0x08 || (line[idx] > 0x0D && line[idx] < 0x20))
     152             :             line[idx] = '?';
     153             :         ++idx;
     154             :     }
     155             : 
     156             :     switch (level) {
     157             :     case AV_LOG_QUIET:
     158             :         android_level = ANDROID_LOG_SILENT;
     159             :         break;
     160             :     case AV_LOG_PANIC:
     161             :         android_level = ANDROID_LOG_FATAL;
     162             :         break;
     163             :     case AV_LOG_FATAL:
     164             :         android_level = ANDROID_LOG_FATAL;
     165             :         break;
     166             :     case AV_LOG_ERROR:
     167             :         android_level = ANDROID_LOG_ERROR;
     168             :         break;
     169             :     case AV_LOG_WARNING:
     170             :         android_level = ANDROID_LOG_WARN;
     171             :         break;
     172             :     case AV_LOG_INFO:
     173             :         android_level = ANDROID_LOG_INFO;
     174             :         break;
     175             :     case AV_LOG_VERBOSE:
     176             :         android_level = ANDROID_LOG_INFO;
     177             :         break;
     178             :     case AV_LOG_DEBUG:
     179             :         android_level = ANDROID_LOG_DEBUG;
     180             :         break;
     181             :     case AV_LOG_TRACE:
     182             :         android_level = ANDROID_LOG_VERBOSE;
     183             :         break;
     184             :     default:
     185             :         android_level = ANDROID_LOG_DEFAULT;
     186             :         break;
     187             :     }
     188             :     __android_log_print(android_level, "FFmpeg", "%s", line);
     189             : }
     190             : #endif
     191             : 
     192             : static void
     193          38 : init_once()
     194             : {
     195             : #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
     196             :     av_register_all();
     197             : #endif
     198          38 :     avdevice_register_all();
     199          38 :     avformat_network_init();
     200             : #if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7, 13, 100)
     201             :     avfilter_register_all();
     202             : #endif
     203             : 
     204             : #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
     205             :     av_lockmgr_register(avcodecManageMutex);
     206             : #endif
     207             : 
     208          38 :     setAvLogLevel();
     209             : 
     210             : #ifdef __ANDROID__
     211             :     // android doesn't like stdout and stderr :(
     212             :     av_log_set_callback(androidAvLogCb);
     213             : #endif
     214          38 : }
     215             : 
     216             : static std::once_flag already_called;
     217             : 
     218             : void
     219          45 : av_init()
     220             : {
     221          45 :     std::call_once(already_called, init_once);
     222          45 : }
     223             : 
     224             : bool
     225           0 : is_yuv_planar(const AVPixFmtDescriptor& desc)
     226             : {
     227           0 :     if (not(desc.flags & AV_PIX_FMT_FLAG_PLANAR) or desc.flags & AV_PIX_FMT_FLAG_RGB)
     228           0 :         return false;
     229             : 
     230             :     /* handle formats that do not use all planes */
     231           0 :     unsigned used_bit_mask = (1u << desc.nb_components) - 1;
     232           0 :     for (unsigned i = 0; i < desc.nb_components; ++i)
     233           0 :         used_bit_mask &= ~(1u << desc.comp[i].plane);
     234             : 
     235           0 :     return not used_bit_mask;
     236             : }
     237             : 
     238             : std::string
     239          59 : getError(int err)
     240             : {
     241          59 :     std::string ret(AV_ERROR_MAX_STRING_SIZE, '\0');
     242          59 :     av_strerror(err, (char*) ret.data(), ret.size());
     243          59 :     return ret;
     244           0 : }
     245             : 
     246             : const char*
     247          49 : getDictValue(const AVDictionary* d, const std::string& key, int flags)
     248             : {
     249          49 :     auto kv = av_dict_get(d, key.c_str(), nullptr, flags);
     250          49 :     if (kv)
     251          49 :         return kv->value;
     252             :     else
     253           0 :         return "";
     254             : }
     255             : 
     256             : void
     257         151 : setDictValue(AVDictionary** d, const std::string& key, const std::string& value, int flags)
     258             : {
     259         151 :     av_dict_set(d, key.c_str(), value.c_str(), flags);
     260         151 : }
     261             : 
     262             : void
     263        9359 : fillWithBlack(AVFrame* frame)
     264             : {
     265        9359 :     const AVPixelFormat format = static_cast<AVPixelFormat>(frame->format);
     266        9359 :     const int planes = av_pix_fmt_count_planes(format);
     267             :     // workaround for casting pointers to different sizes
     268             :     // on 64 bit machines: sizeof(ptrdiff_t) != sizeof(int)
     269             :     ptrdiff_t linesizes[4];
     270       37436 :     for (int i = 0; i < planes; ++i)
     271       28077 :         linesizes[i] = frame->linesize[i];
     272        9359 :     int ret = av_image_fill_black(frame->data, linesizes, format, frame->color_range, frame->width, frame->height);
     273        9359 :     if (ret < 0) {
     274           0 :         JAMI_ERR() << "Failed to blacken frame";
     275             :     }
     276        9359 : }
     277             : 
     278             : void
     279           0 : fillWithSilence(AVFrame* frame)
     280             : {
     281           0 :     int ret = av_samples_set_silence(frame->extended_data,
     282             :                                      0,
     283             :                                      frame->nb_samples,
     284             :                                      frame->ch_layout.nb_channels,
     285           0 :                                      (AVSampleFormat) frame->format);
     286           0 :     if (ret < 0)
     287           0 :         JAMI_ERR() << "Failed to fill frame with silence";
     288           0 : }
     289             : 
     290             : } // namespace libav_utils
     291             : } // namespace jami

Generated by: LCOV version 1.14