Line data Source code
1 : /* 2 : * Copyright (C) 2004-2024 Savoir-faire Linux Inc. 3 : * 4 : * Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com> 5 : * Author: Guillaume Roguez <Guillaume.Roguez@savoirfairelinux.com> 6 : * Author: Vivien Didelot <vivien.didelot@savoirfairelinux.com> 7 : * 8 : * This program is free software; you can redistribute it and/or modify 9 : * it under the terms of the GNU General Public License as published by 10 : * the Free Software Foundation; either version 3 of the License, or 11 : * (at your option) any later version. 12 : * 13 : * This program is distributed in the hope that it will be useful, 14 : * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 : * GNU General Public License for more details. 17 : * 18 : * You should have received a copy of the GNU General Public License 19 : * along with this program; if not, write to the Free Software 20 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 : */ 22 : 23 : #pragma once 24 : 25 : #include "noncopyable.h" 26 : #include "threadloop.h" 27 : #include "media/media_stream.h" 28 : #include "media/media_device.h" // DeviceParams 29 : #include "media/video/video_base.h" 30 : #include "media/media_codec.h" 31 : 32 : #include <map> 33 : #include <atomic> 34 : #include <future> 35 : #include <string> 36 : #include <mutex> 37 : #include <condition_variable> 38 : #include <array> 39 : 40 : #if __APPLE__ 41 : #import "TargetConditionals.h" 42 : #endif 43 : 44 : namespace jami { 45 : class MediaDecoder; 46 : class MediaDemuxer; 47 : } // namespace jami 48 : 49 : namespace jami { 50 : namespace video { 51 : 52 : class SinkClient; 53 : 54 : enum class VideoInputMode { ManagedByClient, ManagedByDaemon, Undefined }; 55 : 56 : class VideoInput : public VideoGenerator 57 : { 58 : public: 59 : VideoInput(VideoInputMode inputMode = VideoInputMode::Undefined, 60 : const std::string& resource = "local", 61 : const std::string& sink = ""); 62 : ~VideoInput(); 63 : 64 : // as VideoGenerator 65 : const std::string& getName() const { return resource_; } 66 : int getWidth() const; 67 : int getHeight() const; 68 : AVPixelFormat getPixelFormat() const; 69 : 70 : const DeviceParams& getConfig() const { 71 : return decOpts_; 72 : } 73 116 : std::shared_future<DeviceParams> getParams() const { 74 116 : return futureDecOpts_; 75 : } 76 : 77 : MediaStream getInfo() const; 78 : 79 : void setSink(const std::string& sinkId); 80 : void updateStartTime(int64_t startTime); 81 : void configureFilePlayback(const std::string& path, 82 : std::shared_ptr<MediaDemuxer>& demuxer, 83 : int index); 84 : void flushBuffers(); 85 20 : void setPaused(bool paused) { paused_ = paused; } 86 : void setSeekTime(int64_t time); 87 : void setupSink(const int width, const int height); 88 : void stopSink(); 89 : 90 : void setRecorderCallback(const std::function<void(const MediaStream& ms)>& cb); 91 : 92 : #if VIDEO_CLIENT_INPUT 93 : /* 94 : * these functions are used to pass buffer from/to the daemon 95 : * on the Android and UWP builds 96 : */ 97 : void* obtainFrame(int length); 98 : void releaseFrame(void* frame); 99 : #else 100 : void stopInput(); 101 : #endif 102 : 103 : void setSuccessfulSetupCb(const std::function<void(MediaType, bool)>& cb) 104 : { 105 : onSuccessfulSetup_ = cb; 106 : } 107 : 108 : /** 109 : * Restart stopped video input 110 : * @note if a media is removed, then re-added in a conference, the loop will be stopped 111 : * and this input must be restarted 112 : */ 113 : void restart(); 114 : 115 : private: 116 : NON_COPYABLE(VideoInput); 117 : 118 : std::shared_future<DeviceParams> switchInput(const std::string& resource); 119 : 120 : std::string resource_; 121 : std::atomic<bool> switchPending_ = {false}; 122 : std::atomic_bool isStopped_ = {false}; 123 : 124 : DeviceParams decOpts_; 125 : std::promise<DeviceParams> foundDecOpts_; 126 : std::shared_future<DeviceParams> futureDecOpts_; 127 : bool emulateRate_ = false; 128 : 129 : std::atomic_bool decOptsFound_ {false}; 130 : void foundDecOpts(const DeviceParams& params); 131 : 132 : void clearOptions(); 133 : 134 : // true if decOpts_ is ready to use, false if using promise/future 135 : bool initCamera(const std::string& device); 136 : bool initFile(std::string path); 137 : 138 : #ifdef __APPLE__ 139 : bool initAVFoundation(const std::string& display); 140 : #elif defined(WIN32) 141 : bool initWindowsGrab(const std::string& display); 142 : #else 143 : bool initLinuxGrab(const std::string& display); 144 : #endif 145 : 146 : bool isCapturing() const noexcept; 147 : void startLoop(); 148 : 149 : void switchDevice(); 150 : bool capturing_ {false}; 151 : void createDecoder(); 152 : void deleteDecoder(); 153 : std::unique_ptr<MediaDecoder> decoder_; 154 : std::shared_ptr<SinkClient> sink_; 155 : ThreadLoop loop_; 156 : 157 : // for ThreadLoop 158 : bool setup(); 159 : void process(); 160 : void cleanup(); 161 : 162 : bool captureFrame(); 163 : 164 : int rotation_ {0}; 165 : std::shared_ptr<AVBufferRef> displayMatrix_; 166 : void setRotation(int angle); 167 : VideoInputMode inputMode_; 168 366 : inline bool videoManagedByClient() const 169 : { 170 366 : return inputMode_ == VideoInputMode::ManagedByClient; 171 : } 172 : bool playingFile_ = false; 173 : std::atomic_bool paused_ {true}; 174 : 175 : std::function<void(MediaType, bool)> onSuccessfulSetup_; 176 : std::function<void(const MediaStream& ms)> recorderCallback_; 177 : }; 178 : 179 : } // namespace video 180 : } // namespace jami