LCOV - code coverage report
Current view: top level - foo/src/media - media_decoder.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 284 508 55.9 %
Date: 2026-02-28 10:41:24 Functions: 47 69 68.1 %

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

Generated by: LCOV version 1.14