LCOV - code coverage report
Current view: top level - src/media - libav_utils.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 71.8 % 71 51
Test Date: 2026-11-09 10:41:14 Functions: 83.3 % 12 10

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

Generated by: LCOV version 2.0-1