LCOV - code coverage report
Current view: top level - src/media - media_decoder.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 59.7 % 526 314
Test Date: 2026-06-13 09:18:46 Functions: 57.6 % 99 57

            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 "media_decoder.h"
      20              : #include "media_device.h"
      21              : #include "media_buffer.h"
      22              : #include "media_io_handle.h"
      23              : #include "audio/ringbufferpool.h"
      24              : #include "decoder_finder.h"
      25              : #include "manager.h"
      26              : 
      27              : #ifdef ENABLE_HWACCEL
      28              : #include "video/accel.h"
      29              : #endif
      30              : 
      31              : #include "string_utils.h"
      32              : #include "logger.h"
      33              : #include "client/jami_signal.h"
      34              : 
      35              : #include <unistd.h>
      36              : #include <cstddef>
      37              : #include <thread> // hardware_concurrency
      38              : #include <chrono>
      39              : #include <algorithm>
      40              : #include <asio/steady_timer.hpp>
      41              : 
      42              : namespace jami {
      43              : 
      44              : // maximum number of packets the jitter buffer can queue
      45              : const unsigned jitterBufferMaxSize_ {1500};
      46              : // maximum time a packet can be queued
      47              : const constexpr auto jitterBufferMaxDelay_ = std::chrono::milliseconds(50);
      48              : 
      49          374 : MediaDemuxer::MediaDemuxer()
      50          374 :     : inputCtx_(avformat_alloc_context())
      51          748 :     , startTime_(AV_NOPTS_VALUE)
      52          374 : {}
      53              : 
      54          374 : MediaDemuxer::~MediaDemuxer()
      55              : {
      56          374 :     if (streamInfoTimer_) {
      57            0 :         streamInfoTimer_->cancel();
      58            0 :         streamInfoTimer_.reset();
      59              :     }
      60          374 :     if (inputCtx_)
      61          319 :         avformat_close_input(&inputCtx_);
      62          374 :     av_dict_free(&options_);
      63          374 : }
      64              : 
      65              : const char*
      66            0 : MediaDemuxer::getStatusStr(Status status)
      67              : {
      68            0 :     switch (status) {
      69            0 :     case Status::Success:
      70            0 :         return "Success";
      71            0 :     case Status::EndOfFile:
      72            0 :         return "End of file";
      73            0 :     case Status::ReadBufferOverflow:
      74            0 :         return "Read overflow";
      75            0 :     case Status::ReadError:
      76            0 :         return "Read error";
      77            0 :     case Status::FallBack:
      78            0 :         return "Fallback";
      79            0 :     case Status::RestartRequired:
      80            0 :         return "Restart required";
      81            0 :     default:
      82            0 :         return "Undefined";
      83              :     }
      84              : }
      85              : 
      86              : int
      87          374 : MediaDemuxer::openInput(const DeviceParams& params)
      88              : {
      89          374 :     inputParams_ = params;
      90          374 :     const auto* iformat = av_find_input_format(params.format.c_str());
      91              : 
      92          374 :     if (!iformat && !params.format.empty())
      93            4 :         JAMI_WARNING("Unable to find format \"{}\"", params.format);
      94              : 
      95          374 :     std::string input;
      96              : 
      97          374 :     if (params.input == "pipewiregrab") {
      98              :         //
      99              :         // We rely on pipewiregrab for screen/window sharing on Wayland.
     100              :         // Because pipewiregrab is a "video source filter" (part of FFmpeg's libavfilter
     101              :         // library), its options must all be passed as part of the `input` string.
     102              :         //
     103            0 :         input = fmt::format("pipewiregrab=draw_mouse=1:fd={}:node={}", params.fd, params.node);
     104            0 :         JAMI_LOG("Attempting to open input {}", input);
     105              :         //
     106              :         // In all other cases, we use the `options_` AVDictionary to pass options to FFmpeg.
     107              :         //
     108              :         // NOTE: We rely on the "lavfi" virtual input device to read pipewiregrab's output
     109              :         // and create a corresponding stream (cf. the getDeviceParams function in
     110              :         // daemon/src/media/video/v4l2/video_device_impl.cpp). The `options_` dictionary
     111              :         // could be used to set lavfi's parameters if that was ever needed, but it isn't at
     112              :         // the moment. (Doc: https://ffmpeg.org/ffmpeg-devices.html#lavfi)
     113              :         //
     114              :     } else {
     115          374 :         if (params.width and params.height) {
     116            0 :             auto sizeStr = fmt::format("{}x{}", params.width, params.height);
     117            0 :             av_dict_set(&options_, "video_size", sizeStr.c_str(), 0);
     118            0 :         }
     119              : 
     120          374 :         if (params.framerate) {
     121              : #ifdef _WIN32
     122              :             // On Windows, framerate settings don't reduce to avrational values
     123              :             // that correspond to valid video device formats.
     124              :             // e.g. A the rational<double>(10000000, 333333) or 30.000030000
     125              :             //      will be reduced by av_reduce to 999991/33333 or 30.00003000003
     126              :             //      which cause the device opening routine to fail.
     127              :             // So we treat this imprecise reduction and adjust the value,
     128              :             // or let dshow choose the framerate, which is, unfortunately,
     129              :             // NOT the highest according to our experimentations.
     130              :             auto framerate {params.framerate.real()};
     131              :             framerate = params.framerate.numerator() / (params.framerate.denominator() + 0.5);
     132              :             if (params.framerate.denominator() != 4999998)
     133              :                 av_dict_set(&options_, "framerate", jami::to_string(framerate).c_str(), 0);
     134              : #else
     135            1 :             av_dict_set(&options_, "framerate", jami::to_string(params.framerate.real()).c_str(), 0);
     136              : #endif
     137              :         }
     138              : 
     139          374 :         if (params.offset_x || params.offset_y) {
     140            0 :             av_dict_set(&options_, "offset_x", std::to_string(params.offset_x).c_str(), 0);
     141            0 :             av_dict_set(&options_, "offset_y", std::to_string(params.offset_y).c_str(), 0);
     142              :         }
     143          374 :         if (params.channel)
     144            0 :             av_dict_set(&options_, "channel", std::to_string(params.channel).c_str(), 0);
     145          374 :         av_dict_set(&options_, "loop", params.loop.c_str(), 0);
     146          374 :         av_dict_set(&options_, "sdp_flags", params.sdp_flags.c_str(), 0);
     147              : 
     148              :         // Set jitter buffer options
     149          374 :         av_dict_set(&options_, "reorder_queue_size", std::to_string(jitterBufferMaxSize_).c_str(), 0);
     150          373 :         auto us = std::chrono::duration_cast<std::chrono::microseconds>(jitterBufferMaxDelay_).count();
     151          373 :         av_dict_set(&options_, "max_delay", std::to_string(us).c_str(), 0);
     152              : 
     153          374 :         if (!params.pixel_format.empty()) {
     154            0 :             av_dict_set(&options_, "pixel_format", params.pixel_format.c_str(), 0);
     155              :         }
     156          374 :         if (!params.window_id.empty()) {
     157            0 :             av_dict_set(&options_, "window_id", params.window_id.c_str(), 0);
     158              :         }
     159          374 :         av_dict_set(&options_, "draw_mouse", "1", 0);
     160          373 :         av_dict_set(&options_, "is_area", std::to_string(params.is_area).c_str(), 0);
     161              : 
     162          374 :         input = params.input;
     163              : 
     164         1496 :         JAMI_LOG("Attempting to open input {} with format {}, pixel format {}, size {}x{}, rate {}",
     165              :                  input,
     166              :                  params.format,
     167              :                  params.pixel_format,
     168              :                  params.width,
     169              :                  params.height,
     170              :                  params.framerate.real());
     171              :     }
     172              : 
     173              :     // Ask FFmpeg to open the input using the options set above
     174          374 :     if (params.disable_dts_probe_delay && params.format == "sdp") {
     175          136 :         av_opt_set_int(inputCtx_, "max_ts_probe", 0, AV_OPT_SEARCH_CHILDREN);
     176          136 :         av_opt_set_int(inputCtx_, "fpsprobesize", 0, AV_OPT_SEARCH_CHILDREN);
     177              :     } else {
     178              :         // Don't waste time fetching framerate when finding stream info
     179          238 :         av_opt_set_int(inputCtx_, "fpsprobesize", 1, AV_OPT_SEARCH_CHILDREN);
     180              :     }
     181              : 
     182          374 :     int ret = avformat_open_input(&inputCtx_, input.c_str(), iformat, options_ ? &options_ : NULL);
     183              : 
     184          374 :     if (ret) {
     185          220 :         JAMI_ERROR("avformat_open_input failed: {}", libav_utils::getError(ret));
     186          319 :     } else if (inputCtx_->nb_streams > 0 && inputCtx_->streams[0]->codecpar) {
     187          319 :         baseWidth_ = inputCtx_->streams[0]->codecpar->width;
     188          319 :         baseHeight_ = inputCtx_->streams[0]->codecpar->height;
     189         1276 :         JAMI_LOG("Opened input using format {:s} and resolution {:d}x{:d}", params.format, baseWidth_, baseHeight_);
     190              :     }
     191              : 
     192          374 :     return ret;
     193          374 : }
     194              : 
     195              : int64_t
     196            5 : MediaDemuxer::getDuration() const
     197              : {
     198            5 :     return inputCtx_->duration;
     199              : }
     200              : 
     201              : bool
     202            9 : MediaDemuxer::seekFrame(int, int64_t timestamp)
     203              : {
     204            9 :     std::lock_guard lk(inputCtxMutex_);
     205            9 :     if (av_seek_frame(inputCtx_, -1, timestamp, AVSEEK_FLAG_BACKWARD) >= 0) {
     206            9 :         clearFrames();
     207            9 :         return true;
     208              :     }
     209            0 :     return false;
     210            9 : }
     211              : 
     212              : void
     213          319 : MediaDemuxer::findStreamInfo(bool videoStream)
     214              : {
     215          319 :     if (not streamInfoFound_) {
     216          319 :         inputCtx_->max_analyze_duration = 30l * AV_TIME_BASE;
     217          319 :         if (videoStream && keyFrameRequestCb_) {
     218          136 :             if (!streamInfoTimer_)
     219          136 :                 streamInfoTimer_ = std::make_unique<asio::steady_timer>(*Manager::instance().ioContext());
     220          136 :             streamInfoTimer_->expires_after(std::chrono::milliseconds(1500));
     221          136 :             streamInfoTimer_->async_wait([weak = weak_from_this()](const std::error_code& ec) {
     222          136 :                 if (ec)
     223           67 :                     return;
     224           69 :                 if (auto self = weak.lock()) {
     225           69 :                     if (!self->streamInfoFound_) {
     226          276 :                         JAMI_LOG("findStreamInfo: 1500ms elapsed, requesting keyframe to aid probing");
     227           69 :                         if (self->keyFrameRequestCb_)
     228           69 :                             self->keyFrameRequestCb_();
     229              :                     }
     230           69 :                 }
     231              :             });
     232              :         }
     233              : 
     234          319 :         int err = avformat_find_stream_info(inputCtx_, nullptr);
     235          319 :         if (err < 0) {
     236            0 :             JAMI_ERROR("Unable to find stream info: {}", libav_utils::getError(err));
     237              :         }
     238          319 :         streamInfoFound_ = true;
     239          319 :         if (streamInfoTimer_) {
     240          136 :             streamInfoTimer_->cancel();
     241          136 :             streamInfoTimer_.reset();
     242              :         }
     243              :     }
     244          319 : }
     245              : 
     246              : int
     247          325 : MediaDemuxer::selectStream(AVMediaType type)
     248              : {
     249          325 :     auto sti = av_find_best_stream(inputCtx_, type, -1, -1, nullptr, 0);
     250          325 :     if (type == AVMEDIA_TYPE_VIDEO && sti >= 0) {
     251          142 :         auto* st = inputCtx_->streams[sti];
     252          142 :         auto disposition = st->disposition;
     253          142 :         if (disposition & AV_DISPOSITION_ATTACHED_PIC) {
     254            4 :             JAMI_LOG("Skipping attached picture stream");
     255            1 :             sti = -1;
     256              :         }
     257              :     }
     258          325 :     return sti;
     259              : }
     260              : 
     261              : void
     262          322 : MediaDemuxer::setInterruptCallback(int (*cb)(void*), void* opaque)
     263              : {
     264          322 :     if (cb) {
     265          322 :         inputCtx_->interrupt_callback.callback = cb;
     266          322 :         inputCtx_->interrupt_callback.opaque = opaque;
     267              :     } else {
     268            0 :         inputCtx_->interrupt_callback.callback = 0;
     269              :     }
     270          322 : }
     271              : void
     272            6 : MediaDemuxer::setNeedFrameCb(std::function<void()> cb)
     273              : {
     274            6 :     needFrameCb_ = std::move(cb);
     275            6 : }
     276              : 
     277              : void
     278            6 : MediaDemuxer::setFileFinishedCb(std::function<void(bool)> cb)
     279              : {
     280            6 :     fileFinishedCb_ = std::move(cb);
     281            6 : }
     282              : 
     283              : void
     284          136 : MediaDemuxer::setKeyFrameRequestCb(std::function<void()> cb)
     285              : {
     286          136 :     keyFrameRequestCb_ = std::move(cb);
     287          136 : }
     288              : 
     289              : void
     290            9 : MediaDemuxer::clearFrames()
     291              : {
     292              :     {
     293            9 :         std::lock_guard lk {videoBufferMutex_};
     294           12 :         while (!videoBuffer_.empty()) {
     295            3 :             videoBuffer_.pop();
     296              :         }
     297            9 :     }
     298              :     {
     299            9 :         std::lock_guard lk {audioBufferMutex_};
     300           11 :         while (!audioBuffer_.empty()) {
     301            2 :             audioBuffer_.pop();
     302              :         }
     303            9 :     }
     304            9 : }
     305              : 
     306              : bool
     307            0 : MediaDemuxer::emitFrame(bool isAudio)
     308              : {
     309            0 :     if (isAudio) {
     310            0 :         return pushFrameFrom(audioBuffer_, isAudio, audioBufferMutex_);
     311              :     } else {
     312            0 :         return pushFrameFrom(videoBuffer_, isAudio, videoBufferMutex_);
     313              :     }
     314              : }
     315              : 
     316              : bool
     317            0 : MediaDemuxer::pushFrameFrom(std::queue<std::unique_ptr<AVPacket, std::function<void(AVPacket*)>>>& buffer,
     318              :                             bool isAudio,
     319              :                             std::mutex& mutex)
     320              : {
     321            0 :     std::unique_lock lock(mutex);
     322            0 :     if (buffer.empty()) {
     323            0 :         if (currentState_ == MediaDemuxer::CurrentState::Finished) {
     324            0 :             fileFinishedCb_(isAudio);
     325              :         } else {
     326            0 :             needFrameCb_();
     327              :         }
     328            0 :         return false;
     329              :     }
     330            0 :     auto packet = std::move(buffer.front());
     331            0 :     if (!packet) {
     332            0 :         return false;
     333              :     }
     334            0 :     auto streamIndex = packet->stream_index;
     335            0 :     if (static_cast<unsigned>(streamIndex) >= streams_.size() || streamIndex < 0) {
     336            0 :         return false;
     337              :     }
     338            0 :     if (auto& cb = streams_[streamIndex]) {
     339            0 :         buffer.pop();
     340            0 :         lock.unlock();
     341            0 :         cb(*packet.get());
     342              :     }
     343            0 :     return true;
     344            0 : }
     345              : 
     346              : MediaDemuxer::Status
     347            5 : MediaDemuxer::demuxe()
     348              : {
     349            0 :     auto packet = std::unique_ptr<AVPacket, std::function<void(AVPacket*)>>(av_packet_alloc(), [](AVPacket* p) {
     350            5 :         if (p)
     351            5 :             av_packet_free(&p);
     352            5 :     });
     353              : 
     354              :     bool isVideo;
     355              :     {
     356            5 :         std::lock_guard lk(inputCtxMutex_);
     357            5 :         int ret = av_read_frame(inputCtx_, packet.get());
     358            5 :         if (ret == AVERROR(EAGAIN)) {
     359            0 :             return Status::Success;
     360            5 :         } else if (ret == AVERROR_EOF) {
     361            0 :             return Status::EndOfFile;
     362            5 :         } else if (ret < 0) {
     363            0 :             JAMI_ERROR("Unable to read frame: {}", libav_utils::getError(ret));
     364            0 :             return Status::ReadError;
     365              :         }
     366              : 
     367            5 :         auto streamIndex = packet->stream_index;
     368            5 :         if (static_cast<unsigned>(streamIndex) >= streams_.size() || streamIndex < 0) {
     369            0 :             return Status::Success;
     370              :         }
     371              : 
     372            5 :         isVideo = inputCtx_->streams[streamIndex]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO;
     373            5 :     }
     374              : 
     375            5 :     if (isVideo) {
     376            3 :         std::lock_guard lk {videoBufferMutex_};
     377            3 :         videoBuffer_.push(std::move(packet));
     378            3 :         if (videoBuffer_.size() >= 90) {
     379            0 :             return Status::ReadBufferOverflow;
     380              :         }
     381            3 :     } else {
     382            2 :         std::lock_guard lk {audioBufferMutex_};
     383            2 :         audioBuffer_.push(std::move(packet));
     384            2 :         if (audioBuffer_.size() >= 300) {
     385            0 :             return Status::ReadBufferOverflow;
     386              :         }
     387            2 :     }
     388            5 :     return Status::Success;
     389            5 : }
     390              : 
     391              : void
     392          624 : MediaDemuxer::setIOContext(MediaIOHandle* ioctx)
     393              : {
     394          624 :     inputCtx_->pb = ioctx->getContext();
     395          624 : }
     396              : 
     397              : MediaDemuxer::Status
     398         5760 : MediaDemuxer::decode()
     399              : {
     400         5760 :     if (inputParams_.format == "x11grab" || inputParams_.format == "dxgigrab") {
     401            0 :         auto ret = inputCtx_->iformat->read_header(inputCtx_);
     402            0 :         if (ret == AVERROR_EXTERNAL) {
     403            0 :             JAMI_ERROR("Unable to read frame: {}\n", libav_utils::getError(ret));
     404            0 :             return Status::ReadError;
     405              :         }
     406            0 :         auto* codecpar = inputCtx_->streams[0]->codecpar;
     407            0 :         if (baseHeight_ != codecpar->height || baseWidth_ != codecpar->width) {
     408            0 :             baseHeight_ = codecpar->height;
     409            0 :             baseWidth_ = codecpar->width;
     410            0 :             inputParams_.height = ((baseHeight_ >> 3) << 3);
     411            0 :             inputParams_.width = ((baseWidth_ >> 3) << 3);
     412            0 :             return Status::RestartRequired;
     413              :         }
     414              :     }
     415              : 
     416         5760 :     libjami::PacketBuffer packet(av_packet_alloc());
     417         5760 :     int ret = av_read_frame(inputCtx_, packet.get());
     418         5760 :     if (ret == AVERROR(EAGAIN)) {
     419              :         /*no data available. Calculate time until next frame.
     420              :          We do not use the emulated frame mechanism from the decoder because it will affect all
     421              :          platforms. With the current implementation, the demuxer will be waiting just in case when
     422              :          av_read_frame returns EAGAIN. For some platforms, av_read_frame is blocking and it will
     423              :          never happen.
     424              :          */
     425            0 :         if (inputParams_.framerate.numerator() == 0)
     426            0 :             return Status::Success;
     427            0 :         rational<double> frameTime = 1e6 / inputParams_.framerate;
     428            0 :         int64_t timeToSleep = lastReadPacketTime_ - av_gettime_relative() + frameTime.real<int64_t>();
     429            0 :         if (timeToSleep <= 0) {
     430            0 :             return Status::Success;
     431              :         }
     432            0 :         std::this_thread::sleep_for(std::chrono::microseconds(timeToSleep));
     433            0 :         return Status::Success;
     434         5760 :     } else if (ret == AVERROR_EOF) {
     435          130 :         return Status::EndOfFile;
     436         5630 :     } else if (ret == AVERROR(EACCES)) {
     437            0 :         return Status::RestartRequired;
     438         5630 :     } else if (ret < 0) {
     439            0 :         auto media = inputCtx_->streams[0]->codecpar->codec_type;
     440            0 :         const auto* const type = media == AVMediaType::AVMEDIA_TYPE_AUDIO
     441            0 :                                      ? "AUDIO"
     442            0 :                                      : (media == AVMediaType::AVMEDIA_TYPE_VIDEO ? "VIDEO" : "UNSUPPORTED");
     443            0 :         JAMI_ERROR("Unable to read [{}] frame: {}", type, libav_utils::getError(ret));
     444            0 :         return Status::ReadError;
     445              :     }
     446              : 
     447         5630 :     auto streamIndex = packet->stream_index;
     448         5630 :     if (static_cast<unsigned>(streamIndex) >= streams_.size() || streamIndex < 0) {
     449            0 :         return Status::Success;
     450              :     }
     451              : 
     452         5630 :     lastReadPacketTime_ = av_gettime_relative();
     453              : 
     454         5630 :     auto& cb = streams_[streamIndex];
     455         5630 :     if (cb) {
     456         5630 :         DecodeStatus ret = cb(*packet.get());
     457         5630 :         if (ret == DecodeStatus::FallBack)
     458            0 :             return Status::FallBack;
     459              :     }
     460         5630 :     return Status::Success;
     461         5760 : }
     462              : 
     463            0 : MediaDecoder::MediaDecoder(const std::shared_ptr<MediaDemuxer>& demuxer, int index)
     464            0 :     : demuxer_(demuxer)
     465            0 :     , avStream_(demuxer->getStream(index))
     466              : {
     467            0 :     demuxer->setStreamCallback(index, [this](AVPacket& packet) { return decode(packet); });
     468            0 :     setupStream();
     469            0 : }
     470              : 
     471           10 : MediaDecoder::MediaDecoder(const std::shared_ptr<MediaDemuxer>& demuxer, int index, MediaObserver observer)
     472           10 :     : demuxer_(demuxer)
     473           10 :     , avStream_(demuxer->getStream(index))
     474           20 :     , callback_(std::move(observer))
     475              : {
     476           10 :     demuxer->setStreamCallback(index, [this](AVPacket& packet) { return decode(packet); });
     477           10 :     setupStream();
     478           10 : }
     479              : 
     480              : bool
     481            0 : MediaDecoder::emitFrame(bool isAudio)
     482              : {
     483            0 :     return demuxer_->emitFrame(isAudio);
     484              : }
     485              : 
     486            0 : MediaDecoder::MediaDecoder()
     487            0 :     : demuxer_(new MediaDemuxer)
     488            0 : {}
     489              : 
     490          368 : MediaDecoder::MediaDecoder(MediaObserver o)
     491          368 :     : demuxer_(new MediaDemuxer)
     492          368 :     , callback_(std::move(o))
     493          368 : {}
     494              : 
     495          378 : MediaDecoder::~MediaDecoder()
     496              : {
     497              : #ifdef ENABLE_HWACCEL
     498          378 :     if (decoderCtx_ && decoderCtx_->hw_device_ctx)
     499            0 :         av_buffer_unref(&decoderCtx_->hw_device_ctx);
     500              : #endif
     501          378 :     if (decoderCtx_)
     502          323 :         avcodec_free_context(&decoderCtx_);
     503          378 : }
     504              : 
     505              : void
     506           18 : MediaDecoder::flushBuffers()
     507              : {
     508           18 :     avcodec_flush_buffers(decoderCtx_);
     509           18 : }
     510              : 
     511              : int
     512          368 : MediaDecoder::openInput(const DeviceParams& p)
     513              : {
     514          368 :     passthrough_ = p.passthrough;
     515          368 :     return demuxer_->openInput(p);
     516              : }
     517              : 
     518              : void
     519          322 : MediaDecoder::setInterruptCallback(int (*cb)(void*), void* opaque)
     520              : {
     521          322 :     demuxer_->setInterruptCallback(cb, opaque);
     522          322 : }
     523              : 
     524              : void
     525          624 : MediaDecoder::setIOContext(MediaIOHandle* ioctx)
     526              : {
     527          624 :     demuxer_->setIOContext(ioctx);
     528          623 : }
     529              : 
     530              : void
     531          136 : MediaDecoder::setKeyFrameRequestCb(std::function<void()> cb)
     532              : {
     533          136 :     demuxer_->setKeyFrameRequestCb(std::move(cb));
     534          136 : }
     535              : 
     536              : int
     537          313 : MediaDecoder::setup(AVMediaType type)
     538              : {
     539          313 :     demuxer_->findStreamInfo(type == AVMEDIA_TYPE_VIDEO);
     540          313 :     auto stream = demuxer_->selectStream(type);
     541          313 :     if (stream < 0) {
     542            0 :         JAMI_ERROR("No stream found for type {}", static_cast<int>(type));
     543            0 :         return -1;
     544              :     }
     545          313 :     avStream_ = demuxer_->getStream(stream);
     546          313 :     if (avStream_ == nullptr) {
     547            0 :         JAMI_ERROR("No stream found at index {}", stream);
     548            0 :         return -1;
     549              :     }
     550         5943 :     demuxer_->setStreamCallback(stream, [this](AVPacket& packet) { return decode(packet); });
     551          313 :     return setupStream();
     552              : }
     553              : 
     554              : int
     555          323 : MediaDecoder::setupStream()
     556              : {
     557          323 :     int ret = 0;
     558          323 :     avcodec_free_context(&decoderCtx_);
     559              : 
     560          323 :     if (prepareDecoderContext() < 0)
     561            0 :         return -1; // failed
     562              : 
     563              : #ifdef ENABLE_HWACCEL
     564              :     // if there was a fallback to software decoding, do not enable accel
     565              :     // it has been disabled already by the video_receive_thread/video_input
     566          323 :     enableAccel_ &= Manager::instance().videoPreferences.getDecodingAccelerated();
     567              : 
     568          323 :     if (enableAccel_ and not fallback_) {
     569          323 :         auto APIs = video::HardwareAccel::getCompatibleAccel(decoderCtx_->codec_id,
     570          323 :                                                              decoderCtx_->width,
     571          323 :                                                              decoderCtx_->height,
     572          323 :                                                              CODEC_DECODER);
     573          736 :         for (const auto& it : APIs) {
     574          413 :             accel_ = std::make_unique<video::HardwareAccel>(it); // save accel
     575          413 :             auto ret = accel_->initAPI(false, nullptr);
     576          413 :             if (ret < 0) {
     577          413 :                 accel_.reset();
     578          413 :                 continue;
     579              :             }
     580            0 :             if (prepareDecoderContext() < 0)
     581            0 :                 return -1; // failed
     582            0 :             accel_->setDetails(decoderCtx_);
     583            0 :             decoderCtx_->opaque = accel_.get();
     584            0 :             decoderCtx_->pix_fmt = accel_->getFormat();
     585            0 :             if (avcodec_open2(decoderCtx_, inputDecoder_, &options_) < 0) {
     586              :                 // Failed to open codec
     587            0 :                 JAMI_WARNING("Fail to open hardware decoder for {} with {}",
     588              :                              avcodec_get_name(decoderCtx_->codec_id),
     589              :                              it.getName());
     590            0 :                 avcodec_free_context(&decoderCtx_);
     591            0 :                 decoderCtx_ = nullptr;
     592            0 :                 accel_.reset();
     593            0 :                 continue;
     594            0 :             } else {
     595              :                 // Codec opened successfully.
     596            0 :                 JAMI_WARNING("Using hardware decoding for {} with {}",
     597              :                              avcodec_get_name(decoderCtx_->codec_id),
     598              :                              it.getName());
     599            0 :                 break;
     600              :             }
     601              :         }
     602          323 :     }
     603              : #endif
     604              : 
     605         1292 :     JAMI_LOG("Using {} ({}) decoder for {}",
     606              :              inputDecoder_->long_name,
     607              :              inputDecoder_->name,
     608              :              av_get_media_type_string(avStream_->codecpar->codec_type));
     609          323 :     decoderCtx_->thread_count = std::max(1, std::min(8, static_cast<int>(std::thread::hardware_concurrency()) / 2));
     610          323 :     if (emulateRate_)
     611            0 :         JAMI_LOG("Using framerate emulation");
     612          323 :     startTime_ = av_gettime(); // Used to set pts after decoding, and for rate emulation
     613              : 
     614              : #ifdef ENABLE_HWACCEL
     615          323 :     if (!accel_) {
     616         1292 :         JAMI_WARNING("Not using hardware decoding for {}", avcodec_get_name(decoderCtx_->codec_id));
     617          323 :         ret = avcodec_open2(decoderCtx_, inputDecoder_, nullptr);
     618              :     }
     619              : #else
     620              :     ret = avcodec_open2(decoderCtx_, inputDecoder_, nullptr);
     621              : #endif
     622          323 :     if (ret < 0) {
     623            0 :         JAMI_ERROR("Unable to open codec: {}", libav_utils::getError(ret));
     624            0 :         return -1;
     625              :     }
     626              : 
     627          323 :     return 0;
     628              : }
     629              : 
     630              : int
     631          323 : MediaDecoder::prepareDecoderContext()
     632              : {
     633          323 :     inputDecoder_ = findDecoder(avStream_->codecpar->codec_id);
     634          323 :     if (!inputDecoder_) {
     635            0 :         JAMI_ERROR("Unsupported codec");
     636            0 :         return -1;
     637              :     }
     638              : 
     639          323 :     decoderCtx_ = avcodec_alloc_context3(inputDecoder_);
     640          323 :     if (!decoderCtx_) {
     641            0 :         JAMI_ERROR("Failed to create decoder context");
     642            0 :         return -1;
     643              :     }
     644          323 :     avcodec_parameters_to_context(decoderCtx_, avStream_->codecpar);
     645          323 :     decoderCtx_->pkt_timebase = avStream_->time_base;
     646          323 :     width_ = decoderCtx_->width;
     647          323 :     height_ = decoderCtx_->height;
     648          323 :     decoderCtx_->framerate = avStream_->avg_frame_rate;
     649          323 :     if (avStream_->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
     650          141 :         if (decoderCtx_->framerate.num == 0 || decoderCtx_->framerate.den == 0)
     651          136 :             decoderCtx_->framerate = inputParams_.framerate;
     652          141 :         if (decoderCtx_->framerate.num == 0 || decoderCtx_->framerate.den == 0)
     653          136 :             decoderCtx_->framerate = {30, 1};
     654          182 :     } else if (avStream_->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
     655          182 :         if (decoderCtx_->codec_id == AV_CODEC_ID_OPUS) {
     656          180 :             av_opt_set_int(decoderCtx_, "decode_fec", fecEnabled_ ? 1 : 0, AV_OPT_SEARCH_CHILDREN);
     657              :         }
     658          182 :         auto format = libav_utils::choose_sample_fmt_default(
     659          182 :             inputDecoder_, Manager::instance().getRingBufferPool().getInternalAudioFormat().sampleFormat);
     660          182 :         decoderCtx_->sample_fmt = format;
     661          182 :         decoderCtx_->request_sample_fmt = format;
     662              :     }
     663          323 :     return 0;
     664              : }
     665              : 
     666              : void
     667           52 : MediaDecoder::updateStartTime(int64_t startTime)
     668              : {
     669           52 :     startTime_ = startTime;
     670           52 : }
     671              : 
     672              : DecodeStatus
     673         5630 : MediaDecoder::decode(AVPacket& packet)
     674              : {
     675         5630 :     if (inputDecoder_->type == AVMEDIA_TYPE_VIDEO && passthrough_) {
     676              : #ifdef ENABLE_VIDEO
     677              :         // If passthrough, we don't decode, just pass the packet
     678            0 :         auto f = std::static_pointer_cast<MediaFrame>(std::make_shared<VideoFrame>());
     679            0 :         if (auto p = av_packet_clone(&packet)) {
     680            0 :             f->setPacket(libjami::PacketBuffer(p));
     681              :         }
     682            0 :         if (callback_)
     683            0 :             callback_(std::move(f));
     684              : 
     685            0 :         if (contextCallback_ && firstDecode_.load()) {
     686            0 :             firstDecode_.exchange(false);
     687            0 :             contextCallback_();
     688              :         }
     689            0 :         return DecodeStatus::FrameFinished;
     690              : #endif
     691            0 :     }
     692              : 
     693         5630 :     int frameFinished = 0;
     694         5630 :     auto ret = avcodec_send_packet(decoderCtx_, &packet);
     695              :     // TODO: Investigate avcodec_send_packet returning AVERROR_INVALIDDATA.
     696              :     // * Bug Windows documented here: git.jami.net/savoirfairelinux/jami-daemon/-/issues/1116
     697              :     // where avcodec_send_packet returns AVERROR_INVALIDDATA when the size information in the
     698              :     // packet is incorrect. Falling back onto sw decoding in this causes a segfault.
     699              :     // * A second problem occurs on some Windows devices with intel CPUs in which hardware
     700              :     // decoding fails with AVERROR_INVALIDDATA when using H.264. However, in this scenario,
     701              :     // falling back to software decoding works fine.
     702              :     // We need to figure out why this behavior occurs and how to discriminate between the two.
     703         5630 :     if (ret < 0 && ret != AVERROR(EAGAIN)) {
     704              : #ifdef ENABLE_HWACCEL
     705            0 :         if (accel_) {
     706            0 :             JAMI_WARNING("Decoding error falling back to software");
     707            0 :             fallback_ = true;
     708            0 :             accel_.reset();
     709            0 :             avcodec_flush_buffers(decoderCtx_);
     710            0 :             setupStream();
     711            0 :             return DecodeStatus::FallBack;
     712              :         }
     713              : #endif
     714            0 :         avcodec_flush_buffers(decoderCtx_);
     715            0 :         return ret == AVERROR_EOF ? DecodeStatus::Success : DecodeStatus::DecodeError;
     716              :     }
     717              : 
     718              : #ifdef ENABLE_VIDEO
     719         5630 :     auto f = (inputDecoder_->type == AVMEDIA_TYPE_VIDEO)
     720        11252 :                  ? std::static_pointer_cast<MediaFrame>(std::make_shared<VideoFrame>())
     721        11256 :                  : std::static_pointer_cast<MediaFrame>(std::make_shared<AudioFrame>());
     722              : #else
     723              :     auto f = std::static_pointer_cast<MediaFrame>(std::make_shared<AudioFrame>());
     724              : #endif
     725         5630 :     auto* frame = f->pointer();
     726         5630 :     ret = avcodec_receive_frame(decoderCtx_, frame);
     727              :     // time_base is not set in AVCodecContext for decoding
     728              :     // fail to set it causes pts to be incorrectly computed down in the function
     729         5630 :     if (inputDecoder_->type == AVMEDIA_TYPE_VIDEO) {
     730         5626 :         decoderCtx_->time_base.num = decoderCtx_->framerate.den;
     731         5626 :         decoderCtx_->time_base.den = decoderCtx_->framerate.num;
     732              :     } else {
     733            4 :         decoderCtx_->time_base.num = 1;
     734            4 :         decoderCtx_->time_base.den = decoderCtx_->sample_rate;
     735              :     }
     736         5630 :     frame->time_base = decoderCtx_->time_base;
     737         5630 :     if (resolutionChangedCallback_) {
     738         5626 :         if (decoderCtx_->width != width_ or decoderCtx_->height != height_) {
     739            0 :             JAMI_LOG("Resolution changed from {}x{} to {}x{}", width_, height_, decoderCtx_->width, decoderCtx_->height);
     740            0 :             width_ = decoderCtx_->width;
     741            0 :             height_ = decoderCtx_->height;
     742            0 :             resolutionChangedCallback_(width_, height_);
     743              :         }
     744              :     }
     745         5630 :     if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
     746            0 :         return DecodeStatus::DecodeError;
     747              :     }
     748         5630 :     if (ret >= 0)
     749         5363 :         frameFinished = 1;
     750              : 
     751         5630 :     if (frameFinished) {
     752         5363 :         if (inputDecoder_->type == AVMEDIA_TYPE_VIDEO) {
     753         5359 :             frame->format = (AVPixelFormat) correctPixFmt(frame->format);
     754              :         } else {
     755              :             // It's possible (albeit rare) for avcodec_receive_frame to return a frame with
     756              :             // unspecified channel order. This can cause issues later on in the resampler
     757              :             // because swr_convert_frame expects the ch_layout of the input frame to match
     758              :             // the in_ch_layout of the SwrContext, but swr_init sets in_ch_layout to a default
     759              :             // value based on the number of channels if the channel order of the input frame
     760              :             // is unspecified.
     761            4 :             if (frame->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) {
     762            4 :                 av_channel_layout_default(&frame->ch_layout, frame->ch_layout.nb_channels);
     763              :             }
     764              :         }
     765         5363 :         auto packetTimestamp = frame->pts; // in stream time base
     766         5363 :         frame->pts = av_rescale_q_rnd(av_gettime() - startTime_,
     767              :                                       {1, AV_TIME_BASE},
     768         5363 :                                       decoderCtx_->time_base,
     769              :                                       static_cast<AVRounding>(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
     770         5363 :         lastTimestamp_ = frame->pts;
     771         5363 :         if (emulateRate_ and packetTimestamp != AV_NOPTS_VALUE) {
     772            0 :             auto startTime = avStream_->start_time == AV_NOPTS_VALUE ? 0 : avStream_->start_time;
     773            0 :             rational<double> frame_time = rational<double>(getTimeBase())
     774            0 :                                           * rational<double>(static_cast<double>(packetTimestamp - startTime));
     775            0 :             auto target_relative = static_cast<std::int64_t>(frame_time.real() * 1e6);
     776            0 :             auto target_absolute = startTime_ + target_relative;
     777            0 :             if (target_relative < seekTime_) {
     778            0 :                 return DecodeStatus::Success;
     779              :             }
     780              :             // required frame found. Reset seek time
     781            0 :             if (target_relative >= seekTime_) {
     782            0 :                 resetSeekTime();
     783              :             }
     784            0 :             auto now = av_gettime();
     785            0 :             if (target_absolute > now) {
     786            0 :                 std::this_thread::sleep_for(std::chrono::microseconds(target_absolute - now));
     787              :             }
     788              :         }
     789              : 
     790         5363 :         if (callback_)
     791         5363 :             callback_(std::move(f));
     792              : 
     793         5363 :         if (contextCallback_ && firstDecode_.load()) {
     794           32 :             firstDecode_.exchange(false);
     795           32 :             contextCallback_();
     796              :         }
     797         5363 :         return DecodeStatus::FrameFinished;
     798              :     }
     799          267 :     return DecodeStatus::Success;
     800            0 : }
     801              : 
     802              : void
     803           18 : MediaDecoder::setSeekTime(int64_t time)
     804              : {
     805           18 :     seekTime_ = time;
     806           18 : }
     807              : 
     808              : MediaDemuxer::Status
     809         5760 : MediaDecoder::decode()
     810              : {
     811         5760 :     auto ret = demuxer_->decode();
     812         5760 :     if (ret == MediaDemuxer::Status::RestartRequired) {
     813            0 :         avcodec_flush_buffers(decoderCtx_);
     814            0 :         setupStream();
     815            0 :         ret = MediaDemuxer::Status::EndOfFile;
     816              :     }
     817         5760 :     return ret;
     818              : }
     819              : 
     820              : #ifdef ENABLE_VIDEO
     821              : #ifdef ENABLE_HWACCEL
     822              : void
     823            0 : MediaDecoder::enableAccel(bool enableAccel)
     824              : {
     825            0 :     enableAccel_ = enableAccel;
     826            0 :     emitSignal<libjami::ConfigurationSignal::HardwareDecodingChanged>(enableAccel_);
     827            0 :     if (!enableAccel) {
     828            0 :         accel_.reset();
     829            0 :         if (decoderCtx_)
     830            0 :             decoderCtx_->opaque = nullptr;
     831              :     }
     832            0 : }
     833              : #endif
     834              : 
     835              : DecodeStatus
     836            0 : MediaDecoder::flush()
     837              : {
     838              :     AVPacket inpacket;
     839            0 :     av_init_packet(&inpacket);
     840              : 
     841            0 :     int frameFinished = 0;
     842            0 :     int ret = 0;
     843            0 :     ret = avcodec_send_packet(decoderCtx_, &inpacket);
     844            0 :     if (ret < 0 && ret != AVERROR(EAGAIN))
     845            0 :         return ret == AVERROR_EOF ? DecodeStatus::Success : DecodeStatus::DecodeError;
     846              : 
     847            0 :     auto result = std::make_shared<MediaFrame>();
     848            0 :     ret = avcodec_receive_frame(decoderCtx_, result->pointer());
     849            0 :     if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
     850            0 :         return DecodeStatus::DecodeError;
     851            0 :     if (ret >= 0)
     852            0 :         frameFinished = 1;
     853              : 
     854            0 :     if (frameFinished) {
     855            0 :         av_packet_unref(&inpacket);
     856            0 :         if (callback_)
     857            0 :             callback_(std::move(result));
     858            0 :         return DecodeStatus::FrameFinished;
     859              :     }
     860              : 
     861            0 :     return DecodeStatus::Success;
     862            0 : }
     863              : #endif // ENABLE_VIDEO
     864              : 
     865              : int
     866          146 : MediaDecoder::getWidth() const
     867              : {
     868          146 :     return decoderCtx_ ? decoderCtx_->width : 0;
     869              : }
     870              : 
     871              : int
     872          146 : MediaDecoder::getHeight() const
     873              : {
     874          146 :     return decoderCtx_ ? decoderCtx_->height : 0;
     875              : }
     876              : 
     877              : std::string
     878            0 : MediaDecoder::getDecoderName() const
     879              : {
     880            0 :     return decoderCtx_ ? decoderCtx_->codec->name : "";
     881              : }
     882              : 
     883              : rational<double>
     884            5 : MediaDecoder::getFps() const
     885              : {
     886            5 :     return {(double) avStream_->avg_frame_rate.num, (double) avStream_->avg_frame_rate.den};
     887              : }
     888              : 
     889              : rational<unsigned>
     890            0 : MediaDecoder::getTimeBase() const
     891              : {
     892            0 :     return {(unsigned) avStream_->time_base.num, (unsigned) avStream_->time_base.den};
     893              : }
     894              : 
     895              : AVPixelFormat
     896            5 : MediaDecoder::getPixelFormat() const
     897              : {
     898            5 :     return decoderCtx_->pix_fmt;
     899              : }
     900              : 
     901              : int
     902         5359 : MediaDecoder::correctPixFmt(int input_pix_fmt)
     903              : {
     904              :     // https://ffmpeg.org/pipermail/ffmpeg-user/2014-February/020152.html
     905              :     int pix_fmt;
     906         5359 :     switch (input_pix_fmt) {
     907            0 :     case AV_PIX_FMT_YUVJ420P:
     908            0 :         pix_fmt = AV_PIX_FMT_YUV420P;
     909            0 :         break;
     910            0 :     case AV_PIX_FMT_YUVJ422P:
     911            0 :         pix_fmt = AV_PIX_FMT_YUV422P;
     912            0 :         break;
     913            0 :     case AV_PIX_FMT_YUVJ444P:
     914            0 :         pix_fmt = AV_PIX_FMT_YUV444P;
     915            0 :         break;
     916            0 :     case AV_PIX_FMT_YUVJ440P:
     917            0 :         pix_fmt = AV_PIX_FMT_YUV440P;
     918            0 :         break;
     919         5359 :     default:
     920         5359 :         pix_fmt = input_pix_fmt;
     921         5359 :         break;
     922              :     }
     923         5359 :     return pix_fmt;
     924              : }
     925              : 
     926              : MediaStream
     927          181 : MediaDecoder::getStream(const std::string& name) const
     928              : {
     929          181 :     if (!decoderCtx_) {
     930          260 :         JAMI_WARNING("No decoder context");
     931           65 :         return {};
     932              :     }
     933          116 :     auto ms = MediaStream(name, decoderCtx_, lastTimestamp_);
     934              : #ifdef ENABLE_HWACCEL
     935              :     // accel_ is null if not using accelerated codecs
     936          116 :     if (accel_)
     937           23 :         ms.format = accel_->getSoftwareFormat();
     938              : #endif
     939          116 :     return ms;
     940          116 : }
     941              : 
     942              : } // namespace jami
        

Generated by: LCOV version 2.0-1