LCOV - code coverage report
Current view: top level - src/media - media_player.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 131 182 72.0 %
Date: 2024-12-21 08:56:24 Functions: 16 27 59.3 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2004-2024 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 "media_player.h"
      19             : #include "client/videomanager.h"
      20             : #include "client/ring_signal.h"
      21             : #include "jami/media_const.h"
      22             : #include "manager.h"
      23             : #include <string>
      24             : namespace jami {
      25             : 
      26             : static constexpr auto MS_PER_PACKET = std::chrono::milliseconds(20);
      27             : 
      28          11 : MediaPlayer::MediaPlayer(const std::string& resource)
      29          22 :     : loop_(std::bind(&MediaPlayer::configureMediaInputs, this),
      30          22 :             std::bind(&MediaPlayer::process, this),
      31          22 :             [] {})
      32             : {
      33          11 :     auto suffix = resource;
      34          11 :     static const std::string& sep = libjami::Media::VideoProtocolPrefix::SEPARATOR;
      35          11 :     const auto pos = resource.find(sep);
      36          11 :     if (pos != std::string::npos) {
      37           5 :         suffix = resource.substr(pos + sep.size());
      38             :     }
      39             : 
      40          11 :     path_ = suffix;
      41             : 
      42          11 :     audioInput_ = jami::getAudioInput(path_);
      43          11 :     audioInput_->setPaused(paused_);
      44             : #ifdef ENABLE_VIDEO
      45          11 :     videoInput_ = jami::getVideoInput(path_, video::VideoInputMode::ManagedByDaemon, resource);
      46          11 :     videoInput_->setPaused(paused_);
      47             : #endif
      48             : 
      49          11 :     demuxer_ = std::make_shared<MediaDemuxer>();
      50          11 :     loop_.start();
      51          11 : }
      52             : 
      53          11 : MediaPlayer::~MediaPlayer()
      54             : {
      55          11 :     pause(true);
      56          11 :     loop_.join();
      57          11 :     audioInput_.reset();
      58             : #ifdef ENABLE_VIDEO
      59          11 :     videoInput_.reset();
      60             : #endif
      61          11 : }
      62             : 
      63             : bool
      64          11 : MediaPlayer::configureMediaInputs()
      65             : {
      66          11 :     DeviceParams devOpts = {};
      67          11 :     devOpts.input = path_;
      68          11 :     devOpts.name = path_;
      69          11 :     devOpts.loop = "1";
      70             : 
      71          11 :     size_t dot = path_.find_last_of('.');
      72          11 :     std::string ext = dot == std::string::npos ? "" : path_.substr(dot + 1);
      73          11 :     bool decodeImg = (ext == "jpeg" || ext == "jpg" || ext == "png" || ext == "pdf");
      74             : 
      75             :     // Force 1fps for static image
      76          11 :     if (decodeImg) {
      77           1 :         devOpts.format = "image2";
      78           1 :         devOpts.framerate = 1;
      79             :     } else {
      80          30 :         JAMI_WARNING("Guessing file type for {}", path_);
      81             :     }
      82             : 
      83          11 :     if (demuxer_->openInput(devOpts) < 0) {
      84           0 :         emitInfo();
      85           0 :         return false;
      86             :     }
      87          11 :     demuxer_->findStreamInfo();
      88             : 
      89          11 :     pauseInterval_ = 0;
      90          11 :     startTime_ = av_gettime();
      91          11 :     lastPausedTime_ = startTime_;
      92             : 
      93             :     try {
      94          11 :         audioStream_ = demuxer_->selectStream(AVMEDIA_TYPE_AUDIO);
      95          11 :         if (hasAudio()) {
      96          10 :             audioInput_->configureFilePlayback(path_, demuxer_, audioStream_);
      97          10 :             audioInput_->updateStartTime(startTime_);
      98          10 :             audioInput_->start();
      99             :         }
     100           0 :     } catch (const std::exception& e) {
     101           0 :         JAMI_ERROR("MediaPlayer {} open audio input failed: {}", path_, e.what());
     102           0 :     }
     103             : #ifdef ENABLE_VIDEO
     104             :     try {
     105          11 :         videoStream_ = demuxer_->selectStream(AVMEDIA_TYPE_VIDEO);
     106          11 :         if (hasVideo()) {
     107          10 :             videoInput_->configureFilePlayback(path_, demuxer_, videoStream_);
     108          10 :             videoInput_->updateStartTime(startTime_);
     109             :         }
     110           0 :     } catch (const std::exception& e) {
     111           0 :         videoInput_ = nullptr;
     112           0 :         JAMI_ERROR("MediaPlayer {} open video input failed: {}", path_, e.what());
     113           0 :     }
     114             : #endif
     115             : 
     116          11 :     demuxer_->setNeedFrameCb([this]() -> void { readBufferOverflow_ = false; });
     117             : 
     118          11 :     demuxer_->setFileFinishedCb([this](bool isAudio) -> void {
     119           0 :         if (isAudio) {
     120           0 :             audioStreamEnded_ = true;
     121             :         } else {
     122           0 :             videoStreamEnded_ = true;
     123             :         }
     124           0 :     });
     125             : 
     126          11 :     if (decodeImg) {
     127           1 :         fileDuration_ = 0;
     128             :     } else {
     129          10 :         fileDuration_ = demuxer_->getDuration();
     130          10 :         if (fileDuration_ <= 0) {
     131           0 :             emitInfo();
     132           0 :             return false;
     133             :         }
     134             :     }
     135             : 
     136          11 :     emitInfo();
     137          11 :     demuxer_->updateCurrentState(MediaDemuxer::CurrentState::Demuxing);
     138          11 :     return true;
     139          11 : }
     140             : 
     141             : void
     142   204605219 : MediaPlayer::process()
     143             : {
     144   204605219 :     if (!demuxer_)
     145           0 :         return;
     146   204605219 :     if (fileDuration_ > 0 && streamsFinished()) {
     147           0 :         audioStreamEnded_ = false;
     148           0 :         videoStreamEnded_ = false;
     149           0 :         playFileFromBeginning();
     150             :     }
     151             : 
     152   204605219 :     if (paused_ || readBufferOverflow_) {
     153          11 :         std::this_thread::sleep_for(MS_PER_PACKET);
     154          11 :         return;
     155             :     }
     156             : 
     157   204605208 :     const auto ret = demuxer_->demuxe();
     158   204605208 :     switch (ret) {
     159         755 :     case MediaDemuxer::Status::Success:
     160             :     case MediaDemuxer::Status::FallBack:
     161         755 :         break;
     162   204604453 :     case MediaDemuxer::Status::EndOfFile:
     163   204604453 :         demuxer_->updateCurrentState(MediaDemuxer::CurrentState::Finished);
     164   204604453 :         break;
     165           0 :     case MediaDemuxer::Status::ReadError:
     166           0 :         JAMI_ERROR("Failed to decode frame");
     167           0 :         break;
     168           0 :     case MediaDemuxer::Status::ReadBufferOverflow:
     169           0 :         readBufferOverflow_ = true;
     170           0 :         break;
     171           0 :     case MediaDemuxer::Status::RestartRequired:
     172             :     default:
     173           0 :         break;
     174             :     }
     175             : }
     176             : 
     177             : void
     178          11 : MediaPlayer::emitInfo()
     179             : {
     180          11 :     std::map<std::string, std::string> info {{"duration", std::to_string(fileDuration_)},
     181          22 :                                              {"audio_stream", std::to_string(audioStream_)},
     182          77 :                                              {"video_stream", std::to_string(videoStream_)}};
     183          11 :     emitSignal<libjami::MediaPlayerSignal::FileOpened>(path_, info);
     184          11 : }
     185             : 
     186             : bool
     187           3 : MediaPlayer::isInputValid()
     188             : {
     189           3 :     return !path_.empty();
     190             : }
     191             : 
     192             : void
     193           0 : MediaPlayer::muteAudio(bool mute)
     194             : {
     195           0 :     if (hasAudio()) {
     196           0 :         audioInput_->setMuted(mute);
     197             :     }
     198           0 : }
     199             : 
     200             : void
     201          18 : MediaPlayer::pause(bool pause)
     202             : {
     203          18 :     if (pause == paused_) {
     204           4 :         return;
     205             :     }
     206          14 :     paused_ = pause;
     207          14 :     if (!pause) {
     208           7 :         pauseInterval_ += av_gettime() - lastPausedTime_;
     209             :     } else {
     210           7 :         lastPausedTime_ = av_gettime();
     211             :     }
     212          14 :     auto newTime = startTime_ + pauseInterval_;
     213          14 :     if (hasAudio()) {
     214           9 :         audioInput_->setPaused(paused_);
     215           9 :         audioInput_->updateStartTime(newTime);
     216             :     }
     217             : #ifdef ENABLE_VIDEO
     218          14 :     if (hasVideo()) {
     219           9 :         videoInput_->setPaused(paused_);
     220           9 :         videoInput_->updateStartTime(newTime);
     221             :     }
     222             : #endif
     223             : }
     224             : 
     225             : bool
     226          11 : MediaPlayer::seekToTime(int64_t time)
     227             : {
     228          11 :     if (time < 0 || time > fileDuration_) {
     229           2 :         return false;
     230             :     }
     231           9 :     if (time >= fileDuration_) {
     232           0 :         playFileFromBeginning();
     233           0 :         return true;
     234             :     }
     235           9 :     if (!demuxer_->seekFrame(-1, time)) {
     236           0 :         return false;
     237             :     }
     238           9 :     flushMediaBuffers();
     239           9 :     demuxer_->updateCurrentState(MediaDemuxer::CurrentState::Demuxing);
     240             : 
     241           9 :     int64_t currentTime = av_gettime();
     242           9 :     if (paused_){
     243           5 :         pauseInterval_ += currentTime - lastPausedTime_;
     244           5 :         lastPausedTime_ = currentTime;
     245             :     }
     246             : 
     247           9 :     startTime_ = currentTime - pauseInterval_ - time;
     248           9 :     if (hasAudio()) {
     249           9 :         audioInput_->setSeekTime(time);
     250           9 :         audioInput_->updateStartTime(startTime_);
     251             :     }
     252             : #ifdef ENABLE_VIDEO
     253           9 :     if (hasVideo()) {
     254           9 :         videoInput_->setSeekTime(time);
     255           9 :         videoInput_->updateStartTime(startTime_);
     256             :     }
     257             : #endif
     258           9 :     return true;
     259             : }
     260             : void
     261           0 : MediaPlayer::playFileFromBeginning()
     262             : {
     263           0 :     pause(true);
     264           0 :     demuxer_->updateCurrentState(MediaDemuxer::CurrentState::Demuxing);
     265           0 :     if (!demuxer_->seekFrame(-1, 0)) {
     266           0 :         return;
     267             :     }
     268           0 :     flushMediaBuffers();
     269           0 :     startTime_ = av_gettime();
     270           0 :     lastPausedTime_ = startTime_;
     271           0 :     pauseInterval_ = 0;
     272           0 :     if (hasAudio()) {
     273           0 :         audioInput_->updateStartTime(startTime_);
     274             :     }
     275             : #ifdef ENABLE_VIDEO
     276           0 :     if (hasVideo()) {
     277           0 :         videoInput_->updateStartTime(startTime_);
     278             :     }
     279             : #endif
     280           0 :     if (autoRestart_)
     281           0 :         pause(false);
     282             : }
     283             : 
     284             : void
     285           9 : MediaPlayer::flushMediaBuffers()
     286             : {
     287             : #ifdef ENABLE_VIDEO
     288           9 :     if (hasVideo()) {
     289           9 :         videoInput_->flushBuffers();
     290             :     }
     291             : #endif
     292             : 
     293           9 :     if (hasAudio()) {
     294           9 :         audioInput_->flushBuffers();
     295             :     }
     296           9 : }
     297             : 
     298             : const std::string&
     299           3 : MediaPlayer::getId() const
     300             : {
     301           3 :     return path_;
     302             : }
     303             : 
     304             : int64_t
     305          15 : MediaPlayer::getPlayerPosition() const
     306             : {
     307          15 :     if (paused_) {
     308           9 :         return lastPausedTime_ - startTime_ - pauseInterval_;
     309             :     }
     310           6 :     return av_gettime() - startTime_ - pauseInterval_;
     311             : }
     312             : 
     313             : int64_t
     314           0 : MediaPlayer::getPlayerDuration() const
     315             : {
     316           0 :     return fileDuration_;
     317             : }
     318             : 
     319             : bool
     320           5 : MediaPlayer::isPaused() const
     321             : {
     322           5 :     return paused_;
     323             : }
     324             : 
     325             : bool
     326   204605218 : MediaPlayer::streamsFinished()
     327             : {
     328   204605218 :     bool audioFinished = hasAudio() ? audioStreamEnded_ : true;
     329   204605218 :     bool videoFinished = hasVideo() ? videoStreamEnded_ : true;
     330   204605218 :     return audioFinished && videoFinished;
     331             : }
     332             : 
     333             : } // namespace jami

Generated by: LCOV version 1.14