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-04-26 09:41:19 Functions: 16 27 59.3 %

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

Generated by: LCOV version 1.14