LCOV - code coverage report
Current view: top level - src/media/audio - audio_frame_resizer.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 83.8 % 68 57
Test Date: 2026-06-13 09:18:46 Functions: 52.9 % 17 9

            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 "audio_frame_resizer.h"
      19              : #include "libav_deps.h"
      20              : #include "libav_utils.h"
      21              : #include "logger.h"
      22              : #include <libavutil/frame.h>
      23              : 
      24              : extern "C" {
      25              : #include <libavutil/audio_fifo.h>
      26              : }
      27              : 
      28              : #include <stdexcept>
      29              : #include <utility>
      30              : 
      31              : namespace jami {
      32              : 
      33          767 : AudioFrameResizer::AudioFrameResizer(const AudioFormat& format,
      34              :                                      int size,
      35          767 :                                      std::function<void(std::shared_ptr<AudioFrame>&&)> cb)
      36          767 :     : format_(format)
      37          767 :     , frameSize_(size)
      38          767 :     , cb_(std::move(cb))
      39          767 :     , queue_(av_audio_fifo_alloc(format.sampleFormat, static_cast<int>(format.nb_channels), frameSize_))
      40          767 : {}
      41              : 
      42          767 : AudioFrameResizer::~AudioFrameResizer()
      43              : {
      44          767 :     av_audio_fifo_free(queue_);
      45          767 : }
      46              : 
      47              : int
      48          370 : AudioFrameResizer::samples() const
      49              : {
      50          370 :     return av_audio_fifo_size(queue_);
      51              : }
      52              : 
      53              : int
      54            0 : AudioFrameResizer::frameSize() const
      55              : {
      56            0 :     return frameSize_;
      57              : }
      58              : 
      59              : AudioFormat
      60            0 : AudioFrameResizer::format() const
      61              : {
      62            0 :     return format_;
      63              : }
      64              : 
      65              : void
      66          278 : AudioFrameResizer::setFormat(const AudioFormat& format, int size)
      67              : {
      68          278 :     if (size)
      69          278 :         setFrameSize(size);
      70          278 :     if (format != format_) {
      71          177 :         if (auto discarded = samples())
      72            4 :             JAMI_WARNING("Discarding {} samples", discarded);
      73          177 :         av_audio_fifo_free(queue_);
      74          177 :         format_ = format;
      75          177 :         queue_ = av_audio_fifo_alloc(format.sampleFormat, static_cast<int>(format.nb_channels), frameSize_);
      76              :     }
      77          278 : }
      78              : 
      79              : void
      80          278 : AudioFrameResizer::setFrameSize(int frameSize)
      81              : {
      82          278 :     if (frameSize_ != frameSize) {
      83          176 :         frameSize_ = frameSize;
      84          176 :         if (cb_)
      85          176 :             while (auto frame = dequeue())
      86          176 :                 cb_(std::move(frame));
      87              :     }
      88          278 : }
      89              : 
      90              : void
      91            5 : AudioFrameResizer::enqueue(std::shared_ptr<AudioFrame>&& frame)
      92              : {
      93            5 :     if (not frame or frame->pointer() == nullptr)
      94            0 :         return;
      95              : 
      96            5 :     int ret = 0;
      97            5 :     auto* f = frame->pointer();
      98            5 :     AudioFormat format(f->sample_rate, f->ch_layout.nb_channels, (AVSampleFormat) f->format);
      99            5 :     if (format != format_) {
     100            0 :         JAMI_WARNING("Expected {} but got {}", format_.toString(), format.toString());
     101            0 :         setFormat(format, frameSize_);
     102              :     }
     103              : 
     104            5 :     auto nb_samples = samples();
     105            5 :     if (cb_ && nb_samples == 0 && f->nb_samples == frameSize_) {
     106            1 :         nextOutputPts_ = frame->pointer()->pts + frameSize_;
     107            1 :         cb_(std::move(frame));
     108            1 :         return; // return if frame was just passed through
     109              :     }
     110              : 
     111              :     // voice activity
     112            4 :     hasVoice_ = frame->has_voice;
     113              : 
     114              :     // queue reallocates itself if need be
     115            4 :     if ((ret = av_audio_fifo_write(queue_, reinterpret_cast<void**>(f->data), f->nb_samples)) < 0) {
     116            0 :         JAMI_ERROR("Audio resizer error: {}", libav_utils::getError(ret));
     117            0 :         throw std::runtime_error("Failed to add audio to frame resizer");
     118              :     }
     119              : 
     120            4 :     if (nextOutputPts_ == 0)
     121            3 :         nextOutputPts_ = frame->pointer()->pts - nb_samples;
     122              : 
     123            4 :     if (cb_)
     124            6 :         while (auto frame = dequeue())
     125            6 :             cb_(std::move(frame));
     126              : }
     127              : 
     128              : std::shared_ptr<AudioFrame>
     129          182 : AudioFrameResizer::dequeue()
     130              : {
     131          182 :     if (samples() < frameSize_)
     132          180 :         return {};
     133              : 
     134            2 :     auto frame = std::make_shared<AudioFrame>(format_, frameSize_);
     135              :     int ret;
     136            2 :     if ((ret = av_audio_fifo_read(queue_, reinterpret_cast<void**>(frame->pointer()->data), frameSize_)) < 0) {
     137            0 :         JAMI_ERROR("Unable to read samples from queue: {}", libav_utils::getError(ret));
     138            0 :         return {};
     139              :     }
     140            2 :     frame->pointer()->pts = nextOutputPts_;
     141            2 :     frame->has_voice = hasVoice_;
     142            2 :     nextOutputPts_ += frameSize_;
     143            2 :     return frame;
     144            2 : }
     145              : 
     146              : } // namespace jami
        

Generated by: LCOV version 2.0-1